diff --git a/src/dynamic_data/libraries/script_resolver.rs b/src/dynamic_data/libraries/script_resolver.rs index d403677..b5c452b 100755 --- a/src/dynamic_data/libraries/script_resolver.rs +++ b/src/dynamic_data/libraries/script_resolver.rs @@ -47,8 +47,10 @@ pub enum ScriptCategory { Side = 4, /// A script that can be attached to the entire battle. Battle = 5, + /// A special script for weather, for use on battles. + Weather = 6, /// A special script for held items. As they're part of a held item, they're attached to a Pokemon. - ItemBattleTrigger = 6, + ItemBattleTrigger = 7, } /// A basic empty script resolver, that always returns None. diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index f4112ea..f9fec84 100755 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -11,12 +11,12 @@ use crate::dynamic_data::models::battle_party::BattleParty; use crate::dynamic_data::models::battle_random::BattleRandom; use crate::dynamic_data::models::battle_side::BattleSide; use crate::dynamic_data::models::pokemon::Pokemon; -use crate::dynamic_data::ChoiceQueue; use crate::dynamic_data::DynamicLibrary; use crate::dynamic_data::Script; use crate::dynamic_data::ScriptSet; use crate::dynamic_data::VolatileScriptsOwner; use crate::dynamic_data::{is_valid_target, ScriptWrapper}; +use crate::dynamic_data::{ChoiceQueue, ScriptContainer}; use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData}; use crate::{script_hook, PkmnResult, StringKey}; @@ -48,6 +48,8 @@ pub struct Battle { event_hook: EventHook, /// The index of the current turn. 0 until all choices current_turn: AtomicU32, + /// The current weather of the battle. + weather: ScriptContainer, /// All the volatile scripts attached to a Pokemon volatile_scripts: Arc, /// The time the last turn took to run. Defaults to 0. @@ -91,6 +93,7 @@ impl Battle { result: RwLock::new(BattleResult::Inconclusive), event_hook: Default::default(), current_turn: AtomicU32::new(0), + weather: Default::default(), volatile_scripts: Default::default(), last_turn_time: Default::default(), script_source_data: Default::default(), @@ -331,6 +334,30 @@ impl Battle { .store(time.num_nanoseconds().unwrap() as u64, Ordering::SeqCst); Ok(()) } + + /// Sets the current weather for the battle. If None is passed, this clears the weather. + pub fn set_weather(&self, weather: Option) { + if let Some(weather) = weather { + let script = self + .library() + .load_script(self.into(), ScriptCategory::Weather, &weather) + .unwrap() + .expect(format!("Couldn't find weather script by name {}", weather).as_str()); + self.weather.set(script); + } else { + self.weather.clear(); + } + } + + /// Gets the current weather of the battle. If no weather is present, this returns None. + pub fn weather_name(&self) -> Option { + if let Some(script) = self.weather.get() { + let lock = script.read(); + Some(lock.as_ref().unwrap().name().clone()) + } else { + None + } + } } impl VolatileScriptsOwner for Battle { @@ -353,6 +380,7 @@ impl ScriptSource for Battle { } fn get_own_scripts(&self, scripts: &mut Vec) { + scripts.push((&self.weather).into()); scripts.push((&self.volatile_scripts).into()); } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index ac31fa0..4b3285d 100755 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -282,6 +282,11 @@ impl Pokemon { pub fn weight(&self) -> f32 { self.weight.load(Ordering::Relaxed) } + /// Sets the weight of the Pokemon in kilograms. + pub fn set_weight(&self, weight: f32) { + self.weight.store(weight, Ordering::Relaxed) + } + /// The height of the Pokemon in meters. pub fn height(&self) -> f32 { self.height.load(Ordering::Relaxed) @@ -685,6 +690,11 @@ impl Pokemon { let move_data = self.library.static_data().moves().get(move_name).unwrap(); learned_moves[move_pos.unwrap()] = Some(Arc::new(LearnedMove::new(move_data, learn_method))); } + + /// Removes the current non-volatile status from the Pokemon. + pub fn clear_status(&self) { + self.status_script.clear() + } } /// The data of the Pokemon related to being in a battle. @@ -780,6 +790,8 @@ pub enum DamageSource { MoveDamage = 0, /// The damage is done by something else. Misc = 1, + /// The damage is done because of struggling. + Struggle = 2, } #[cfg(test)] diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs old mode 100644 new mode 100755 index 5c05240..52d8512 --- a/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle.rs @@ -4,9 +4,17 @@ use crate::dynamic_data::{ use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use crate::StringKey; use wasmer::FunctionEnvMut; register! { + fn battle_get_pokemon_per_side( + env: FunctionEnvMut, + battle: ExternRef, + ) -> u8 { + battle.value_func(&env).unwrap().pokemon_per_side() + } + fn battle_get_parties( env: FunctionEnvMut, battle: ExternRef, @@ -47,6 +55,18 @@ register! { ExternRef::func_new(&env, battle.value_func(&env).unwrap().random()) } + fn battle_get_weather_name( + env: FunctionEnvMut, + battle: ExternRef, + ) -> ExternRef { + let weather = battle.value_func(&env).unwrap().weather_name(); + if let Some(weather) = weather { + ExternRef::func_new(&env, &weather) + } else { + ExternRef::null() + } + } + fn battle_find_party_for_pokemon( env: FunctionEnvMut, battle: ExternRef, diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle_random.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs b/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs index af5be68..cb2b98a 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/battle_side.rs @@ -3,9 +3,9 @@ use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script::WebAssemblyScript; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::{ScriptCategory, StringKey}; -use std::ffi::CString; -use wasmer::{FunctionEnvMut, Value}; +use crate::ScriptCategory; +use std::ffi::CStr; +use wasmer::FunctionEnvMut; register! { fn battleside_has_fled_battle( @@ -61,8 +61,8 @@ register! { name_ptr: u32 ) -> u32 { unsafe { - let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); - let script = side.value_func(&env).unwrap().add_volatile_script(&c_name.into()).unwrap(); + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + let script = side.value_func(&env).unwrap().add_volatile_script(&c_name.as_ref().into()).unwrap(); if let Some(script) = script { let script = script.get_as::(); script.get_wasm_pointer() @@ -78,19 +78,33 @@ register! { script_ptr: u32 ) -> u32 { let side = side.value_func(&env).unwrap(); - let name_ptr = env.data().data().exported_functions().get(&StringKey::new("script_get_name")).unwrap().call(&mut env.data().data().store_mut(), &[Value::I32(script_ptr as i32)]).unwrap().get(0).unwrap().i32().unwrap() as u32; unsafe{ - let name_ptr: CString = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); - let script = env.data().data().setup_script(script_ptr, ScriptCategory::Side, &name_ptr.into(), side.into()).unwrap(); + let env = env.data().data(); + let name_ptr = env.script_function_cache().script_get_name(&env).unwrap().call(&mut env.store_mut(), script_ptr).unwrap(); + let c_name: &CStr = CStr::from_ptr(env.get_raw_pointer(name_ptr)); + let script = env.setup_script(script_ptr, ScriptCategory::Side, &c_name.as_ref().into(), side.into()).unwrap(); + env.script_function_cache().dealloc_cstring(&env).unwrap().call(&mut env.store_mut(), name_ptr).unwrap(); + if let Some(script) = script { let script = side.add_volatile_script_with_script(script); - script.unwrap().unwrap().get_as::().get_wasm_pointer() + let s = script.as_ref().unwrap().as_ref().unwrap().get_as::(); + s.get_wasm_pointer() } else { 0 } } } + fn battleside_has_volatile( + env: FunctionEnvMut, + side: ExternRef, + name_ptr: u32 + ) -> u8 { + unsafe { + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + if side.value_func(&env).unwrap().has_volatile_script(&c_name.as_ref().into()) { 1 } else { 0 } + } + } fn battleside_get_volatile( env: FunctionEnvMut, @@ -98,8 +112,8 @@ register! { name_ptr: u32 ) -> u32 { unsafe { - let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); - let script = side.value_func(&env).unwrap().get_volatile_script(&c_name.into()); + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + let script = side.value_func(&env).unwrap().get_volatile_script(&c_name.as_ref().into()); if let Some(script) = script { let script = script.get_as::(); script.get_wasm_pointer() @@ -115,8 +129,8 @@ register! { name_ptr: u32 ) { unsafe { - let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr)); - side.value_func(&env).unwrap().remove_volatile_script(&c_name.into()); + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + side.value_func(&env).unwrap().remove_volatile_script(&c_name.as_ref().into()); } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs b/src/script_implementations/wasm/export_registry/dynamic_data/choice_queue.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs b/src/script_implementations/wasm/export_registry/dynamic_data/executing_move.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs b/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs old mode 100644 new mode 100755 index 44c36e7..b2e0026 --- a/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/hit_data.rs @@ -5,6 +5,20 @@ use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use wasmer::FunctionEnvMut; register! { + fn hit_data_get_damage( + env: FunctionEnvMut, + hit: ExternRef, + ) -> u32 { + hit.value_func(&env).unwrap().damage() + } + + fn hit_data_is_critical( + env: FunctionEnvMut, + hit: ExternRef, + ) -> u8 { + if hit.value_func(&env).unwrap().is_critical() { 1 } else { 0 } + } + fn hit_data_fail( env: FunctionEnvMut, hit: ExternRef, diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/party.rs b/src/script_implementations/wasm/export_registry/dynamic_data/party.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs index d6824a3..be9aabd 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs @@ -1,11 +1,14 @@ use std::mem::transmute; -use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon}; +use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon, VolatileScriptsOwner}; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; +use crate::script_implementations::wasm::script::WebAssemblyScript; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{ClampedStatisticSet, Species}; use crate::static_data::{Item, StatisticSet}; +use crate::{ScriptCategory, StringKey}; +use std::ffi::{c_char, CStr}; use wasmer::FunctionEnvMut; register! { @@ -65,6 +68,38 @@ register! { ExternRef::func_new(&env, species) } + fn pokemon_get_weight( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> f32 { + pokemon.value_func(&env).unwrap().weight() + } + + fn pokemon_set_weight( + env: FunctionEnvMut, + pokemon: ExternRef, + weight: f32, + ) { + pokemon.value_func(&env).unwrap().set_weight(weight) + } + + + fn pokemon_get_height( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> f32 { + pokemon.value_func(&env).unwrap().height() + } + + fn pokemon_get_gender( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> u8 { + unsafe { + transmute(pokemon.value_func(&env).unwrap().gender()) + } + } + fn pokemon_damage( env: FunctionEnvMut, pokemon: ExternRef, @@ -118,14 +153,25 @@ register! { } } - fn pokemon_get_battle_side_index( + fn pokemon_get_battle_index( env: FunctionEnvMut, pokemon: ExternRef, ) -> u8 { if let Some(i) = pokemon.value_func(&env).unwrap().get_battle_index() { i } else { - 0 + 255 + } + } + + fn pokemon_get_battle_side_index( + env: FunctionEnvMut, + pokemon: ExternRef, + ) -> u8 { + if let Some(i) = pokemon.value_func(&env).unwrap().get_battle_side_index() { + i + } else { + 255 } } @@ -141,4 +187,110 @@ register! { } } + fn pokemon_has_held_item( + env: FunctionEnvMut, + pokemon: ExternRef, + name: u32 + ) -> u8 { + let name : *mut c_char = env.data().data().get_raw_pointer(name); + let name = unsafe { CStr::from_ptr(name) }; + let key = StringKey::new(&name.to_str().unwrap().clone()); + if pokemon.value_func(&env).unwrap().has_held_item(&key) { 1 } else { 0 } + } + + fn pokemon_heal( + env: FunctionEnvMut, + pokemon: ExternRef, + amount: u32, + allow_revive: u8 + ) -> u8 { + if pokemon.value_func(&env).unwrap().heal(amount, allow_revive == 1) { 1 } else { 0 } + } + + fn pokemon_clear_status( + env: FunctionEnvMut, + pokemon: ExternRef, + ) { + pokemon.value_func(&env).unwrap().clear_status() + } + + fn pokemon_add_volatile_by_name( + env: FunctionEnvMut, + pokemon: ExternRef, + name_ptr: u32 + ) -> u32 { + unsafe { + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + let script = pokemon.value_func(&env).unwrap().add_volatile_script(&c_name.as_ref().into()).unwrap(); + if let Some(script) = script { + let script = script.get_as::(); + script.get_wasm_pointer() + } else { + 0 + } + } + } + + fn pokemon_add_volatile( + env: FunctionEnvMut, + pokemon: ExternRef, + script_ptr: u32 + ) -> u32 { + let pokemon = pokemon.value_func(&env).unwrap(); + unsafe{ + let env = env.data().data(); + let name_ptr = env.script_function_cache().script_get_name(&env).unwrap().call(&mut env.store_mut(), script_ptr).unwrap(); + let c_name: &CStr = CStr::from_ptr(env.get_raw_pointer(name_ptr)); + let script = env.setup_script(script_ptr, ScriptCategory::Pokemon, &c_name.as_ref().into(), pokemon.into()).unwrap(); + env.script_function_cache().dealloc_cstring(&env).unwrap().call(&mut env.store_mut(), name_ptr).unwrap(); + + if let Some(script) = script { + let script = pokemon.add_volatile_script_with_script(script); + let s = script.as_ref().unwrap().as_ref().unwrap().get_as::(); + s.get_wasm_pointer() + } else { + 0 + } + } + } + + fn pokemon_has_volatile( + env: FunctionEnvMut, + pokemon: ExternRef, + name_ptr: u32 + ) -> u8 { + unsafe { + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + if pokemon.value_func(&env).unwrap().has_volatile_script(&c_name.as_ref().into()) { 1 } else { 0 } + } + } + + fn pokemon_get_volatile( + env: FunctionEnvMut, + pokemon: ExternRef, + name_ptr: u32 + ) -> u32 { + unsafe { + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + let script = pokemon.value_func(&env).unwrap().get_volatile_script(&c_name.as_ref().into()); + if let Some(script) = script { + let script = script.get_as::(); + script.get_wasm_pointer() + } else { + 0 + } + } + } + + fn pokemon_remove_volatile( + env: FunctionEnvMut, + pokemon: ExternRef, + name_ptr: u32 + ) { + unsafe { + let c_name = CStr::from_ptr(env.data().data().get_raw_pointer(name_ptr)); + pokemon.value_func(&env).unwrap().remove_volatile_script(&c_name.as_ref().into()); + } + } + } diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs index 490a0d8..eae7ac9 100755 --- a/src/script_implementations/wasm/export_registry/mod.rs +++ b/src/script_implementations/wasm/export_registry/mod.rs @@ -1,4 +1,4 @@ -use std::ffi::CString; +use std::ffi::{c_char, CStr, CString}; use std::mem::{align_of, forget}; use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; @@ -80,34 +80,29 @@ pub(crate) fn register_webassembly_funcs( } /// Logging function for WASM. -fn _print(env: FunctionEnvMut, p: u32, len: u32) { +fn _print(env: FunctionEnvMut, p: u32) { unsafe { - let mem: *mut u8 = env.data().data().memory().offset(p as isize); - let s = String::from_raw_parts(mem, len as usize, len as usize); - println!("{}", s); - forget(s); + let mem: *mut c_char = env.data().data().get_raw_pointer(p); + let s = CStr::from_ptr(mem); + println!("{}", s.to_str().unwrap()); } } /// Triggers when WASM panics. #[track_caller] -fn _error( - env: FunctionEnvMut, - message: u32, - message_len: u32, - file: u32, - file_len: u32, - line: u32, - position: u32, -) { +fn _error(env: FunctionEnvMut, message: u32, file: u32, line: u32, position: u32) { unsafe { - let mem: *mut u8 = env.data().data().memory().offset(message as isize); - let message_str = String::from_raw_parts(mem, message_len as usize, message_len as usize); - let mem: *mut u8 = env.data().data().memory().offset(file as isize); - let file_str = String::from_raw_parts(mem, file_len as usize, file_len as usize); + let mem: *const c_char = env.data().data().get_raw_pointer(message); + let message_str = CStr::from_ptr(mem); + let mem: *const c_char = env.data().data().get_raw_pointer(file); + let file_str = CStr::from_ptr(mem); + panic!( - "Error: {} in file {}, line: {}:{}", - message_str, file_str, line, position + "WASM Error: {} in file {}, line: {}:{}", + message_str.to_str().unwrap(), + file_str.to_str().unwrap(), + line, + position ); } } diff --git a/src/script_implementations/wasm/export_registry/static_data/item.rs b/src/script_implementations/wasm/export_registry/static_data/item.rs old mode 100644 new mode 100755 diff --git a/src/script_implementations/wasm/export_registry/static_data/mod.rs b/src/script_implementations/wasm/export_registry/static_data/mod.rs index 513832f..7a2fe94 100755 --- a/src/script_implementations/wasm/export_registry/static_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/static_data/mod.rs @@ -1,10 +1,13 @@ +use std::mem::transmute; use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::defines::LevelInt; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary}; +use crate::static_data::{ + ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, StatisticSet, TypeLibrary, +}; mod item; /// Moves data registration @@ -43,6 +46,12 @@ register! { data_library.value_func(&env).unwrap().maximum_level() } + fn statistic_set_get(env: FunctionEnvMut, statistics_set: ExternRef>, stat: u8) -> i64 { + unsafe { + statistics_set.value_func(&env).unwrap().get_stat(transmute(stat)) as i64 + } + } + manual manual_registration } diff --git a/src/script_implementations/wasm/script_function_cache.rs b/src/script_implementations/wasm/script_function_cache.rs index 074f592..892c827 100755 --- a/src/script_implementations/wasm/script_function_cache.rs +++ b/src/script_implementations/wasm/script_function_cache.rs @@ -21,6 +21,8 @@ macro_rules! script_function_cache { #[derive(Default)] #[allow(unused_parens)] pub(super) struct ScriptFunctionCache { + script_get_name: RwLock>>, + dealloc_cstring: RwLock>>, $( $name: RwLock>>, )* @@ -168,5 +170,42 @@ script_function_cache! { share_experience(ExternRef, ExternRef, WasmPtr) block_weather(ExternRef, WasmPtr) change_capture_rate_bonus(ExternRef, ExternRef, WasmPtr) - +} + +impl ScriptFunctionCache { + pub(crate) fn script_get_name(&self, env: &Arc) -> Option> { + { + let read_lock = self.script_get_name.read(); + if let Some(f) = read_lock.as_ref() { + return Some(f.clone()); + } + } + { + let exported = env.exported_functions(); + let f = exported.get::(&"script_get_name".into()); + if let Some(f) = f { + let func: TypedFunction = f.typed(&env.store_ref()).unwrap(); + let _ = self.script_get_name.write().insert(func); + } + } + self.script_get_name.read().as_ref().cloned() + } + + pub(crate) fn dealloc_cstring(&self, env: &Arc) -> Option> { + { + let read_lock = self.dealloc_cstring.read(); + if let Some(f) = read_lock.as_ref() { + return Some(f.clone()); + } + } + { + let exported = env.exported_functions(); + let f = exported.get::(&"dealloc_cstring".into()); + if let Some(f) = f { + let func: TypedFunction = f.typed(&env.store_ref()).unwrap(); + let _ = self.dealloc_cstring.write().insert(func); + } + } + self.dealloc_cstring.read().as_ref().cloned() + } } diff --git a/src/static_data/species_data/gender.rs b/src/static_data/species_data/gender.rs index d47e283..ca3cdaa 100755 --- a/src/static_data/species_data/gender.rs +++ b/src/static_data/species_data/gender.rs @@ -3,6 +3,7 @@ /// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature /// that allows for a more progressive gender system for those that want it? #[derive(Eq, PartialEq, Copy, Clone, Debug)] +#[repr(u8)] pub enum Gender { /// The Pokemon has no gender. Genderless = 0, diff --git a/src/utils/string_key.rs b/src/utils/string_key.rs index 8202f1e..ce57b55 100755 --- a/src/utils/string_key.rs +++ b/src/utils/string_key.rs @@ -1,5 +1,5 @@ use std::borrow::Borrow; -use std::ffi::CString; +use std::ffi::CStr; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::Deref; @@ -106,18 +106,11 @@ impl Display for StringKey { } } -impl Into for CString { +impl Into for &CStr { fn into(self) -> StringKey { StringKey::new(self.to_str().unwrap()) } } - -impl Into for &CString { - fn into(self) -> StringKey { - StringKey::new(self.to_str().unwrap()) - } -} - /// Converts a character to lowercased in a const safe way. const fn to_lower(c: u8) -> u8 { if c >= b'A' && c <= b'Z' { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index ea7572b..14cfa46 100755 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,4 +1,4 @@ -pub mod data_getter; +mod data_getter; pub mod library_loader; pub mod test_case; pub mod test_step; diff --git a/tests/data/gen7_scripts.wasm b/tests/data/gen7_scripts.wasm index 9ca435c..2e9857b 100755 Binary files a/tests/data/gen7_scripts.wasm and b/tests/data/gen7_scripts.wasm differ diff --git a/tests/main.rs b/tests/main.rs index 7ce09ee..a48dc5a 100755 --- a/tests/main.rs +++ b/tests/main.rs @@ -9,7 +9,11 @@ use std::sync::Arc; use conquer_once::OnceCell; -use pkmn_lib::dynamic_data::DynamicLibrary; +use pkmn_lib::dynamic_data::{ + Battle, BattleParty, DamageSource, DynamicLibrary, ExecutingMove, MoveChoice, PokemonBuilder, PokemonParty, Script, + ScriptCategory, ScriptContainer, ScriptOwnerData, TurnChoice, VolatileScriptsOwner, +}; +use pkmn_lib::script_implementations::wasm::script::WebAssemblyScript; use crate::common::{library_loader, TestCase}; @@ -50,3 +54,65 @@ fn integration_tests(input: &Path) { println!("\tRunning integration test {}", test_case.name); test_case.run_test(get_library()); } + +/// Assurance has the interesting properties that it creates a special data script internally, and +/// deletes that data script through the get_owner functionality. +#[test] +#[cfg_attr(miri, ignore)] +fn validate_assurance() { + let lib = get_library(); + let p1 = Arc::new( + PokemonBuilder::new(lib.clone(), "charizard".into(), 100) + .learn_move("assurance".into()) + .build(), + ); + let p2 = Arc::new(PokemonBuilder::new(lib.clone(), "venusaur".into(), 100).build()); + + let party1 = BattleParty::new( + Arc::new(PokemonParty::new_from_vec(vec![Some(p1.clone())])), + vec![(0, 0)], + ); + let party2 = BattleParty::new( + Arc::new(PokemonParty::new_from_vec(vec![Some(p2.clone())])), + vec![(1, 0)], + ); + + let battle = Battle::new(lib.clone(), vec![party1, party2], false, 2, 1, None); + + battle.sides()[0].set_pokemon(0, Some(p1.clone())); + battle.sides()[1].set_pokemon(0, Some(p2.clone())); + + let script = lib + .load_script(ScriptOwnerData::None, ScriptCategory::Move, &"assurance".into()) + .unwrap() + .unwrap(); + + let mv = p1.learned_moves().read()[0].as_ref().unwrap().clone(); + let choice = TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0)); + script.on_before_turn(&choice); + assert!(battle.sides()[1].has_volatile_script(&"assurance_data".into())); + + let executing_move = ExecutingMove::new( + vec![], + 1, + p1.clone(), + mv.clone(), + mv.move_data().clone(), + ScriptContainer::default(), + ); + let mut v = 20_u8; + script.change_base_power(&executing_move, &p2, 0, &mut v); + assert_eq!(v, 20_u8); + + let s = battle.sides()[1].get_volatile_script(&"assurance_data".into()); + let data_script = s.as_ref().unwrap().get_as::(); + + data_script.on_damage(&p2, DamageSource::Misc, 100, 50); + + let mut v = 20_u8; + script.change_base_power(&executing_move, &p2, 0, &mut v); + assert_eq!(v, 40_u8); + + data_script.on_end_turn(); + assert!(!battle.sides()[1].has_volatile_script(&"assurance_data".into())); +}