From 05430c5e848223421eb0383f684e7493e5a06e28 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 28 Aug 2022 15:50:12 +0200 Subject: [PATCH] Implements first few actual move effects. --- Cargo.toml | 1 + gen_7_scripts/src/lib.rs | 3 +- gen_7_scripts/src/moves/acrobatics.rs | 40 ++ gen_7_scripts/src/moves/acupressure.rs | 35 ++ gen_7_scripts/src/moves/after_you.rs | 35 ++ gen_7_scripts/src/moves/mod.rs | 4 + gen_7_scripts/src/moves/multi_hit_move.rs | 37 ++ gen_7_scripts/src/registered_scripts.rs | 19 +- gen_7_scripts/src/test_script.rs | 102 --- .../src/util_scripts/force_effect_trigger.rs | 35 ++ gen_7_scripts/src/util_scripts/mod.rs | 3 + pkmn_lib_interface/Cargo.toml | 2 +- .../src/app_interface/dynamic_data/battle.rs | 9 +- .../dynamic_data/battle_party.rs | 1 - .../dynamic_data/choice_queue.rs | 31 + .../dynamic_data/executing_move.rs | 161 +++++ .../src/app_interface/dynamic_data/mod.rs | 4 + .../src/app_interface/dynamic_data/pokemon.rs | 17 +- .../dynamic_data/turn_choices.rs | 64 +- pkmn_lib_interface/src/app_interface/list.rs | 17 +- pkmn_lib_interface/src/app_interface/mod.rs | 3 +- .../data_libraries/item_library.rs | 5 +- .../static_data/data_libraries/mod.rs | 20 +- .../data_libraries/move_library.rs | 5 +- .../data_libraries/species_library.rs | 5 +- .../data_libraries/type_library.rs | 26 +- .../src/app_interface/static_data/item.rs | 2 +- .../src/app_interface/static_data/mod.rs | 20 + .../app_interface/static_data/move_data.rs | 6 +- .../src/app_interface/static_data/species.rs | 17 +- .../src/app_interface/string_key.rs | 23 +- pkmn_lib_interface/src/handling/cacheable.rs | 25 +- .../src/handling/capabilities.rs | 53 +- pkmn_lib_interface/src/handling/extern_ref.rs | 17 + pkmn_lib_interface/src/handling/script.rs | 331 +++++++++- pkmn_lib_interface/src/handling/temporary.rs | 132 ++-- pkmn_lib_interface/src/lib.rs | 580 ++++++++++++++++-- pkmn_lib_interface/src/utils.rs | 4 +- 38 files changed, 1539 insertions(+), 355 deletions(-) create mode 100644 gen_7_scripts/src/moves/acrobatics.rs create mode 100644 gen_7_scripts/src/moves/acupressure.rs create mode 100644 gen_7_scripts/src/moves/after_you.rs create mode 100644 gen_7_scripts/src/moves/mod.rs create mode 100644 gen_7_scripts/src/moves/multi_hit_move.rs delete mode 100644 gen_7_scripts/src/test_script.rs create mode 100644 gen_7_scripts/src/util_scripts/force_effect_trigger.rs create mode 100644 gen_7_scripts/src/util_scripts/mod.rs create mode 100644 pkmn_lib_interface/src/app_interface/dynamic_data/choice_queue.rs create mode 100644 pkmn_lib_interface/src/app_interface/dynamic_data/executing_move.rs diff --git a/Cargo.toml b/Cargo.toml index 22c8dac..3ac6ea5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] edition = "2021" +resolver = "2" members = [ "pkmn_lib_interface", diff --git a/gen_7_scripts/src/lib.rs b/gen_7_scripts/src/lib.rs index 10d6dc3..29ae7c8 100644 --- a/gen_7_scripts/src/lib.rs +++ b/gen_7_scripts/src/lib.rs @@ -12,7 +12,8 @@ use pkmn_lib_interface::set_load_script_fn; #[macro_use] pub mod registered_scripts; -pub mod test_script; +pub mod moves; +pub mod util_scripts; #[no_mangle] #[cfg(not(test))] diff --git a/gen_7_scripts/src/moves/acrobatics.rs b/gen_7_scripts/src/moves/acrobatics.rs new file mode 100644 index 0000000..967a6e5 --- /dev/null +++ b/gen_7_scripts/src/moves/acrobatics.rs @@ -0,0 +1,40 @@ +use pkmn_lib_interface::app_interface::{ExecutingMove, Pokemon}; +use pkmn_lib_interface::handling::{Script, ScriptCapabilities}; + +pub struct Acrobatics {} + +impl Acrobatics { + pub const fn get_const_name() -> &'static str { + "acrobatics" + } +} + +impl Script for Acrobatics { + fn new() -> Self { + Self {} + } + + fn get_name() -> &'static str { + Self::get_const_name() + } + + fn get_capabilities(&self) -> &[ScriptCapabilities] { + &[ScriptCapabilities::ChangeBasePower] + } + + fn change_base_power( + &self, + mv: ExecutingMove, + _target: Pokemon, + _hit: u8, + base_power: &mut u8, + ) { + if mv.user().held_item().is_none() { + if *base_power >= 128_u8 { + *base_power = 255 + } else { + *base_power *= 2; + } + } + } +} diff --git a/gen_7_scripts/src/moves/acupressure.rs b/gen_7_scripts/src/moves/acupressure.rs new file mode 100644 index 0000000..fa915f8 --- /dev/null +++ b/gen_7_scripts/src/moves/acupressure.rs @@ -0,0 +1,35 @@ +use core::mem::transmute; +use pkmn_lib_interface::app_interface::{ExecutingMove, Pokemon, Statistic}; +use pkmn_lib_interface::handling::{Script, ScriptCapabilities}; + +pub struct Acupressure {} + +impl Acupressure { + pub const fn get_const_name() -> &'static str { + "acupressure" + } +} + +impl Script for Acupressure { + fn new() -> Self { + Self {} + } + + fn get_name() -> &'static str { + Self::get_const_name() + } + + fn get_capabilities(&self) -> &[ScriptCapabilities] { + &[ScriptCapabilities::OnSecondaryEffect] + } + + fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) { + if target == mv.user() { + mv.get_hit_data(&target, hit).fail(); + return; + } + let rand_stat: Statistic = + unsafe { transmute(target.battle().unwrap().random().get_between(1, 6) as u8) }; + target.change_stat_boost(rand_stat, 2, false); + } +} diff --git a/gen_7_scripts/src/moves/after_you.rs b/gen_7_scripts/src/moves/after_you.rs new file mode 100644 index 0000000..477b59f --- /dev/null +++ b/gen_7_scripts/src/moves/after_you.rs @@ -0,0 +1,35 @@ +use pkmn_lib_interface::app_interface::{ExecutingMove, Pokemon}; +use pkmn_lib_interface::handling::{Script, ScriptCapabilities}; + +pub struct AfterYou {} + +impl AfterYou { + pub const fn get_const_name() -> &'static str { + "after_you" + } +} + +impl Script for AfterYou { + fn new() -> Self { + Self {} + } + + fn get_name() -> &'static str { + Self::get_const_name() + } + + fn get_capabilities(&self) -> &[ScriptCapabilities] { + &[ScriptCapabilities::OnSecondaryEffect] + } + + fn on_secondary_effect(&self, mv: ExecutingMove, target: Pokemon, hit: u8) { + if !target + .battle() + .unwrap() + .choice_queue() + .move_pokemon_choice_next(&target) + { + mv.get_hit_data(&target, hit).fail() + } + } +} diff --git a/gen_7_scripts/src/moves/mod.rs b/gen_7_scripts/src/moves/mod.rs new file mode 100644 index 0000000..afbce0c --- /dev/null +++ b/gen_7_scripts/src/moves/mod.rs @@ -0,0 +1,4 @@ +pub mod acrobatics; +pub mod acupressure; +pub mod after_you; +pub mod multi_hit_move; diff --git a/gen_7_scripts/src/moves/multi_hit_move.rs b/gen_7_scripts/src/moves/multi_hit_move.rs new file mode 100644 index 0000000..121ec0d --- /dev/null +++ b/gen_7_scripts/src/moves/multi_hit_move.rs @@ -0,0 +1,37 @@ +use pkmn_lib_interface::app_interface::TurnChoice; +use pkmn_lib_interface::handling::{Script, ScriptCapabilities}; + +pub struct MultiHitMove {} + +impl MultiHitMove { + pub const fn get_const_name() -> &'static str { + "2_5_hit_move" + } +} + +impl Script for MultiHitMove { + fn new() -> Self { + Self {} + } + + fn get_name() -> &'static str { + Self::get_const_name() + } + + fn get_capabilities(&self) -> &[ScriptCapabilities] { + &[ScriptCapabilities::ChangeNumberOfHits] + } + + fn change_number_of_hits(&self, choice: TurnChoice, number_of_hits: &mut u8) { + // 35% chance that it will hit 2 times, a 35% chance it will hit 3 times, a 15% chance it + // will hit 4 times, and a 15% chance it will hit 5 times. + let rand_value = choice.user().battle().unwrap().random().get_between(0, 100); + *number_of_hits = match rand_value { + 0..=34 => 2, + 35..=69 => 3, + 70..=84 => 4, + 85..=100 => 5, + _ => *number_of_hits, + } + } +} diff --git a/gen_7_scripts/src/registered_scripts.rs b/gen_7_scripts/src/registered_scripts.rs index 1f17759..17655f1 100644 --- a/gen_7_scripts/src/registered_scripts.rs +++ b/gen_7_scripts/src/registered_scripts.rs @@ -1,19 +1,19 @@ -use crate::test_script::TestScript; +use crate::moves::*; use alloc::boxed::Box; -use pkmn_lib_interface::app_interface::{get_hash_const, StringKey}; +use pkmn_lib_interface::app_interface::{get_hash, StringKey}; use pkmn_lib_interface::handling::{Script, ScriptCategory}; macro_rules! resolve_match { ( $mid:expr, $( - $key:expr => $script:ident, + $script:ty, )* ) => ( match $mid { $( - const { get_hash_const($key) } => { - return Some(Box::new($script {})) + const { get_hash(<$script>::get_const_name()) } => { + return Some(Box::new(<$script>::new())) } )* _ => {} @@ -26,13 +26,18 @@ pub fn get_script(category: ScriptCategory, name: &StringKey) -> Option { resolve_match!( name.hash(), - b"test" => TestScript, + acrobatics::Acrobatics, + acupressure::Acupressure, + after_you::AfterYou, + multi_hit_move::MultiHitMove, ); } ScriptCategory::Ability => {} ScriptCategory::Status => {} ScriptCategory::Pokemon => {} - ScriptCategory::Battle => {} + ScriptCategory::Battle => { + resolve_match!(name.hash(), crate::util_scripts::ForceEffectTriggerScript,) + } ScriptCategory::Side => {} ScriptCategory::ItemBattleTrigger => {} } diff --git a/gen_7_scripts/src/test_script.rs b/gen_7_scripts/src/test_script.rs deleted file mode 100644 index 61e10f2..0000000 --- a/gen_7_scripts/src/test_script.rs +++ /dev/null @@ -1,102 +0,0 @@ -use pkmn_lib_interface::app_interface::list::ImmutableList; -use pkmn_lib_interface::app_interface::{ - get_hash_const, DamageSource, DataLibrary, DynamicLibrary, EffectParameter, TurnChoice, -}; -use pkmn_lib_interface::dbg; -use pkmn_lib_interface::handling::{Script, ScriptCapabilities}; - -pub struct TestScript {} - -impl Script for TestScript { - fn new() -> Self { - TestScript {} - } - fn destroy(&self) {} - - fn get_name(&self) -> &str { - "TestScript" - } - - fn get_capabilities(&self) -> &[ScriptCapabilities] { - &[ - ScriptCapabilities::Initialize, - ScriptCapabilities::OnBeforeTurn, - ] - } - - fn on_initialize( - &self, - library: &DynamicLibrary, - parameters: Option>, - ) { - let l = library.data_library(); - let ml = l.move_library(); - let m = ml.get_by_hash(const { get_hash_const(b"tackle") }).unwrap(); - dbg!("found move!"); - dbg!("{:?} has {} base power", m.name().str(), m.base_power()); - dbg!( - "Found a parameter with value: {}", - parameters.unwrap().get(0).unwrap() - ); - } - - fn on_before_turn(&self, choice: TurnChoice) { - dbg!( - "On before turn for user: {}", - choice.user().species().name() - ); - // choice.user().damage(50, DamageSource::Misc); - if let TurnChoice::Move(d) = choice { - dbg!( - "On before turn for move choice: {}", - d.used_move().move_data().name() - ); - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use pkmn_lib_interface::app_interface::move_library::MoveLibrary; - use pkmn_lib_interface::app_interface::species_library::SpeciesLibrary; - use pkmn_lib_interface::app_interface::type_library::TypeLibrary; - use pkmn_lib_interface::app_interface::{ - Item, ItemLibrary, LibrarySettings, MoveCategory, MoveData, MoveTarget, StaticData, - }; - use pkmn_lib_interface::handling::Script; - - #[test] - fn test_foo() { - let item = Item::mock(); - assert_eq!(item.name().str().to_str().unwrap(), "test"); - - let script = TestScript::new(); - assert_eq!(script.get_name(), "TestScript"); - - let lib = DynamicLibrary::new(StaticData::mock( - MoveLibrary::mock(), - ItemLibrary::mock(), - SpeciesLibrary::mock(), - TypeLibrary::mock(), - LibrarySettings::mock(100), - )); - lib.data_library().move_library().insert( - const { get_hash_const(b"tackle") }, - MoveData::mock( - "tackle", - 0, - MoveCategory::Physical, - 60, - 100, - 10, - MoveTarget::Adjacent, - 0, - ), - ); - script.on_initialize( - &lib, - Some(ImmutableList::mock((&[EffectParameter::Int(100)]).to_vec())), - ); - } -} diff --git a/gen_7_scripts/src/util_scripts/force_effect_trigger.rs b/gen_7_scripts/src/util_scripts/force_effect_trigger.rs new file mode 100644 index 0000000..f222f27 --- /dev/null +++ b/gen_7_scripts/src/util_scripts/force_effect_trigger.rs @@ -0,0 +1,35 @@ +use pkmn_lib_interface::app_interface::{ExecutingMove, Pokemon}; +use pkmn_lib_interface::handling::{Script, ScriptCapabilities}; + +pub struct ForceEffectTriggerScript {} + +impl ForceEffectTriggerScript { + pub const fn get_const_name() -> &'static str { + "force_effect_trigger" + } +} + +impl Script for ForceEffectTriggerScript { + fn new() -> Self { + Self {} + } + + fn get_name() -> &'static str { + Self::get_const_name() + } + + fn get_capabilities(&self) -> &[ScriptCapabilities] { + &[ScriptCapabilities::ChangeEffectChance] + } + + fn change_effect_chance( + &self, + _mv: ExecutingMove, + _target: Pokemon, + _hit: u8, + chance: &mut f32, + ) { + // Set to 50_000% chance. + *chance = 50_000.0; + } +} diff --git a/gen_7_scripts/src/util_scripts/mod.rs b/gen_7_scripts/src/util_scripts/mod.rs new file mode 100644 index 0000000..cbf2d3f --- /dev/null +++ b/gen_7_scripts/src/util_scripts/mod.rs @@ -0,0 +1,3 @@ +mod force_effect_trigger; + +pub use force_effect_trigger::*; diff --git a/pkmn_lib_interface/Cargo.toml b/pkmn_lib_interface/Cargo.toml index d6e1cde..d950253 100644 --- a/pkmn_lib_interface/Cargo.toml +++ b/pkmn_lib_interface/Cargo.toml @@ -10,10 +10,10 @@ mock_data = [] [dependencies] wee_alloc = "0.4.5" cstr_core = { version = "0.2.6", features = ["nightly"]} -lazy_static = { version = "1.4.0", features = ["spin_no_std"] } enumflags2 = { version = "0.7.5", default-features = false } spin = { version = "0.9.4", default-features = false, features = ["rwlock"] } paste = { version = "1.0.7" } +hashbrown = { version = "0.12.3" } [dev-dependencies] diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/battle.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/battle.rs index 08c4047..9314a70 100644 --- a/pkmn_lib_interface/src/app_interface/dynamic_data/battle.rs +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/battle.rs @@ -1,4 +1,4 @@ -use crate::app_interface::{BattleParty, BattleRandom, BattleSide, Pokemon}; +use crate::app_interface::{BattleParty, BattleRandom, BattleSide, ChoiceQueue, Pokemon}; use crate::handling::cached_value::CachedValue; use crate::handling::Cacheable; use crate::{ @@ -13,6 +13,7 @@ struct BattleInner { parties: CachedValue>, sides: CachedValue>, random: CachedValue, + choice_queue: CachedValue, } #[derive(Clone)] @@ -30,6 +31,9 @@ impl Battle { parties: cached_value!({ battle_get_parties(reference).get_immutable_list() }), sides: cached_value!({ battle_get_sides(reference).get_immutable_list() }), random: cached_value!({ battle_get_random(reference).get_value().unwrap() }), + choice_queue: cached_value!({ + battle_get_choice_queue(reference).get_value().unwrap() + }), }), }) } @@ -38,6 +42,8 @@ impl Battle { pub fn library(&self) -> DynamicLibrary; pub fn parties(&self) -> ImmutableList; pub fn sides(&self) -> ImmutableList; + pub fn random(&self) -> BattleRandom; + pub fn choice_queue(&self) -> ChoiceQueue; } #[cfg(not(feature = "mock_data"))] @@ -72,5 +78,6 @@ extern "wasm" { fn battle_get_parties(r: ExternRef) -> VecExternRef; fn battle_get_sides(r: ExternRef) -> VecExternRef; fn battle_get_random(r: ExternRef) -> ExternRef; + fn battle_get_choice_queue(r: ExternRef) -> ExternRef; fn battle_get_pokemon(r: ExternRef, side: u8, index: u8) -> ExternRef; } diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/battle_party.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/battle_party.rs index 7ef96e0..87dc1e3 100644 --- a/pkmn_lib_interface/src/app_interface/dynamic_data/battle_party.rs +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/battle_party.rs @@ -39,7 +39,6 @@ impl ExternalReferenceType for BattleParty { } } - #[cfg(not(feature = "mock_data"))] extern "wasm" { fn battle_party_get_party(r: ExternRef) -> ExternRef; diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/choice_queue.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/choice_queue.rs new file mode 100644 index 0000000..b39959c --- /dev/null +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/choice_queue.rs @@ -0,0 +1,31 @@ +use crate::{ExternRef, ExternalReferenceType, Pokemon}; + +#[derive(Clone)] +pub struct ChoiceQueue { + reference: ExternRef, +} + +impl ChoiceQueue { + #[cfg(not(feature = "mock_data"))] + pub fn new(reference: ExternRef) -> Self { + Self { reference } + } + + pub fn move_pokemon_choice_next(&self, pokemon: &Pokemon) -> bool { + unsafe { choice_queue_move_pokemon_choice_next(self.reference, pokemon.reference()) } + } +} + +#[cfg(not(feature = "mock_data"))] +impl ExternalReferenceType for ChoiceQueue { + fn from_extern_value(reference: ExternRef) -> Self { + Self::new(reference) + } +} + +extern "wasm" { + fn choice_queue_move_pokemon_choice_next( + r: ExternRef, + pokemon: ExternRef, + ) -> bool; +} diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/executing_move.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/executing_move.rs new file mode 100644 index 0000000..e421f69 --- /dev/null +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/executing_move.rs @@ -0,0 +1,161 @@ +use crate::app_interface::{LearnedMove, MoveData, Pokemon}; +use crate::handling::cached_value::CachedValue; +use crate::handling::temporary::Temporary; +use crate::{cached_value, ExternRef, ExternalReferenceType, Script}; + +struct ExecutingMoveInner { + reference: ExternRef, + number_of_hits: CachedValue, + user: CachedValue, + chosen_move: CachedValue, + use_move: CachedValue, +} + +#[derive(Clone)] +pub struct ExecutingMove { + inner: Temporary, +} + +impl ExecutingMove { + pub(crate) fn new(reference: ExternRef) -> Self { + Self { + inner: Temporary::new( + reference.get_internal_index(), + ExecutingMoveInner { + reference, + number_of_hits: cached_value!({ executing_move_get_number_of_hits(reference) }), + user: cached_value!({ + executing_move_get_user(reference).get_value().unwrap() + }), + chosen_move: cached_value!({ + executing_move_get_chosen_move(reference) + .get_value() + .unwrap() + }), + use_move: cached_value!({ + executing_move_get_use_move(reference).get_value().unwrap() + }), + }, + ), + } + } + + pub fn number_of_hits(&self) -> u8 { + self.inner.value().number_of_hits.value() + } + pub fn user(&self) -> Pokemon { + self.inner.value().user.value() + } + pub fn chosen_move(&self) -> LearnedMove { + self.inner.value().chosen_move.value() + } + pub fn use_move(&self) -> MoveData { + self.inner.value().use_move.value() + } + pub fn move_script(&self) -> Option<&dyn Script> { + unsafe { executing_move_get_script(self.inner.value().reference).as_ref() } + } + pub fn number_of_targets(&self) -> usize { + unsafe { executing_move_get_number_of_targets(self.inner.value().reference) } + } + pub fn is_pokemon_target(&self, pokemon: &Pokemon) -> bool { + unsafe { + executing_move_is_pokemon_target(self.inner.value().reference, pokemon.reference()) + } + } + pub fn get_hit_data(&self, pokemon: &Pokemon, hit: u8) -> HitData { + unsafe { + executing_move_get_hit_data(self.inner.value().reference, pokemon.reference(), hit) + .get_value() + .unwrap() + } + } +} + +#[derive(Clone)] +pub struct HitData { + reference: ExternRef, +} + +impl HitData { + pub fn is_critical(&self) -> bool { + unsafe { hit_data_is_critical(self.reference) } + } + pub fn base_power(&self) -> u8 { + unsafe { hit_data_get_base_power(self.reference) } + } + pub fn effectiveness(&self) -> f32 { + unsafe { hit_data_get_effectiveness(self.reference) } + } + pub fn damage(&self) -> u32 { + unsafe { hit_data_get_damage(self.reference) } + } + pub fn move_type(&self) -> u8 { + unsafe { hit_data_get_move_type(self.reference) } + } + pub fn has_failed(&self) -> bool { + unsafe { hit_data_is_critical(self.reference) } + } + + pub fn set_critical(&self, critical: bool) { + unsafe { hit_data_set_critical(self.reference, critical) } + } + pub fn set_effectiveness(&self, effectiveness: f32) { + unsafe { hit_data_set_effectiveness(self.reference, effectiveness) } + } + pub fn set_damage(&self, damage: u32) { + unsafe { hit_data_set_damage(self.reference, damage) } + } + pub fn set_move_type(&self, move_type: u8) { + unsafe { hit_data_set_move_type(self.reference, move_type) } + } + pub fn fail(&self) { + unsafe { hit_data_fail(self.reference) } + } +} + +impl ExternalReferenceType for ExecutingMove { + fn from_extern_value(reference: ExternRef) -> Self { + Self::new(reference) + } +} + +impl ExternalReferenceType for HitData { + fn from_extern_value(reference: ExternRef) -> Self { + Self { reference } + } +} + +extern "wasm" { + fn executing_move_get_number_of_targets(r: ExternRef) -> usize; + fn executing_move_get_number_of_hits(r: ExternRef) -> u8; + fn executing_move_get_user(r: ExternRef) -> ExternRef; + fn executing_move_get_chosen_move(r: ExternRef) -> ExternRef; + fn executing_move_get_use_move(r: ExternRef) -> ExternRef; + #[allow(improper_ctypes)] + fn executing_move_get_script(r: ExternRef) -> *const dyn Script; + + fn executing_move_is_pokemon_target( + r: ExternRef, + pokemon: ExternRef, + ) -> bool; + fn executing_move_get_hit_data( + r: ExternRef, + target: ExternRef, + hit: u8, + ) -> ExternRef; + + fn hit_data_is_critical(r: ExternRef) -> bool; + fn hit_data_get_base_power(r: ExternRef) -> u8; + fn hit_data_get_effectiveness(r: ExternRef) -> f32; + fn hit_data_get_damage(r: ExternRef) -> u32; + fn hit_data_get_move_type(r: ExternRef) -> u8; + fn hit_data_has_failed(r: ExternRef) -> bool; + + fn hit_data_set_critical(r: ExternRef, critical: bool); + fn hit_data_set_base_power(r: ExternRef, power: u8); + fn hit_data_set_effectiveness(r: ExternRef, effectiveness: f32); + fn hit_data_set_damage(r: ExternRef, damage: u32); + fn hit_data_set_move_type(r: ExternRef, move_type: u8); + fn hit_data_fail(r: ExternRef); +} diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/mod.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/mod.rs index 03edd0d..8ca783b 100644 --- a/pkmn_lib_interface/src/app_interface/dynamic_data/mod.rs +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/mod.rs @@ -2,7 +2,9 @@ mod battle; mod battle_party; mod battle_random; mod battle_side; +mod choice_queue; mod dynamic_library; +mod executing_move; mod learned_move; mod party; mod pokemon; @@ -13,7 +15,9 @@ pub use battle::*; pub use battle_party::*; pub use battle_random::*; pub use battle_side::*; +pub use choice_queue::*; pub use dynamic_library::DynamicLibrary; +pub use executing_move::*; pub use learned_move::*; pub use party::*; pub use pokemon::*; diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/pokemon.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/pokemon.rs index 615c25e..b8ef9f7 100644 --- a/pkmn_lib_interface/src/app_interface/dynamic_data/pokemon.rs +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/pokemon.rs @@ -7,7 +7,7 @@ use crate::handling::cached_value::CachedValue; use crate::handling::Cacheable; use crate::{ cached_value, cached_value_getters, wasm_optional_reference_getters, wasm_reference_getters, - wasm_value_getters, DynamicLibrary, ExternRef, ExternalReferenceType, Script, + wasm_value_getters, DynamicLibrary, ExternRef, ExternalReferenceType, Script, TypeIdentifier, }; use alloc::boxed::Box; use alloc::rc::Rc; @@ -61,6 +61,10 @@ impl Pokemon { }) } + pub(crate) fn reference(&self) -> ExternRef { + self.inner.reference + } + cached_value_getters! { pub fn library(&self) -> DynamicLibrary; pub fn flat_stats(&self) -> StatisticSet; @@ -101,8 +105,8 @@ impl Pokemon { unsafe { pokemon_get_type(self.inner.reference, index) } } #[cfg(not(feature = "mock_data"))] - pub fn has_type(&self, type_identifier: u8) -> bool { - unsafe { pokemon_has_type(self.inner.reference, type_identifier) } + pub fn has_type(&self, type_identifier: TypeIdentifier) -> bool { + unsafe { pokemon_has_type(self.inner.reference, type_identifier.into()) } } #[cfg(not(feature = "mock_data"))] pub fn has_type_by_name(&self, type_name: &str) -> bool { @@ -206,6 +210,12 @@ wasm_value_getters! { pub fn is_usable(&self) -> bool; } +impl PartialEq for Pokemon { + fn eq(&self, other: &Self) -> bool { + self.inner.reference == other.inner.reference + } +} + /// A source of damage. This should be as unique as possible. #[derive(Debug, Clone, Copy)] #[repr(u8)] @@ -246,6 +256,7 @@ extern "wasm" { diff_amount: i8, self_inflicted: bool, ) -> bool; + #[allow(improper_ctypes)] fn pokemon_get_ability_script(r: ExternRef) -> *const Box; fn pokemon_change_species( r: ExternRef, diff --git a/pkmn_lib_interface/src/app_interface/dynamic_data/turn_choices.rs b/pkmn_lib_interface/src/app_interface/dynamic_data/turn_choices.rs index f5a1e3c..1f6de76 100644 --- a/pkmn_lib_interface/src/app_interface/dynamic_data/turn_choices.rs +++ b/pkmn_lib_interface/src/app_interface/dynamic_data/turn_choices.rs @@ -3,10 +3,9 @@ use crate::handling::cached_value::CachedValue; use crate::handling::temporary::Temporary; use crate::ExternRef; #[cfg(not(feature = "mock_data"))] -use crate::{cached_value, wasm_value_getters, ExternalReferenceType, Script}; +use crate::{cached_value, ExternalReferenceType, Script}; #[cfg(not(feature = "mock_data"))] use alloc::boxed::Box; -use alloc::rc::Rc; struct BaseTurnChoiceData { reference: ExternRef, @@ -19,12 +18,10 @@ struct MoveTurnChoiceDataInner { target_side: CachedValue, target_index: CachedValue, } + #[derive(Clone)] -struct MoveTurnChoiceDataTemporary { - inner: Rc, -} pub struct MoveTurnChoiceData { - temp: Temporary, + inner: Temporary, } pub enum TurnChoice { @@ -38,7 +35,7 @@ pub enum TurnChoice { impl TurnChoice { fn base(&self) -> &BaseTurnChoiceData { match self { - TurnChoice::Move(d) => &d.temp.value_ref().inner.base, + TurnChoice::Move(d) => &d.inner.value_ref().base, TurnChoice::Item() => unimplemented!(), TurnChoice::Switch() => unimplemented!(), TurnChoice::Flee => unimplemented!(), @@ -68,22 +65,22 @@ impl TurnChoice { impl MoveTurnChoiceData { pub fn used_move(&self) -> LearnedMove { - self.temp.value().inner.used_move.value() + self.inner.value().used_move.value() } pub fn target_side(&self) -> u8 { - self.temp.value().inner.target_side.value() + self.inner.value().target_side.value() } pub fn target_index(&self) -> u8 { - self.temp.value().inner.target_index.value() + self.inner.value().target_index.value() } #[cfg(not(feature = "mock_data"))] pub fn priority(&self) -> i8 { - unsafe { turn_choice_move_priority(self.temp.value().inner.base.reference.cast()) } + unsafe { turn_choice_move_priority(self.inner.value().base.reference.cast()) } } #[cfg(not(feature = "mock_data"))] pub fn move_script(&self) -> Option<&Box> { - unsafe { turn_choice_move_script(self.temp.value().inner.base.reference.cast()).as_ref() } + unsafe { turn_choice_move_script(self.inner.value().base.reference.cast()).as_ref() } } } @@ -93,7 +90,10 @@ impl ExternalReferenceType for TurnChoice { let kind = unsafe { turn_choice_get_kind(reference) }; match kind { 0 => TurnChoice::Move(MoveTurnChoiceData { - temp: Temporary::from_reference(reference.cast()), + inner: Temporary::new( + reference.get_internal_index(), + MoveTurnChoiceDataInner::from_reference(reference.cast()), + ), }), _ => panic!("Unknown turn choice type"), } @@ -101,24 +101,22 @@ impl ExternalReferenceType for TurnChoice { } #[cfg(not(feature = "mock_data"))] -impl ExternalReferenceType for MoveTurnChoiceDataTemporary { - fn from_extern_value(reference: ExternRef) -> Self { +impl MoveTurnChoiceDataInner { + fn from_reference(reference: ExternRef) -> Self { Self { - inner: Rc::new(MoveTurnChoiceDataInner { - base: BaseTurnChoiceData { - reference: reference.cast(), - user: cached_value!({ - turn_choice_get_user(reference.cast()).get_value().unwrap() - }), - }, - used_move: cached_value!({ - turn_choice_move_used_move(reference.cast()) - .get_value() - .unwrap() + base: BaseTurnChoiceData { + reference: reference.cast(), + user: cached_value!({ + turn_choice_get_user(reference.cast()).get_value().unwrap() }), - target_side: cached_value!({ turn_choice_move_target_side(reference.cast()) }), - target_index: cached_value!({ turn_choice_move_target_index(reference.cast()) }), + }, + used_move: cached_value!({ + turn_choice_move_used_move(reference.cast()) + .get_value() + .unwrap() }), + target_side: cached_value!({ turn_choice_move_target_side(reference.cast()) }), + target_index: cached_value!({ turn_choice_move_target_index(reference.cast()) }), } } } @@ -135,14 +133,6 @@ extern "wasm" { fn turn_choice_move_target_side(r: ExternRef) -> u8; fn turn_choice_move_target_index(r: ExternRef) -> u8; fn turn_choice_move_priority(r: ExternRef) -> i8; + #[allow(improper_ctypes)] fn turn_choice_move_script(r: ExternRef) -> *const Box; } - -#[no_mangle] -#[cfg(not(feature = "mock_data"))] -unsafe extern "wasm" fn turn_choice_mark_deleted(r: ExternRef, kind: u8) { - match kind { - 0 => Temporary::::mark_as_deleted(r.cast()), - _ => panic!("Unknown turn choice type"), - } -} diff --git a/pkmn_lib_interface/src/app_interface/list.rs b/pkmn_lib_interface/src/app_interface/list.rs index 3bd6e87..ec6d055 100644 --- a/pkmn_lib_interface/src/app_interface/list.rs +++ b/pkmn_lib_interface/src/app_interface/list.rs @@ -1,6 +1,6 @@ use crate::{ExternalReferenceType, VecExternRef}; use alloc::boxed::Box; -use alloc::collections::BTreeMap; +#[cfg(feature = "mock_data")] use alloc::rc::Rc; use alloc::vec::Vec; use core::marker::PhantomData; @@ -44,13 +44,22 @@ where pub(crate) fn from_ref(extern_ref: VecExternRef) -> Self { unsafe { - let existing = CACHE.get(&extern_ref.get_internal_index()); + if let None = CACHE { + CACHE = Some(hashbrown::HashMap::new()); + } + let existing = CACHE + .as_ref() + .unwrap() + .get(&extern_ref.get_internal_index()); if let Some(v) = existing { let inner = *v as *const ImmutableListInner; ImmutableList { inner } } else { let v = Self::new(extern_ref); - CACHE.insert(extern_ref.get_internal_index(), v.inner as *const u8); + CACHE + .as_mut() + .unwrap() + .insert(extern_ref.get_internal_index(), v.inner as *const u8); v } } @@ -102,4 +111,4 @@ where } } -static mut CACHE: BTreeMap = BTreeMap::new(); +static mut CACHE: Option> = None; diff --git a/pkmn_lib_interface/src/app_interface/mod.rs b/pkmn_lib_interface/src/app_interface/mod.rs index 9106659..71c48d7 100644 --- a/pkmn_lib_interface/src/app_interface/mod.rs +++ b/pkmn_lib_interface/src/app_interface/mod.rs @@ -5,5 +5,4 @@ pub mod string_key; pub use dynamic_data::*; pub use static_data::*; -pub use string_key::get_hash_const; -pub use string_key::StringKey; +pub use string_key::*; diff --git a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/item_library.rs b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/item_library.rs index da4eb91..da9dde5 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/item_library.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/item_library.rs @@ -1,12 +1,11 @@ use crate::app_interface::{DataLibrary, Item}; use crate::{ExternRef, ExternalReferenceType, StringKey}; -use alloc::collections::BTreeMap; use alloc::rc::Rc; use spin::rwlock::RwLock; struct ItemLibraryInner { ptr: ExternRef, - cache: RwLock>, + cache: RwLock>, } #[derive(Clone)] @@ -27,7 +26,7 @@ impl ItemLibrary { } impl DataLibrary for ItemLibrary { - fn get_cache(&self) -> &spin::rwlock::RwLock> { + fn get_cache(&self) -> &spin::rwlock::RwLock> { &self.inner.cache } diff --git a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/mod.rs b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/mod.rs index 94c8791..c6d2241 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/mod.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/mod.rs @@ -1,5 +1,4 @@ use crate::{cached_value, cached_value_getters, ExternRef, ExternalReferenceType, StringKey}; -use alloc::collections::BTreeMap; use alloc::rc::Rc; use move_library::MoveLibrary; use spin::rwlock::RwLock; @@ -51,6 +50,11 @@ impl StaticData { type_library: cached_value!({ static_data_get_type_library(reference).get_value().unwrap() }), + settings: cached_value!({ + static_data_get_library_settings(reference) + .get_value() + .unwrap() + }), }), }) } @@ -88,7 +92,7 @@ crate::handling::cacheable::cacheable!(StaticData); #[cfg(not(feature = "mock_data"))] impl ExternalReferenceType for StaticData { fn from_extern_value(reference: ExternRef) -> Self { - StaticData::mock(reference) + StaticData::new(reference) } } @@ -125,12 +129,20 @@ impl LibrarySettings { } } +#[cfg(not(feature = "mock_data"))] +impl ExternalReferenceType for LibrarySettings { + fn from_extern_value(reference: ExternRef) -> Self { + LibrarySettings::new(reference) + } +} + #[cfg(not(feature = "mock_data"))] extern "wasm" { fn static_data_get_move_library(ptr: ExternRef) -> ExternRef; fn static_data_get_item_library(ptr: ExternRef) -> ExternRef; fn static_data_get_species_library(ptr: ExternRef) -> ExternRef; fn static_data_get_type_library(ptr: ExternRef) -> ExternRef; + fn static_data_get_library_settings(ptr: ExternRef) -> ExternRef; fn library_settings_get_maximum_level(ptr: ExternRef) -> LevelInt; } @@ -141,7 +153,7 @@ where T: ExternalReferenceType, T: Clone, { - fn get_cache(&self) -> &RwLock>; + fn get_cache(&self) -> &RwLock>; fn get_self_ref(&self) -> ExternRef where Self: Sized; @@ -188,7 +200,7 @@ pub trait DataLibrary: Cacheable where T: Clone, { - fn get_cache(&self) -> &RwLock>; + fn get_cache(&self) -> &RwLock>; fn get_self_ref(&self) -> ExternRef where Self: Sized; diff --git a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/move_library.rs b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/move_library.rs index 1b2265d..e03242b 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/move_library.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/move_library.rs @@ -1,13 +1,12 @@ use crate::app_interface::data_libraries::DataLibrary; use crate::app_interface::{MoveData, StringKey}; use crate::{ExternRef, ExternalReferenceType}; -use alloc::collections::BTreeMap; use alloc::rc::Rc; use spin::RwLock; struct MoveLibraryInner { ptr: ExternRef, - cache: RwLock>, + cache: RwLock>, } #[derive(Clone)] @@ -38,7 +37,7 @@ impl MoveLibrary { } impl DataLibrary for MoveLibrary { - fn get_cache(&self) -> &spin::rwlock::RwLock> { + fn get_cache(&self) -> &spin::rwlock::RwLock> { &self.inner.cache } diff --git a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/species_library.rs b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/species_library.rs index f347099..0db69ec 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/species_library.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/species_library.rs @@ -1,12 +1,11 @@ use crate::app_interface::{DataLibrary, Species}; use crate::{ExternRef, ExternalReferenceType, StringKey}; -use alloc::collections::BTreeMap; use alloc::rc::Rc; use spin::RwLock; struct SpeciesLibraryInner { ptr: ExternRef, - cache: RwLock>, + cache: RwLock>, } #[derive(Clone)] @@ -37,7 +36,7 @@ impl SpeciesLibrary { } impl DataLibrary for SpeciesLibrary { - fn get_cache(&self) -> &spin::rwlock::RwLock> { + fn get_cache(&self) -> &spin::rwlock::RwLock> { &self.inner.cache } diff --git a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/type_library.rs b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/type_library.rs index 807f37d..271af21 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/data_libraries/type_library.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/data_libraries/type_library.rs @@ -1,5 +1,4 @@ -use crate::{ExternRef, ExternalReferenceType}; -use alloc::collections::BTreeMap; +use crate::{ExternRef, ExternalReferenceType, TypeIdentifier}; use alloc::rc::Rc; use alloc::string::{String, ToString}; use cstr_core::{c_char, CString}; @@ -7,8 +6,8 @@ use spin::RwLock; struct TypeLibraryInner { reference: ExternRef, - name_to_type_cache: RwLock>, - effectiveness_cache: RwLock>, + name_to_type_cache: RwLock>, + effectiveness_cache: RwLock>, } #[derive(Clone)] @@ -39,7 +38,7 @@ impl TypeLibrary { } #[cfg(not(feature = "mock_data"))] - pub fn get_type_from_name(&self, name: &str) -> Option { + pub fn get_type_from_name(&self, name: &str) -> Option { if let Some(cached) = self.inner.name_to_type_cache.read().get(name) { return Some(*cached); } @@ -48,6 +47,7 @@ impl TypeLibrary { if v == 255 { return None; } + let v = v.into(); self.inner .name_to_type_cache .write() @@ -56,7 +56,11 @@ impl TypeLibrary { } #[cfg(not(feature = "mock_data"))] - pub fn get_single_effectiveness(&self, attacking_type: u8, defending_type: u8) -> f32 { + pub fn get_single_effectiveness( + &self, + attacking_type: TypeIdentifier, + defending_type: TypeIdentifier, + ) -> f32 { if let Some(cached) = self .inner .effectiveness_cache @@ -68,8 +72,8 @@ impl TypeLibrary { let effectiveness = unsafe { type_library_get_single_effectiveness( self.inner.reference, - attacking_type, - defending_type, + attacking_type.into(), + defending_type.into(), ) }; self.inner @@ -80,7 +84,11 @@ impl TypeLibrary { } #[cfg(not(feature = "mock_data"))] - pub fn get_effectiveness(&self, attacking_type: u8, defending_types: &[u8]) -> f32 { + pub fn get_effectiveness( + &self, + attacking_type: TypeIdentifier, + defending_types: &[TypeIdentifier], + ) -> f32 { let mut f = 1.0; for defending_type in defending_types { f *= self.get_single_effectiveness(attacking_type, *defending_type); diff --git a/pkmn_lib_interface/src/app_interface/static_data/item.rs b/pkmn_lib_interface/src/app_interface/static_data/item.rs index d76c904..09e71f2 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/item.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/item.rs @@ -96,7 +96,7 @@ crate::handling::cacheable::cacheable!(Item); #[cfg(not(feature = "mock_data"))] impl ExternalReferenceType for Item { fn from_extern_value(reference: ExternRef) -> Self { - Item::mock(reference) + Item::new(reference) } } diff --git a/pkmn_lib_interface/src/app_interface/static_data/mod.rs b/pkmn_lib_interface/src/app_interface/static_data/mod.rs index 9974b09..12bad7e 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/mod.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/mod.rs @@ -14,3 +14,23 @@ pub use nature::*; pub use species::*; pub type LevelInt = u8; + +/// A unique key that can be used to store a reference to a type. Opaque reference to a byte +/// internally. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] +pub struct TypeIdentifier { + /// The unique internal value. + val: u8, +} + +impl From for TypeIdentifier { + fn from(val: u8) -> Self { + Self { val } + } +} + +impl From for u8 { + fn from(id: TypeIdentifier) -> Self { + id.val + } +} diff --git a/pkmn_lib_interface/src/app_interface/static_data/move_data.rs b/pkmn_lib_interface/src/app_interface/static_data/move_data.rs index d153cd8..67ce385 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/move_data.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/move_data.rs @@ -1,4 +1,4 @@ -use crate::app_interface::{get_hash_const, StringKey}; +use crate::app_interface::{get_hash, StringKey}; use crate::handling::cached_value::CachedValue; use crate::handling::Cacheable; use crate::{cached_value, cached_value_getters, ExternRef, ExternalReferenceType}; @@ -104,8 +104,8 @@ impl MoveData { } #[cfg(not(feature = "mock_data"))] - pub fn has_flag(&self, flag: &[u8; N]) -> bool { - let hash = get_hash_const(flag); + pub fn has_flag(&self, flag: &str) -> bool { + let hash = get_hash(flag); unsafe { move_data_has_flag_by_hash(self.inner.ptr, hash) } } } diff --git a/pkmn_lib_interface/src/app_interface/static_data/species.rs b/pkmn_lib_interface/src/app_interface/static_data/species.rs index d095c6e..6b96edb 100644 --- a/pkmn_lib_interface/src/app_interface/static_data/species.rs +++ b/pkmn_lib_interface/src/app_interface/static_data/species.rs @@ -1,11 +1,10 @@ -use crate::app_interface::get_hash_const; +use crate::app_interface::get_hash; use crate::handling::cached_value::CachedValue; use crate::handling::Cacheable; use crate::{ cached_value, cached_value_getters, ExternRef, ExternalReferenceType, FFIArray, ImmutableList, StringKey, VecExternRef, }; -use alloc::collections::BTreeMap; use alloc::rc::Rc; use alloc::vec::Vec; use spin::RwLock; @@ -147,8 +146,8 @@ impl Form { } #[cfg(not(feature = "mock_data"))] - pub fn has_flag(&self, flag: &[u8; N]) -> bool { - let hash = get_hash_const(flag); + pub fn has_flag(&self, flag: &str) -> bool { + let hash = get_hash(flag); unsafe { form_has_flag_by_hash(self.inner.reference, hash) } } } @@ -160,7 +159,7 @@ pub struct SpeciesInner { gender_rate: CachedValue, growth_rate: CachedValue, capture_rate: CachedValue, - forms: RwLock>>, + forms: RwLock>>, } #[derive(Clone)] @@ -205,8 +204,8 @@ impl Species { } #[cfg(not(feature = "mock_data"))] - pub fn get_form(&self, form_name: &[u8; N]) -> Option
{ - let hash = get_hash_const(form_name); + pub fn get_form(&self, form_name: &str) -> Option { + let hash = get_hash(form_name); unsafe { if let Some(v) = self.inner.forms.read().get(&hash) { v.clone() @@ -220,8 +219,8 @@ impl Species { } #[cfg(not(feature = "mock_data"))] - pub fn has_flag(&self, flag: &[u8; N]) -> bool { - let hash = get_hash_const(flag); + pub fn has_flag(&self, flag: &str) -> bool { + let hash = get_hash(flag); unsafe { species_has_flag_by_hash(self.inner.reference, hash) } } } diff --git a/pkmn_lib_interface/src/app_interface/string_key.rs b/pkmn_lib_interface/src/app_interface/string_key.rs index 9cb84a6..971729b 100644 --- a/pkmn_lib_interface/src/app_interface/string_key.rs +++ b/pkmn_lib_interface/src/app_interface/string_key.rs @@ -31,7 +31,7 @@ impl StringKey { } #[cfg(not(feature = "mock_data"))] - pub(super) fn ptr(&self) -> ExternRef { + pub(crate) fn ptr(&self) -> ExternRef { self.data.ptr } @@ -70,6 +70,12 @@ impl StringKey { } } +impl PartialEq for StringKey { + fn eq(&self, other: &Self) -> bool { + self.data.ptr == other.data.ptr + } +} + crate::handling::cacheable::cacheable!(StringKey); #[cfg(not(feature = "mock_data"))] @@ -134,23 +140,12 @@ const fn to_lower(c: u8) -> u8 { c } -pub const fn get_hash_const(s: &[u8; N]) -> u32 { - let mut crc: u32 = 0xffffffff; - - let mut i: usize = 0; - while i < N { - crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s[i]) as u32)) & 0xff) as usize]; - i += 1; - } - crc ^ 0xffffffff -} - -pub const fn get_hash(s: &[u8]) -> u32 { +pub const fn get_hash(s: &str) -> u32 { let mut crc: u32 = 0xffffffff; let mut i: usize = 0; while i < s.len() { - crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s[i]) as u32)) & 0xff) as usize]; + crc = (crc >> 8) ^ CRC_TABLE[((crc ^ (to_lower(s.as_bytes()[i]) as u32)) & 0xff) as usize]; i += 1; } crc ^ 0xffffffff diff --git a/pkmn_lib_interface/src/handling/cacheable.rs b/pkmn_lib_interface/src/handling/cacheable.rs index 3ac0b23..5811e6b 100644 --- a/pkmn_lib_interface/src/handling/cacheable.rs +++ b/pkmn_lib_interface/src/handling/cacheable.rs @@ -1,11 +1,9 @@ #[cfg(not(feature = "mock_data"))] use crate::ExternRef; -#[cfg(not(feature = "mock_data"))] -use alloc::collections::BTreeMap; pub trait Cacheable { #[cfg(not(feature = "mock_data"))] - fn get_cache<'a>() -> &'a mut BTreeMap, Self> + fn get_cache<'a>() -> &'a mut hashbrown::HashMap, Self> where Self: Sized; @@ -19,21 +17,28 @@ pub trait Cacheable { if let Some(v) = opt { return v.clone(); } - Self::get_cache().insert(ptr, ctor(ptr)); - return Self::get_cache().get(&ptr).unwrap().clone(); + let value = ctor(ptr); + Self::get_cache().insert(ptr, value.clone()); + value } } macro_rules! cacheable { ($type: ty) => { - paste::paste!{ + paste::paste! { #[cfg(not(feature = "mock_data"))] - static mut [<$type:upper _CACHE>]: alloc::collections::BTreeMap, $type> = - alloc::collections::BTreeMap::new(); + static mut [<$type:upper _CACHE>]: Option, $type>> = + None; impl crate::handling::Cacheable for $type { #[cfg(not(feature = "mock_data"))] - fn get_cache<'a>() -> &'a mut alloc::collections::BTreeMap, $type> { - unsafe { &mut [<$type:upper _CACHE>] } + fn get_cache<'a>() -> &'a mut hashbrown::HashMap, $type> { + unsafe { + if let None = [<$type:upper _CACHE>] { + [<$type:upper _CACHE>] = Some(hashbrown::HashMap::new()); + } + + [<$type:upper _CACHE>].as_mut().unwrap() + } } } } diff --git a/pkmn_lib_interface/src/handling/capabilities.rs b/pkmn_lib_interface/src/handling/capabilities.rs index 7574fc5..4b6840f 100644 --- a/pkmn_lib_interface/src/handling/capabilities.rs +++ b/pkmn_lib_interface/src/handling/capabilities.rs @@ -1,59 +1,62 @@ #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ScriptCapabilities { + None = 0, Initialize = 1, OnStack, OnRemove, OnBeforeTurn, - ChangeAttack, - ModifyNumberOfHits, - PreventAttack, - FailAttack, - StopBeforeAttack, - OnBeforeAttack, - FailIncomingAttack, + ChangeMove, + ChangeNumberOfHits, + PreventMove, + FailMove, + StopBeforeMove, + OnBeforeMove, + FailIncomingMove, IsInvulnerable, - OnAttackMiss, - ChangeAttackType, + OnMoveMiss, + ChangeMoveType, ChangeEffectiveness, BlockCritical, OnIncomingHit, OnFaintingOpponent, PreventStatBoostChange, - ModifyStatBoostChange, + ChangeStatBoostChange, PreventSecondaryEffects, OnSecondaryEffect, OnAfterHits, PreventSelfSwitch, - ModifyEffectChance, - ModifyIncomingEffectChance, - OverrideBasePower, + ChangeEffectChance, + ChangeIncomingEffectChance, + ChangeBasePower, ChangeDamageStatsUser, BypassDefensiveStat, BypassOffensiveStat, - ModifyStatModifier, - ModifyDamageModifier, - OverrideDamage, - OverrideIncomingDamage, + ChangeStatModifier, + ChangeDamageModifier, + ChangeDamage, + ChangeIncomingDamage, ChangeSpeed, ChangePriority, OnFail, OnOpponentFail, - PreventRunAway, + PreventSelfRunAway, PreventOpponentRunAway, PreventOpponentSwitch, OnEndTurn, OnDamage, OnFaint, OnAfterHeldItemConsume, - PreventIncomingCritical, - ModifyCriticalStage, - OverrideCriticalModifier, - OverrideSTABModifier, - ModifyExperienceGain, + BlockIncomingCritical, + ChangeAccuracy, + ChangeCriticalStage, + ChangeCriticalModifier, + ChangeSTABModifier, + ChangeExperienceGain, DoesShareExperience, BlockWeather, OnSwitchIn, - ModifyOffensiveStatValue, - ModifyDefensiveStatValue, + ChangeOffensiveStatValue, + ChangeDefensiveStatValue, + ChangeCaptureRate, } diff --git a/pkmn_lib_interface/src/handling/extern_ref.rs b/pkmn_lib_interface/src/handling/extern_ref.rs index 43d5aae..fd3de81 100644 --- a/pkmn_lib_interface/src/handling/extern_ref.rs +++ b/pkmn_lib_interface/src/handling/extern_ref.rs @@ -1,5 +1,6 @@ use crate::ImmutableList; use core::cmp::Ordering; +use core::hash::{Hash, Hasher}; use core::intrinsics::transmute; use core::marker::PhantomData; @@ -28,6 +29,16 @@ impl ExternRef { T::instantiate_from_extern_value(*self) } + #[inline] + pub fn not_null(&self) -> T + where + T: ExternalReferenceType, + T: Sized, + T:, + { + T::instantiate_from_extern_value(*self).unwrap() + } + #[inline] pub(crate) fn cast(&self) -> ExternRef { ExternRef:: { @@ -76,6 +87,12 @@ impl Ord for ExternRef { } } +impl Hash for ExternRef { + fn hash(&self, state: &mut H) { + self.p.hash(state) + } +} + #[repr(C)] pub struct VecExternRef { v: u64, diff --git a/pkmn_lib_interface/src/handling/script.rs b/pkmn_lib_interface/src/handling/script.rs index ba4319b..e37c4ee 100644 --- a/pkmn_lib_interface/src/handling/script.rs +++ b/pkmn_lib_interface/src/handling/script.rs @@ -1,7 +1,9 @@ use crate::app_interface::list::ImmutableList; -use crate::app_interface::{DynamicLibrary, EffectParameter}; +use crate::app_interface::{ + Battle, DamageSource, DynamicLibrary, EffectParameter, ExecutingMove, Item, Pokemon, Statistic, +}; use crate::handling::ScriptCapabilities; -use crate::{ExternRef, TurnChoice}; +use crate::{ExternRef, StringKey, TurnChoice, TypeIdentifier}; use core::ffi::c_void; use core::fmt::Debug; @@ -9,17 +11,338 @@ pub trait Script { fn new() -> Self where Self: Sized; - fn destroy(&self); - fn get_name(&self) -> &str; + fn get_name() -> &'static str + where + Self: Sized; fn get_capabilities(&self) -> &[ScriptCapabilities]; + /// This function is ran when a volatile effect is added while that volatile effect already is + /// in place. Instead of adding the volatile effect twice, it will execute this function instead. + fn stack(&self) {} + /// This function is ran when this script stops being in effect, and is removed from its owner. + fn on_remove(&self) {} + + /// This function is ran when this script starts being in effect. fn on_initialize( &self, _library: &DynamicLibrary, _parameters: Option>, ) { } + + /// This function is ran just before the start of the turn. Everyone has made its choices here, + /// and the turn is about to start. This is a great place to initialize data if you need to know + /// something has happened during a turn. fn on_before_turn(&self, _choice: TurnChoice) {} + + /// This function allows you to modify the effective speed of the Pokemon. This is ran before + /// turn ordering, so overriding here will allow you to put certain Pokemon before others. + fn change_speed(&self, _choice: TurnChoice, _speed: &mut u32) {} + /// This function allows you to modify the effective priority of the Pokemon. This is ran before + /// turn ordering, so overriding here will allow you to put certain Pokemon before others. Note + /// that this is only relevant on move choices, as other turn choice types do not have a priority. + fn change_priority(&self, _choice: TurnChoice, _priority: &mut i8) {} + + /// This function allows you to change the move that is used during execution. This is useful for + /// moves such as metronome, where the move chosen actually differs from the move used. + fn change_move(&self, _choice: TurnChoice, _move_name: &mut StringKey) {} + /// This function allows you to change a move into a multi-hit move. The number of hits set here + /// gets used as the number of hits. If set to 0, this will behave as if the move missed on its + /// first hit. + fn change_number_of_hits(&self, _choice: TurnChoice, _number_of_hits: &mut u8) {} + + /// This function allows you to prevent a move from running. If this gets set to true, the move + /// ends execution here. No PP will be decreased in this case. + fn prevent_move(&self, _move: ExecutingMove, _prevent: &mut bool) {} + /// This function makes the move fail. If the fail field gets set to true, the move ends execution, + /// and fail events get triggered. + fn fail_move(&self, _move: ExecutingMove, _fail: &mut bool) {} + /// Similar to [`Self::prevent_move`]. This function will also stop execution, but PP will be + /// decreased. + fn stop_before_move(&self, _move: ExecutingMove, _stop: &mut bool) {} + /// This function runs just before the move starts its execution. + fn on_before_move(&self, _move: ExecutingMove) {} + /// This function allows a script to prevent a move that is targeted at its owner. If set to true + /// the move fails, and fail events get triggered. + fn fail_incoming_move(&self, _move: ExecutingMove, _target: Pokemon, _fail: &mut bool) {} + /// This function allows a script to make its owner invulnerable to an incoming move. + fn is_invulnerable(&self, _move: ExecutingMove, _target: Pokemon, _invulnerable: &mut bool) {} + /// This function occurs when a move gets missed. This runs on the scripts belonging to the executing + /// move, which include the scripts that are attached to the owner of the script. + fn on_move_miss(&self, _move: ExecutingMove, _target: Pokemon) {} + /// This function allows the script to change the actual type that is used for the move on a target. + fn change_move_type( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _move_type: &mut TypeIdentifier, + ) { + } + /// This function allows the script to change how effective a move is on a target. + fn change_effectiveness( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _effectiveness: &mut f32, + ) { + } + /// This function allows a script to block an outgoing move from being critical. + fn block_critical( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _block_critical: &mut bool, + ) { + } + /// This function allows a script to block an incoming move from being critical. + fn block_incoming_critical( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _block_critical: &mut bool, + ) { + } + /// This function allows a script to modify the accuracy of a move used. This value represents + /// the percentage accuracy, so anything above 100% will make it always hit. + fn change_accuracy( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _accuracy: &mut u8, + ) { + } + + /// This function allows a script to change the critical stage of the move used. + fn change_critical_stage( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _stage: &mut u8, + ) { + } + /// This function allows a script to change the damage modifier of a critical hit. This will only + /// run when a hit is critical. + fn change_critical_modifier( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _modifier: &mut f32, + ) { + } + /// This function allows a script to change the damage modifier of a Same Type Attack Bonus, which + /// occurs when the user has the move type as one of its own types. + fn change_stab_modifier( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _modifier: &mut f32, + ) { + } + + /// This function allows a script to change the effective base power of a move hit. + fn change_base_power( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _base_power: &mut u8, + ) { + } + /// This function allows a script to bypass defensive stat boosts for a move hit. + fn bypass_defensive_stat_boost( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _bypass: &mut bool, + ) { + } + /// This function allows a script to bypass offensive stat boosts for a move hit. + fn bypass_offensive_stat_boost( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _bypass: &mut bool, + ) { + } + /// This function allows a script to change the actual offensive stat values used when calculating damage + fn change_offensive_stat_value( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _amount: &mut u32, + ) { + } + /// This function allows a script to change the actual defensive stat values used when calculating damage. + fn change_defensive_stat_value( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _amount: &mut u32, + ) { + } + + /// This function allows a script to change the raw modifier we retrieved from the stats of the + /// defender and attacker. + fn change_damage_stat_modifier( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _modifier: &mut f32, + ) { + } + /// This function allows a script to apply a raw multiplier to the damage done by a move. + fn change_damage_modifier( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _modifier: &mut f32, + ) { + } + /// This function allows a script to modify the outgoing damage done by a move. + fn change_damage(&self, _move: ExecutingMove, _target: Pokemon, _hit: u8, _damage: &mut u32) {} + /// This function allows a script to modify the incoming damage done by a move. + fn change_incoming_damage( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _damage: &mut u32, + ) { + } + /// This function triggers when an incoming hit happens. This triggers after the damage is done, + /// but before the secondary effect of the move happens. + fn on_incoming_hit(&self, _move: ExecutingMove, _target: Pokemon, _hit: u8) {} + /// This function triggers when an opponent on the field faints. + fn on_opponent_faints(&self, _move: ExecutingMove, _target: Pokemon, _hit: u8) {} + /// This function allows a script attached to a Pokemon or its parents to prevent stat boost + /// changes on that Pokemon. + fn prevent_stat_boost_change( + &self, + _target: Pokemon, + _stat: Statistic, + _amount: i8, + _self_inflicted: bool, + _prevent: &mut bool, + ) { + } + /// This function allows a script attached to a Pokemon or its parents to modify the amount by + /// which the stat boost will change. If the stat boost is done by the user itself, self + /// inflicted will be true, otherwise it will be false. + fn change_stat_boost_change( + &self, + _target: Pokemon, + _stat: Statistic, + _self_inflicted: bool, + _amount: &mut i8, + ) { + } + /// This function allows a script attached to a Pokemon or its parents to prevent an incoming + /// secondary effect. This means the move will still hit and do damage, but not trigger its + /// secondary effect. Note that this function is not called for status moves. + fn prevent_secondary_effect( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _prevent: &mut bool, + ) { + } + /// This function allows a script attached to a move or its parents to change the chance the + /// secondary effect of a move will trigger. The chance is depicted in percentage here, so + /// changing this to above or equal to 100 will make it always hit, while setting it to equal or + /// below 0 will make it never hit. + fn change_effect_chance( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _chance: &mut f32, + ) { + } + /// This function allows a script attached to a Pokemon or its parents to change the chance the + /// secondary effect of an incoming move will trigger. The chance is depicted in percentage here, + /// so changing this to above or equal to 100 will make it always hit, while setting it to equal + /// or below 0 will make it never hit. + fn change_incoming_effect_chance( + &self, + _move: ExecutingMove, + _target: Pokemon, + _hit: u8, + _chance: &mut f32, + ) { + } + /// This function triggers when the move uses its secondary effect. Moves should implement their + /// secondary effects here. Status moves should implement their actual functionality in this + /// function as well, as status moves effects are defined as secondary effects for simplicity. + fn on_secondary_effect(&self, _move: ExecutingMove, _target: Pokemon, _hit: u8) {} + /// This function triggers on a move or its parents when all hits on a target are finished. + fn on_after_hits(&self, _move: ExecutingMove, _target: Pokemon) {} + /// This function prevents the Pokemon it is attached to from being able to switch out. + fn prevent_self_switch(&self, _choice: TurnChoice, _prevent: &mut bool) {} + /// This function allows the prevention of switching for any opponent. + fn prevent_opponent_switch(&self, _choice: TurnChoice, _prevent: &mut bool) {} + /// This function is called on a move and its parents when the move fails. + fn on_fail(&self, _target: Pokemon) {} + /// This function is called on a script when an opponent fails. + fn on_opponent_fail(&self, _target: Pokemon) {} + /// This function allows preventing the running away of the Pokemon its attached to + fn prevent_self_run_away(&self, _choice: TurnChoice, _prevent: &mut bool) {} + /// This function prevents a Pokemon on another side than where its attached to from running away. + fn prevent_opponent_run_away(&self, _choice: TurnChoice, _prevent: &mut bool) {} + /// This function id triggered on all scripts active in the battle after all choices have finished + /// running. Note that choices are not active anymore here, so their scripts do not call this + /// function. + fn on_end_turn(&self) {} + /// This function is triggered on a Pokemon and its parents when the given Pokemon takes damage. + fn on_damage( + &self, + _pokemon: Pokemon, + _source: DamageSource, + _old_health: u32, + _new_health: u32, + ) { + } + /// This function is triggered on a Pokemon and its parents when the given Pokemon faints. + fn on_faint(&self, _pokemon: Pokemon, _source: DamageSource) {} + /// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into + /// the battlefield. + fn on_switch_in(&self, _pokemon: Pokemon) {} + /// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the + /// held item it had. + fn on_after_held_item_consume(&self, _pokemon: Pokemon, _item: &Item) {} + /// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience, + /// and allows for changing this amount of experience. + fn change_experience_gained( + &self, + _fainted_mon: Pokemon, + _winning_mon: Pokemon, + _amount: &mut u32, + ) { + } + /// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience, + /// and allows for making the experience be shared across multiple Pokemon. + fn share_experience(&self, _fainted_mon: Pokemon, _winning_mon: Pokemon, _shares: &mut bool) {} + /// This function is triggered on a battle and its parents when something attempts to change the + /// weather, and allows for blocking the weather change. + fn block_weather(&self, _battle: Battle, _blocked: &mut bool) {} + /// This function is called when a Pokeball is thrown at a Pokemon, and allows modifying the catch + /// rate of this attempt. Pokeball modifier effects should be implemented here, as well as for + /// example status effects that change capture rates. + fn change_capture_rate_bonus(&self, _target: Pokemon, _pokeball: Item, _modifier: &mut u8) {} } impl Debug for dyn Script { diff --git a/pkmn_lib_interface/src/handling/temporary.rs b/pkmn_lib_interface/src/handling/temporary.rs index 71e6f31..f9662fc 100644 --- a/pkmn_lib_interface/src/handling/temporary.rs +++ b/pkmn_lib_interface/src/handling/temporary.rs @@ -1,80 +1,63 @@ -use crate::{ExternRef, ExternalReferenceType}; -use alloc::boxed::Box; -use core::cell::Cell; -use core::mem::forget; - -struct TemporaryData { - use_count: Cell, - is_deleted: bool, - value: T, -} +use alloc::rc::Rc; +use core::sync::atomic::{AtomicBool, Ordering}; #[cfg(not(feature = "mock_data"))] pub struct Temporary { - value: *mut TemporaryData, + is_deleted: Rc, + value: Rc, } #[cfg(not(feature = "mock_data"))] -static mut TEMPORARIES: alloc::collections::BTreeMap = - alloc::collections::BTreeMap::new(); +static mut TEMPORARIES: Option>> = None; #[cfg(not(feature = "mock_data"))] -impl Temporary -where - T: ExternalReferenceType, - T: Clone, -{ +impl Temporary { #[inline] - pub fn from_reference(reference: ExternRef) -> Self { - let temporaries = unsafe { &mut TEMPORARIES }; - let existing = temporaries.get(&reference.get_internal_index()); + pub fn new(reference: u32, value: T) -> Self { unsafe { - if let Some(v) = existing { - let rc = (*v as *mut TemporaryData).as_mut().unwrap(); - if !rc.is_deleted { - *rc.use_count.get_mut() += 1; - return Self { - value: rc as *mut TemporaryData, - }; - } + if let None = TEMPORARIES { + TEMPORARIES = Some(hashbrown::HashMap::new()); } } + let temporaries = unsafe { &mut TEMPORARIES }.as_mut().unwrap(); + let existing = temporaries.get(&reference); + if let Some(v) = existing { + return Self { + is_deleted: v.clone(), + value: Rc::new(value), + }; + } - let value = reference.get_value().unwrap(); - let mut reference_counter = TemporaryData { - use_count: Cell::new(1), - is_deleted: false, - value, - }; - let ptr = &mut reference_counter as *mut TemporaryData; - forget(reference_counter); - temporaries.insert(reference.get_internal_index(), ptr as *const u8); - Self { value: ptr } + let value = Rc::new(value); + let is_deleted = Rc::new(AtomicBool::new(false)); + temporaries.insert(reference, is_deleted.clone()); + Self { is_deleted, value } } #[inline] - pub fn value(&self) -> T { - unsafe { self.value.as_ref().unwrap().value.clone() } + pub fn value(&self) -> Rc { + if self.is_deleted.load(Ordering::SeqCst) { + panic!("Accessed value after it had been deleted"); + } + self.value.clone() } #[inline] pub fn value_ref(&self) -> &T { - unsafe { &self.value.as_ref().unwrap().value } + if self.is_deleted.load(Ordering::SeqCst) { + panic!("Accessed value after it had been deleted"); + } + self.value.as_ref() } +} - pub(crate) fn mark_as_deleted(reference: ExternRef) { +#[cfg(not(feature = "mock_data"))] +impl Temporary { + pub(crate) fn mark_as_deleted(reference: u32) { let temporaries = unsafe { &mut TEMPORARIES }; - let existing = temporaries.get(&reference.get_internal_index()); - unsafe { - if let Some(v) = existing { - crate::utils::print_raw(b"Dropping temporary"); - let rc = (*v as *mut TemporaryData).as_mut().unwrap(); - rc.is_deleted = true; - if rc.use_count.get() == 0 { - drop(Box::from(*v as *mut TemporaryData)) - } - temporaries.remove(&reference.get_internal_index()); - } + let existing = temporaries.as_mut().unwrap().get_mut(&reference); + if let Some(v) = existing { + v.store(false, Ordering::SeqCst); } } } @@ -82,32 +65,28 @@ where #[cfg(not(feature = "mock_data"))] impl Clone for Temporary { fn clone(&self) -> Self { - *unsafe { self.value.as_mut() }.unwrap().use_count.get_mut() += 1; - Self { value: self.value } - } -} - -#[cfg(not(feature = "mock_data"))] -impl Drop for Temporary { - fn drop(&mut self) { - let data = unsafe { self.value.as_mut() }.unwrap(); - let v = data.use_count.get_mut(); - *v -= 1; - if *v == 0 && data.is_deleted { - drop(Box::from(self.value)) + Self { + is_deleted: self.is_deleted.clone(), + value: self.value.clone(), } } } -#[cfg(feature = "mock_data")] -pub struct Temporary { - value: T, +#[no_mangle] +#[cfg(not(feature = "mock_data"))] +unsafe extern "wasm" fn _mark_deleted(r: u32) { + Temporary::::mark_as_deleted(r) } #[cfg(feature = "mock_data")] -impl Temporary { +pub struct Temporary { + value: Rc, +} + +#[cfg(feature = "mock_data")] +impl Temporary { #[inline] - pub fn value(&self) -> T { + pub fn value(&self) -> Rc { self.value.clone() } @@ -116,3 +95,12 @@ impl Temporary { &self.value } } + +#[cfg(feature = "mock_data")] +impl Clone for Temporary { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + } + } +} diff --git a/pkmn_lib_interface/src/lib.rs b/pkmn_lib_interface/src/lib.rs index 2e75b7d..8ee60a4 100644 --- a/pkmn_lib_interface/src/lib.rs +++ b/pkmn_lib_interface/src/lib.rs @@ -12,6 +12,7 @@ #![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)] @@ -23,7 +24,12 @@ extern crate wee_alloc; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; use crate::app_interface::list::ImmutableList; -use crate::app_interface::{DynamicLibrary, EffectParameter, StringKey, TurnChoice}; +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"))] @@ -47,61 +53,565 @@ pub fn set_load_script_fn(f: LoadScriptFnType) { } } -#[no_mangle] -#[cfg(not(feature = "mock_data"))] -extern "wasm" fn load_script(category: ScriptCategory, name: ExternRef) -> u32 { +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 + )* + }; +} + +#[repr(C)] +pub struct ScriptPtr { + ptr: *const Box, +} + +impl ScriptPtr { + pub fn new(ptr: *const Box) -> Self { + Self { ptr } + } + + pub fn val(&self) -> &dyn Script { + unsafe { self.ptr.as_ref().unwrap().as_ref() } + } + + pub fn ptr(&self) -> *const Box { + self.ptr + } +} + +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 0; + return ScriptPtr::new(core::ptr::null()); } let b = Box::new(boxed_script.unwrap()); - Box::into_raw(b) as u32 + ScriptPtr::new(Box::into_raw(b)) } -#[no_mangle] -#[cfg(not(feature = "mock_data"))] -unsafe extern "wasm" fn destroy_script(script: *mut Box) { +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); - boxed_script.destroy(); + drop(boxed_script); } -#[no_mangle] -#[cfg(not(feature = "mock_data"))] -unsafe extern "wasm" fn get_script_capabilities( - script: *const Box, -) -> FFIArray { - let c = script.as_ref().unwrap().get_capabilities(); +fn get_script_capabilities(script: ScriptPtr) -> FFIArray { + let c = script.val().get_capabilities(); FFIArray::new(c) } -#[no_mangle] -#[cfg(not(feature = "mock_data"))] -unsafe extern "wasm" fn script_on_initialize( - script: *const Box, +fn script_stack(script: ScriptPtr) { + script.val().stack(); +} + +fn script_on_remove(script: ScriptPtr) { + script.val().on_remove(); +} + +fn script_on_initialize( + script: ScriptPtr, library: ExternRef, parameters: VecExternRef, ) { - let lib = DynamicLibrary::new(library); let parameters = ImmutableList::from_ref(parameters); - script - .as_ref() - .unwrap() - .as_ref() - .on_initialize(&lib, Some(parameters)); + script.val().on_initialize(&library.not_null(), Some(parameters)); } -#[no_mangle] -#[cfg(not(feature = "mock_data"))] -unsafe extern "wasm" fn script_on_before_turn( - script: *const Box, +fn script_on_before_turn( + script: ScriptPtr, choice: ExternRef, ) { - script - .as_ref() - .unwrap() - .as_ref() - .on_before_turn(choice.get_value().unwrap()) + script.val().on_before_turn(choice.not_null()) +} + +fn script_change_speed( + script:ScriptPtr, + choice: ExternRef, + speed: *mut u32, +) { + script.val().change_speed(choice.not_null(), speed.as_mut().unwrap()) +} + +fn script_change_priority( + script: ScriptPtr, + choice: ExternRef, + priority: *mut i8, +) { + script.val().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().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().change_number_of_hits(choice.not_null(), out.as_mut().unwrap()); +} + +fn script_prevent_move( + script: ScriptPtr, + mv: ExternRef, + out: *mut bool, +) { + script.val().prevent_move(mv.not_null(), out.as_mut().unwrap()); +} + +fn script_fail_move( + script: ScriptPtr, + mv: ExternRef, + out: *mut bool, +) { + script.val().fail_move(mv.not_null(), out.as_mut().unwrap()); +} + +fn script_stop_before_move( + script: ScriptPtr, + mv: ExternRef, + out: *mut bool, +) { + script.val().stop_before_move(mv.not_null(), out.as_mut().unwrap()); +} + +fn script_on_before_move( + script: ScriptPtr, + mv: ExternRef, +) { + script.val().on_before_move(mv.not_null()); +} + +fn script_fail_incoming_move( + script: ScriptPtr, + mv: ExternRef, + target: ExternRef, + out: *mut bool, +) { + script.val().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().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().on_secondary_effect(mv.not_null(), target.not_null(), hit); +} + +fn script_on_after_hits( + script: ScriptPtr, + mv: ExternRef, + target: ExternRef, +) { + script.val().on_after_hits(mv.not_null(), target.not_null()); +} + +fn script_prevent_self_switch( + script: ScriptPtr, + choice: ExternRef, + out: *mut bool, +) { + script.val().prevent_self_switch(choice.not_null(), out.as_mut().unwrap()); +} + +fn script_prevent_opponent_switch( + script: ScriptPtr, + choice: ExternRef, + out: *mut bool, +) { + script.val().prevent_opponent_switch(choice.not_null(), out.as_mut().unwrap()); +} + +fn script_on_fail( + script: ScriptPtr, + pokemon: ExternRef, +) { + script.val().on_fail(pokemon.not_null()); +} + +fn script_on_opponent_fail( + script: ScriptPtr, + pokemon: ExternRef, +) { + script.val().on_opponent_fail(pokemon.not_null()); +} + +fn script_prevent_self_run_away( + script: ScriptPtr, + choice: ExternRef, + out: *mut bool, +) { + script.val().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().prevent_opponent_run_away(choice.not_null(), out.as_mut().unwrap()); +} + +fn script_on_end_turn( + script: ScriptPtr, +) { + script.val().on_end_turn(); +} + +fn script_on_damage( + script: ScriptPtr, + pokemon: ExternRef, + source: DamageSource, + old_health: u32, + new_health: u32, +) { + script.val().on_damage(pokemon.not_null(), source, old_health, new_health); +} + +fn script_on_faint( + script: ScriptPtr, + pokemon: ExternRef, + source: DamageSource, +) { + script.val().on_faint(pokemon.not_null(), source); +} + +fn script_on_switch_in( + script: ScriptPtr, + pokemon: ExternRef, +) { + script.val().on_switch_in(pokemon.not_null()); +} + +fn script_on_after_held_item_consume( + script: ScriptPtr, + pokemon: ExternRef, + item: ExternRef +) { + script.val().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().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().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().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().change_capture_rate_bonus(target.not_null(), pokeball.not_null(), out.as_mut().unwrap()); +} + + + + + + + + + + + + + + + + + } diff --git a/pkmn_lib_interface/src/utils.rs b/pkmn_lib_interface/src/utils.rs index 66521b1..44971bd 100644 --- a/pkmn_lib_interface/src/utils.rs +++ b/pkmn_lib_interface/src/utils.rs @@ -2,7 +2,9 @@ use alloc::alloc::alloc; use core::alloc::Layout; #[cfg(not(feature = "mock_data"))] use core::panic::PanicInfo; -use cstr_core::{c_char, CStr, CString}; +use cstr_core::c_char; +#[cfg(feature = "mock_data")] +use cstr_core::{CStr, CString}; #[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]