diff --git a/gen_7_scripts/build.rs b/gen_7_scripts/build.rs index a2a7a61..4cf7ed0 100644 --- a/gen_7_scripts/build.rs +++ b/gen_7_scripts/build.rs @@ -10,6 +10,7 @@ fn main() { use alloc::boxed::Box; use pkmn_lib_interface::app_interface::{{get_hash, StringKey}}; use pkmn_lib_interface::handling::{{Script, ScriptCategory}}; +use pkmn_lib_interface::handling::item_script::ItemScript; macro_rules! resolve_match {{ ( @@ -56,6 +57,8 @@ pub fn get_script(category: ScriptCategory, name: &StringKey) -> Option Option> {{ + resolve_match! {{ + name.hash().unwrap()," + ) + .unwrap(); + + let item_files = std::fs::read_dir("src/items") + .unwrap() + .map(|f| f.unwrap().path()) + .filter(|f| f.extension().unwrap() == "rs") + .map(|f| f.file_stem().unwrap().to_str().unwrap().to_string()) + .collect::>(); + for file in item_files { + println!("cargo:rerun-if-changed=src/items/{}.rs", file); + let parsed = + syn::parse_file(&std::fs::read_to_string(format!("src/items/{}.rs", file)).unwrap()) + .unwrap(); + // Now we need to find every impl ItemScript for X { ... } block inside parsed + let script_impls = parsed + .items + .iter() + .filter_map(|item| match item { + syn::Item::Impl(impl_block) => match impl_block.trait_ { + Some((_, ref path, _)) => { + if path.segments[0].ident == "ItemScript" { + Some(impl_block) + } else { + None + } + } + None => None, + }, + _ => None, + }) + .collect::>(); + for script_impl in script_impls { + if let syn::Type::Path(p) = script_impl.self_ty.as_ref() { + let ident = p.path.segments[0].ident.to_string(); + writeln!(output_file, " crate::items::{}::{},", file, ident).unwrap(); + } + } + } + + writeln!( + output_file, + r" }} + None +}}" + ) + .unwrap(); +} diff --git a/gen_7_scripts/src/items/healing_item.rs b/gen_7_scripts/src/items/healing_item.rs new file mode 100644 index 0000000..8e46cf1 --- /dev/null +++ b/gen_7_scripts/src/items/healing_item.rs @@ -0,0 +1,40 @@ +use crate::common_usings::*; +use pkmn_lib_interface::handling::item_script::ItemScript; + +script!( + HealingItem, + "healing_item", + amount: AtomicU32 +); + +impl ItemScript for HealingItem { + fn new() -> Self + where + Self: Sized, + { + Self { + amount: Default::default(), + } + } + + fn get_name(&self) -> &'static str { + Self::get_const_name() + } + + fn is_item_usable(&self) -> PkmnResult { + Ok(true) + } + + fn requires_target(&self) -> PkmnResult { + Ok(true) + } + + fn is_target_valid(&self, target: &Pokemon) -> PkmnResult { + Ok(!target.is_fainted()?) + } + + fn on_use_with_target(&self, target: &Pokemon) -> PkmnResult<()> { + target.heal(self.amount.load(Ordering::Relaxed), false)?; + Ok(()) + } +} diff --git a/gen_7_scripts/src/items/mod.rs b/gen_7_scripts/src/items/mod.rs new file mode 100644 index 0000000..bafcb57 --- /dev/null +++ b/gen_7_scripts/src/items/mod.rs @@ -0,0 +1 @@ +pub mod healing_item; diff --git a/gen_7_scripts/src/lib.rs b/gen_7_scripts/src/lib.rs index 942b62a..ae813eb 100755 --- a/gen_7_scripts/src/lib.rs +++ b/gen_7_scripts/src/lib.rs @@ -27,6 +27,7 @@ pub mod status; pub mod util_scripts; pub(crate) mod utils; pub mod weather; +pub mod items; #[no_mangle] #[cfg(not(test))] diff --git a/gen_7_scripts/src/registered_scripts.rs b/gen_7_scripts/src/registered_scripts.rs index 023970f..77cd7bb 100755 --- a/gen_7_scripts/src/registered_scripts.rs +++ b/gen_7_scripts/src/registered_scripts.rs @@ -3,6 +3,7 @@ use alloc::boxed::Box; use pkmn_lib_interface::app_interface::{get_hash, StringKey}; use pkmn_lib_interface::handling::{Script, ScriptCategory}; +use pkmn_lib_interface::handling::item_script::ItemScript; macro_rules! resolve_match { ( @@ -94,4 +95,11 @@ pub fn get_script(category: ScriptCategory, name: &StringKey) -> Option Option> { + resolve_match! { + name.hash().unwrap(), + crate::items::healing_item::HealingItem, + } + None +} diff --git a/pkmn_lib_interface/src/app_interface/static_data/time_of_day.rs b/pkmn_lib_interface/src/app_interface/static_data/time_of_day.rs index 0903b7a..e0b83a2 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/time_of_day.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/time_of_day.rs @@ -1,20 +1,15 @@ /// The time of day. These values are the 4 different groups of time of day in Pokemon games since /// gen 5. The exact times these correspond to differ between games. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] #[repr(u8)] pub enum TimeOfDay { /// The morning. Morning = 0, /// The day. + #[default] Day = 1, /// The evening. Evening = 2, /// The night. Night = 3, } - -impl Default for TimeOfDay { - fn default() -> Self { - TimeOfDay::Day - } -} diff --git a/pkmn_lib_interface/src/handling/item_script.rs b/pkmn_lib_interface/src/handling/item_script.rs new file mode 100644 index 0000000..d443a91 --- /dev/null +++ b/pkmn_lib_interface/src/handling/item_script.rs @@ -0,0 +1,55 @@ +use crate::app_interface::{EffectParameter, Pokemon}; +use alloc::rc::Rc; +use alloc::vec::Vec; + +type Result = crate::result::PkmnResult; + +pub trait ItemScript { + fn new() -> Self + where + Self: Sized; + fn get_name(&self) -> &'static str; + + /// Initializes the script with the given parameters for a specific item + fn on_initialize(&self, _pars: Option>>) -> Result<()> { + Ok(()) + } + + /// Returns whether the item is usable in the current context. + /// Note that the host caches this value, so it will not be called again + fn is_item_usable(&self) -> Result { + Ok(false) + } + + /// Returns whether the item requires a target to be used. + /// Note that the host caches this value, so it will not be called again + fn requires_target(&self) -> Result { + Ok(false) + } + + /// Returns whether the item can be held. + /// Note that the host caches this value, so it will not be called again + fn is_holdable(&self) -> Result { + Ok(false) + } + + /// Returns whether the item is usable on the given target. + fn is_target_valid(&self, _target: &Pokemon) -> Result { + Ok(false) + } + + /// Returns whether the item can be held by the given target. + fn can_target_hold(&self, _target: &Pokemon) -> Result { + Ok(false) + } + + /// Handles the use of the item. + fn on_use(&self) -> Result<()> { + Ok(()) + } + + /// Handles the use of the item on the given target. + fn on_use_with_target(&self, _target: &Pokemon) -> Result<()> { + Ok(()) + } +} diff --git a/pkmn_lib_interface/src/handling/mod.rs b/pkmn_lib_interface/src/handling/mod.rs index 099588a..5d3124c 100755 --- a/pkmn_lib_interface/src/handling/mod.rs +++ b/pkmn_lib_interface/src/handling/mod.rs @@ -5,6 +5,7 @@ pub(crate) mod cached_value; pub mod capabilities; pub mod extern_ref; pub mod ffi_array; +pub mod item_script; pub mod script; #[cfg(not(feature = "mock_data"))] pub(crate) mod temporary; diff --git a/pkmn_lib_interface/src/handling/script.rs b/pkmn_lib_interface/src/handling/script.rs index 41cb009..9d9053b 100755 --- a/pkmn_lib_interface/src/handling/script.rs +++ b/pkmn_lib_interface/src/handling/script.rs @@ -496,7 +496,7 @@ pub enum ScriptOwner { impl ScriptOwner { #[cfg(not(feature = "mock_data"))] - fn from_script(script: &dyn Script) -> Option { + fn from_script<'a>(script: &'a dyn Script) -> Option { let script_ptr = crate::implementation::ScriptPtr::from_existing(script); unsafe { let kind = script_get_owner_kind(script_ptr); diff --git a/pkmn_lib_interface/src/lib.rs b/pkmn_lib_interface/src/lib.rs index 97d77a7..f4d45d9 100755 --- a/pkmn_lib_interface/src/lib.rs +++ b/pkmn_lib_interface/src/lib.rs @@ -42,17 +42,19 @@ pub mod utils; pub use result::*; pub type LoadScriptFnType = Box Option>>; +pub type LoadItemScriptFnType = Box Option>>; #[cfg(not(feature = "mock_data"))] mod implementation { - use super::LoadScriptFnType; + use super::{LoadItemScriptFnType, LoadScriptFnType}; use crate::app_interface::{ BattleImpl, DamageSource, DynamicLibraryImpl, EffectParameter, ExecutingMoveImpl, ItemImpl, - PokemonImpl, Statistic, StringKey, TurnChoice, TypeIdentifier, + Pokemon, PokemonImpl, Statistic, StringKey, TurnChoice, TypeIdentifier, }; use crate::handling::extern_ref::ExternRef; use crate::handling::ffi_array::FFIArray; + use crate::handling::item_script::ItemScript; use crate::handling::wasm_result::WasmVoidResult; use crate::handling::{Script, ScriptCapabilities, ScriptCategory}; use alloc::boxed::Box; @@ -63,6 +65,7 @@ mod implementation { use hashbrown::HashMap; static mut LOAD_SCRIPT_FN: Option = None; + static mut LOAD_ITEM_SCRIPT_FN: Option = None; pub fn set_load_script_fn(f: LoadScriptFnType) { unsafe { @@ -70,6 +73,12 @@ mod implementation { } } + pub fn set_load_item_script_fn(f: LoadItemScriptFnType) { + unsafe { + LOAD_ITEM_SCRIPT_FN = Some(f); + } + } + macro_rules! exported_functions { ( $( @@ -91,74 +100,104 @@ mod implementation { static mut SCRIPT_PTR_REVERSE_CACHE: Option>> = None; static mut SCRIPT_INDEX_COUNTER: AtomicU32 = AtomicU32::new(1); - #[repr(C)] + static mut ITEM_SCRIPT_PTR_CACHE: Option> = None; + static mut ITEM_SCRIPT_PTR_REVERSE_CACHE: Option>> = None; + static mut ITEM_SCRIPT_INDEX_COUNTER: AtomicU32 = AtomicU32::new(1); + + macro_rules! script_ptr_impl { + ($name:ident, $script:ident, $cache:ident, $reverse_cache:ident, $counter:ident) => { + impl $name { + fn get_cache<'a>() -> &'a mut HashMap<*const dyn $script, u32> { + unsafe { + if let None = $cache { + $cache = Some(HashMap::new()); + } + let cache = $cache.as_mut().unwrap(); + cache + } + } + + fn get_reverse_cache<'a>() -> &'a mut HashMap> { + unsafe { + if let None = $reverse_cache { + $reverse_cache = Some(HashMap::new()); + } + let cache = $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($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> { + 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 + } + } + } + }; + } + + #[repr(transparent)] #[derive(Copy, Clone, Default)] 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(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(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> { - 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 - } - } + #[repr(transparent)] + #[derive(Copy, Clone, Default)] + pub struct ItemScriptPtr { + index: u32, } + script_ptr_impl!( + ScriptPtr, + Script, + SCRIPT_PTR_CACHE, + SCRIPT_PTR_REVERSE_CACHE, + SCRIPT_INDEX_COUNTER + ); + + script_ptr_impl!( + ItemScriptPtr, + ItemScript, + ITEM_SCRIPT_PTR_CACHE, + ITEM_SCRIPT_PTR_REVERSE_CACHE, + ITEM_SCRIPT_INDEX_COUNTER + ); + exported_functions! { fn load_script(category: ScriptCategory, name: ExternRef) -> ScriptPtr { @@ -171,6 +210,16 @@ mod implementation { ScriptPtr::new(b) } + fn load_item_script(name: ExternRef) -> ItemScriptPtr { + let name_c = StringKey::new(name); + let boxed_script = unsafe { &LOAD_ITEM_SCRIPT_FN }.as_ref().unwrap()(&name_c); + if boxed_script.is_none() { + return ItemScriptPtr::null(); + } + let b = boxed_script.unwrap(); + ItemScriptPtr::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. @@ -178,6 +227,13 @@ mod implementation { drop(boxed_script); } + fn destroy_item_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() @@ -737,9 +793,109 @@ mod implementation { *out = out_saturating.0; return res; } + + // Item script functions + + fn item_script_item_on_initialize(script: ItemScriptPtr, parameters: u64) -> WasmVoidResult { + let parameters : FFIArray> = FFIArray::from_u64(parameters); + let parameters = Vec::from_raw_parts(parameters.ptr(), parameters.len(), parameters.len()); + let parameters = parameters.into_iter().map(|p| Rc::new(EffectParameter::new(p).unwrap())).collect::>(); + WasmVoidResult::from_res(script.val().unwrap().on_initialize(Some(parameters))) + } + + fn item_script_item_is_usable(script: ItemScriptPtr, out: *mut bool) -> WasmVoidResult { + let res = script.val().unwrap().is_item_usable(); + match res { + Ok(r) => { + unsafe { + *out = r; + } + WasmVoidResult::ok(()) + } + Err(e) => WasmVoidResult::err(e), + } + } + + fn item_script_item_requires_target(script: ItemScriptPtr, out: *mut bool) -> WasmVoidResult { + let res = script.val().unwrap().requires_target(); + match res { + Ok(r) => { + unsafe { + *out = r; + } + WasmVoidResult::ok(()) + } + Err(e) => WasmVoidResult::err(e), + } + } + + fn item_script_item_is_holdable(script: ItemScriptPtr, out: *mut bool) -> WasmVoidResult { + let res = script.val().unwrap().is_holdable(); + match res { + Ok(r) => { + unsafe { + *out = r; + } + WasmVoidResult::ok(()) + } + Err(e) => WasmVoidResult::err(e), + } + } + + fn item_script_item_is_target_valid( + script: ItemScriptPtr, + target: ExternRef, + out: *mut bool + ) -> WasmVoidResult { + let target : Pokemon = target.not_null_rc(); + let res = script.val().unwrap().is_target_valid(&target); + match res { + Ok(r) => { + unsafe { + *out = r; + } + WasmVoidResult::ok(()) + } + Err(e) => WasmVoidResult::err(e), + } + } + + fn item_script_item_can_target_hold( + script: ItemScriptPtr, + target: ExternRef, + out: *mut bool + ) -> WasmVoidResult { + let target : Pokemon = target.not_null_rc(); + let res = script.val().unwrap().can_target_hold(&target); + match res { + Ok(r) => { + unsafe { + *out = r; + } + WasmVoidResult::ok(()) + } + Err(e) => WasmVoidResult::err(e), + } + } + + fn item_script_item_on_use( + script: ItemScriptPtr, + ) -> WasmVoidResult { + WasmVoidResult::from_res(script.val().unwrap().on_use()) + } + + fn item_script_item_on_use_with_target( + script: ItemScriptPtr, + target: ExternRef, + ) -> WasmVoidResult { + let target : Pokemon = target.not_null_rc(); + WasmVoidResult::from_res(script.val().unwrap().on_use_with_target(&target)) + } + } } +use crate::handling::item_script::ItemScript; use crate::handling::{Script, ScriptCategory}; #[cfg(not(feature = "mock_data"))] pub use implementation::*;