Implements first few actual move effects.

This commit is contained in:
Deukhoofd 2022-08-28 15:50:12 +02:00
parent 98130706fb
commit 05430c5e84
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
38 changed files with 1539 additions and 355 deletions

View File

@ -1,5 +1,6 @@
[workspace] [workspace]
edition = "2021" edition = "2021"
resolver = "2"
members = [ members = [
"pkmn_lib_interface", "pkmn_lib_interface",

View File

@ -12,7 +12,8 @@ use pkmn_lib_interface::set_load_script_fn;
#[macro_use] #[macro_use]
pub mod registered_scripts; pub mod registered_scripts;
pub mod test_script; pub mod moves;
pub mod util_scripts;
#[no_mangle] #[no_mangle]
#[cfg(not(test))] #[cfg(not(test))]

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}

View File

@ -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()
}
}
}

View File

@ -0,0 +1,4 @@
pub mod acrobatics;
pub mod acupressure;
pub mod after_you;
pub mod multi_hit_move;

View File

@ -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,
}
}
}

View File

@ -1,19 +1,19 @@
use crate::test_script::TestScript; use crate::moves::*;
use alloc::boxed::Box; 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}; use pkmn_lib_interface::handling::{Script, ScriptCategory};
macro_rules! resolve_match { macro_rules! resolve_match {
( (
$mid:expr, $mid:expr,
$( $(
$key:expr => $script:ident, $script:ty,
)* )*
) => ( ) => (
match $mid { match $mid {
$( $(
const { get_hash_const($key) } => { const { get_hash(<$script>::get_const_name()) } => {
return Some(Box::new($script {})) return Some(Box::new(<$script>::new()))
} }
)* )*
_ => {} _ => {}
@ -26,13 +26,18 @@ pub fn get_script(category: ScriptCategory, name: &StringKey) -> Option<Box<dyn
ScriptCategory::Move => { ScriptCategory::Move => {
resolve_match!( resolve_match!(
name.hash(), name.hash(),
b"test" => TestScript, acrobatics::Acrobatics,
acupressure::Acupressure,
after_you::AfterYou,
multi_hit_move::MultiHitMove,
); );
} }
ScriptCategory::Ability => {} ScriptCategory::Ability => {}
ScriptCategory::Status => {} ScriptCategory::Status => {}
ScriptCategory::Pokemon => {} ScriptCategory::Pokemon => {}
ScriptCategory::Battle => {} ScriptCategory::Battle => {
resolve_match!(name.hash(), crate::util_scripts::ForceEffectTriggerScript,)
}
ScriptCategory::Side => {} ScriptCategory::Side => {}
ScriptCategory::ItemBattleTrigger => {} ScriptCategory::ItemBattleTrigger => {}
} }

View File

@ -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<ImmutableList<EffectParameter>>,
) {
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())),
);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
mod force_effect_trigger;
pub use force_effect_trigger::*;

View File

@ -10,10 +10,10 @@ mock_data = []
[dependencies] [dependencies]
wee_alloc = "0.4.5" wee_alloc = "0.4.5"
cstr_core = { version = "0.2.6", features = ["nightly"]} 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 } enumflags2 = { version = "0.7.5", default-features = false }
spin = { version = "0.9.4", default-features = false, features = ["rwlock"] } spin = { version = "0.9.4", default-features = false, features = ["rwlock"] }
paste = { version = "1.0.7" } paste = { version = "1.0.7" }
hashbrown = { version = "0.12.3" }
[dev-dependencies] [dev-dependencies]

View File

@ -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::cached_value::CachedValue;
use crate::handling::Cacheable; use crate::handling::Cacheable;
use crate::{ use crate::{
@ -13,6 +13,7 @@ struct BattleInner {
parties: CachedValue<ImmutableList<BattleParty>>, parties: CachedValue<ImmutableList<BattleParty>>,
sides: CachedValue<ImmutableList<BattleSide>>, sides: CachedValue<ImmutableList<BattleSide>>,
random: CachedValue<BattleRandom>, random: CachedValue<BattleRandom>,
choice_queue: CachedValue<ChoiceQueue>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -30,6 +31,9 @@ impl Battle {
parties: cached_value!({ battle_get_parties(reference).get_immutable_list() }), parties: cached_value!({ battle_get_parties(reference).get_immutable_list() }),
sides: cached_value!({ battle_get_sides(reference).get_immutable_list() }), sides: cached_value!({ battle_get_sides(reference).get_immutable_list() }),
random: cached_value!({ battle_get_random(reference).get_value().unwrap() }), 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 library(&self) -> DynamicLibrary;
pub fn parties(&self) -> ImmutableList<BattleParty>; pub fn parties(&self) -> ImmutableList<BattleParty>;
pub fn sides(&self) -> ImmutableList<BattleSide>; pub fn sides(&self) -> ImmutableList<BattleSide>;
pub fn random(&self) -> BattleRandom;
pub fn choice_queue(&self) -> ChoiceQueue;
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
@ -72,5 +78,6 @@ extern "wasm" {
fn battle_get_parties(r: ExternRef<Battle>) -> VecExternRef<BattleParty>; fn battle_get_parties(r: ExternRef<Battle>) -> VecExternRef<BattleParty>;
fn battle_get_sides(r: ExternRef<Battle>) -> VecExternRef<BattleSide>; fn battle_get_sides(r: ExternRef<Battle>) -> VecExternRef<BattleSide>;
fn battle_get_random(r: ExternRef<Battle>) -> ExternRef<BattleRandom>; fn battle_get_random(r: ExternRef<Battle>) -> ExternRef<BattleRandom>;
fn battle_get_choice_queue(r: ExternRef<Battle>) -> ExternRef<ChoiceQueue>;
fn battle_get_pokemon(r: ExternRef<Battle>, side: u8, index: u8) -> ExternRef<Pokemon>; fn battle_get_pokemon(r: ExternRef<Battle>, side: u8, index: u8) -> ExternRef<Pokemon>;
} }

View File

@ -39,7 +39,6 @@ impl ExternalReferenceType for BattleParty {
} }
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
extern "wasm" { extern "wasm" {
fn battle_party_get_party(r: ExternRef<BattleParty>) -> ExternRef<Party>; fn battle_party_get_party(r: ExternRef<BattleParty>) -> ExternRef<Party>;

View File

@ -0,0 +1,31 @@
use crate::{ExternRef, ExternalReferenceType, Pokemon};
#[derive(Clone)]
pub struct ChoiceQueue {
reference: ExternRef<ChoiceQueue>,
}
impl ChoiceQueue {
#[cfg(not(feature = "mock_data"))]
pub fn new(reference: ExternRef<ChoiceQueue>) -> 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 {
Self::new(reference)
}
}
extern "wasm" {
fn choice_queue_move_pokemon_choice_next(
r: ExternRef<ChoiceQueue>,
pokemon: ExternRef<Pokemon>,
) -> bool;
}

View File

@ -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<ExecutingMove>,
number_of_hits: CachedValue<u8>,
user: CachedValue<Pokemon>,
chosen_move: CachedValue<LearnedMove>,
use_move: CachedValue<MoveData>,
}
#[derive(Clone)]
pub struct ExecutingMove {
inner: Temporary<ExecutingMoveInner>,
}
impl ExecutingMove {
pub(crate) fn new(reference: ExternRef<ExecutingMove>) -> 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<HitData>,
}
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 {
Self::new(reference)
}
}
impl ExternalReferenceType for HitData {
fn from_extern_value(reference: ExternRef<Self>) -> Self {
Self { reference }
}
}
extern "wasm" {
fn executing_move_get_number_of_targets(r: ExternRef<ExecutingMove>) -> usize;
fn executing_move_get_number_of_hits(r: ExternRef<ExecutingMove>) -> u8;
fn executing_move_get_user(r: ExternRef<ExecutingMove>) -> ExternRef<Pokemon>;
fn executing_move_get_chosen_move(r: ExternRef<ExecutingMove>) -> ExternRef<LearnedMove>;
fn executing_move_get_use_move(r: ExternRef<ExecutingMove>) -> ExternRef<MoveData>;
#[allow(improper_ctypes)]
fn executing_move_get_script(r: ExternRef<ExecutingMove>) -> *const dyn Script;
fn executing_move_is_pokemon_target(
r: ExternRef<ExecutingMove>,
pokemon: ExternRef<Pokemon>,
) -> bool;
fn executing_move_get_hit_data(
r: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
) -> ExternRef<HitData>;
fn hit_data_is_critical(r: ExternRef<HitData>) -> bool;
fn hit_data_get_base_power(r: ExternRef<HitData>) -> u8;
fn hit_data_get_effectiveness(r: ExternRef<HitData>) -> f32;
fn hit_data_get_damage(r: ExternRef<HitData>) -> u32;
fn hit_data_get_move_type(r: ExternRef<HitData>) -> u8;
fn hit_data_has_failed(r: ExternRef<HitData>) -> bool;
fn hit_data_set_critical(r: ExternRef<HitData>, critical: bool);
fn hit_data_set_base_power(r: ExternRef<HitData>, power: u8);
fn hit_data_set_effectiveness(r: ExternRef<HitData>, effectiveness: f32);
fn hit_data_set_damage(r: ExternRef<HitData>, damage: u32);
fn hit_data_set_move_type(r: ExternRef<HitData>, move_type: u8);
fn hit_data_fail(r: ExternRef<HitData>);
}

View File

@ -2,7 +2,9 @@ mod battle;
mod battle_party; mod battle_party;
mod battle_random; mod battle_random;
mod battle_side; mod battle_side;
mod choice_queue;
mod dynamic_library; mod dynamic_library;
mod executing_move;
mod learned_move; mod learned_move;
mod party; mod party;
mod pokemon; mod pokemon;
@ -13,7 +15,9 @@ pub use battle::*;
pub use battle_party::*; pub use battle_party::*;
pub use battle_random::*; pub use battle_random::*;
pub use battle_side::*; pub use battle_side::*;
pub use choice_queue::*;
pub use dynamic_library::DynamicLibrary; pub use dynamic_library::DynamicLibrary;
pub use executing_move::*;
pub use learned_move::*; pub use learned_move::*;
pub use party::*; pub use party::*;
pub use pokemon::*; pub use pokemon::*;

View File

@ -7,7 +7,7 @@ use crate::handling::cached_value::CachedValue;
use crate::handling::Cacheable; use crate::handling::Cacheable;
use crate::{ use crate::{
cached_value, cached_value_getters, wasm_optional_reference_getters, wasm_reference_getters, 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::boxed::Box;
use alloc::rc::Rc; use alloc::rc::Rc;
@ -61,6 +61,10 @@ impl Pokemon {
}) })
} }
pub(crate) fn reference(&self) -> ExternRef<Self> {
self.inner.reference
}
cached_value_getters! { cached_value_getters! {
pub fn library(&self) -> DynamicLibrary; pub fn library(&self) -> DynamicLibrary;
pub fn flat_stats(&self) -> StatisticSet<u32>; pub fn flat_stats(&self) -> StatisticSet<u32>;
@ -101,8 +105,8 @@ impl Pokemon {
unsafe { pokemon_get_type(self.inner.reference, index) } unsafe { pokemon_get_type(self.inner.reference, index) }
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn has_type(&self, type_identifier: u8) -> bool { pub fn has_type(&self, type_identifier: TypeIdentifier) -> bool {
unsafe { pokemon_has_type(self.inner.reference, type_identifier) } unsafe { pokemon_has_type(self.inner.reference, type_identifier.into()) }
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn has_type_by_name(&self, type_name: &str) -> bool { pub fn has_type_by_name(&self, type_name: &str) -> bool {
@ -206,6 +210,12 @@ wasm_value_getters! {
pub fn is_usable(&self) -> bool; 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. /// A source of damage. This should be as unique as possible.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[repr(u8)] #[repr(u8)]
@ -246,6 +256,7 @@ extern "wasm" {
diff_amount: i8, diff_amount: i8,
self_inflicted: bool, self_inflicted: bool,
) -> bool; ) -> bool;
#[allow(improper_ctypes)]
fn pokemon_get_ability_script(r: ExternRef<Pokemon>) -> *const Box<dyn Script>; fn pokemon_get_ability_script(r: ExternRef<Pokemon>) -> *const Box<dyn Script>;
fn pokemon_change_species( fn pokemon_change_species(
r: ExternRef<Pokemon>, r: ExternRef<Pokemon>,

View File

@ -3,10 +3,9 @@ use crate::handling::cached_value::CachedValue;
use crate::handling::temporary::Temporary; use crate::handling::temporary::Temporary;
use crate::ExternRef; use crate::ExternRef;
#[cfg(not(feature = "mock_data"))] #[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"))] #[cfg(not(feature = "mock_data"))]
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::rc::Rc;
struct BaseTurnChoiceData { struct BaseTurnChoiceData {
reference: ExternRef<TurnChoice>, reference: ExternRef<TurnChoice>,
@ -19,12 +18,10 @@ struct MoveTurnChoiceDataInner {
target_side: CachedValue<u8>, target_side: CachedValue<u8>,
target_index: CachedValue<u8>, target_index: CachedValue<u8>,
} }
#[derive(Clone)] #[derive(Clone)]
struct MoveTurnChoiceDataTemporary {
inner: Rc<MoveTurnChoiceDataInner>,
}
pub struct MoveTurnChoiceData { pub struct MoveTurnChoiceData {
temp: Temporary<MoveTurnChoiceDataTemporary>, inner: Temporary<MoveTurnChoiceDataInner>,
} }
pub enum TurnChoice { pub enum TurnChoice {
@ -38,7 +35,7 @@ pub enum TurnChoice {
impl TurnChoice { impl TurnChoice {
fn base(&self) -> &BaseTurnChoiceData { fn base(&self) -> &BaseTurnChoiceData {
match self { match self {
TurnChoice::Move(d) => &d.temp.value_ref().inner.base, TurnChoice::Move(d) => &d.inner.value_ref().base,
TurnChoice::Item() => unimplemented!(), TurnChoice::Item() => unimplemented!(),
TurnChoice::Switch() => unimplemented!(), TurnChoice::Switch() => unimplemented!(),
TurnChoice::Flee => unimplemented!(), TurnChoice::Flee => unimplemented!(),
@ -68,22 +65,22 @@ impl TurnChoice {
impl MoveTurnChoiceData { impl MoveTurnChoiceData {
pub fn used_move(&self) -> LearnedMove { 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 { 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 { pub fn target_index(&self) -> u8 {
self.temp.value().inner.target_index.value() self.inner.value().target_index.value()
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn priority(&self) -> i8 { 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"))] #[cfg(not(feature = "mock_data"))]
pub fn move_script(&self) -> Option<&Box<dyn Script>> { pub fn move_script(&self) -> Option<&Box<dyn Script>> {
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) }; let kind = unsafe { turn_choice_get_kind(reference) };
match kind { match kind {
0 => TurnChoice::Move(MoveTurnChoiceData { 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"), _ => panic!("Unknown turn choice type"),
} }
@ -101,24 +101,22 @@ impl ExternalReferenceType for TurnChoice {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
impl ExternalReferenceType for MoveTurnChoiceDataTemporary { impl MoveTurnChoiceDataInner {
fn from_extern_value(reference: ExternRef<Self>) -> Self { fn from_reference(reference: ExternRef<MoveTurnChoiceData>) -> Self {
Self { Self {
inner: Rc::new(MoveTurnChoiceDataInner { base: BaseTurnChoiceData {
base: BaseTurnChoiceData { reference: reference.cast(),
reference: reference.cast(), user: cached_value!({
user: cached_value!({ turn_choice_get_user(reference.cast()).get_value().unwrap()
turn_choice_get_user(reference.cast()).get_value().unwrap()
}),
},
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()) }), 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<MoveTurnChoiceData>) -> u8; fn turn_choice_move_target_side(r: ExternRef<MoveTurnChoiceData>) -> u8;
fn turn_choice_move_target_index(r: ExternRef<MoveTurnChoiceData>) -> u8; fn turn_choice_move_target_index(r: ExternRef<MoveTurnChoiceData>) -> u8;
fn turn_choice_move_priority(r: ExternRef<MoveTurnChoiceData>) -> i8; fn turn_choice_move_priority(r: ExternRef<MoveTurnChoiceData>) -> i8;
#[allow(improper_ctypes)]
fn turn_choice_move_script(r: ExternRef<MoveTurnChoiceData>) -> *const Box<dyn Script>; fn turn_choice_move_script(r: ExternRef<MoveTurnChoiceData>) -> *const Box<dyn Script>;
} }
#[no_mangle]
#[cfg(not(feature = "mock_data"))]
unsafe extern "wasm" fn turn_choice_mark_deleted(r: ExternRef<TurnChoice>, kind: u8) {
match kind {
0 => Temporary::<MoveTurnChoiceDataTemporary>::mark_as_deleted(r.cast()),
_ => panic!("Unknown turn choice type"),
}
}

View File

@ -1,6 +1,6 @@
use crate::{ExternalReferenceType, VecExternRef}; use crate::{ExternalReferenceType, VecExternRef};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::collections::BTreeMap; #[cfg(feature = "mock_data")]
use alloc::rc::Rc; use alloc::rc::Rc;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -44,13 +44,22 @@ where
pub(crate) fn from_ref(extern_ref: VecExternRef<T>) -> Self { pub(crate) fn from_ref(extern_ref: VecExternRef<T>) -> Self {
unsafe { 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 { if let Some(v) = existing {
let inner = *v as *const ImmutableListInner<T>; let inner = *v as *const ImmutableListInner<T>;
ImmutableList { inner } ImmutableList { inner }
} else { } else {
let v = Self::new(extern_ref); 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 v
} }
} }
@ -102,4 +111,4 @@ where
} }
} }
static mut CACHE: BTreeMap<u32, *const u8> = BTreeMap::new(); static mut CACHE: Option<hashbrown::HashMap<u32, *const u8>> = None;

View File

@ -5,5 +5,4 @@ pub mod string_key;
pub use dynamic_data::*; pub use dynamic_data::*;
pub use static_data::*; pub use static_data::*;
pub use string_key::get_hash_const; pub use string_key::*;
pub use string_key::StringKey;

View File

@ -1,12 +1,11 @@
use crate::app_interface::{DataLibrary, Item}; use crate::app_interface::{DataLibrary, Item};
use crate::{ExternRef, ExternalReferenceType, StringKey}; use crate::{ExternRef, ExternalReferenceType, StringKey};
use alloc::collections::BTreeMap;
use alloc::rc::Rc; use alloc::rc::Rc;
use spin::rwlock::RwLock; use spin::rwlock::RwLock;
struct ItemLibraryInner { struct ItemLibraryInner {
ptr: ExternRef<ItemLibrary>, ptr: ExternRef<ItemLibrary>,
cache: RwLock<BTreeMap<u32, Item>>, cache: RwLock<hashbrown::HashMap<u32, Item>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -27,7 +26,7 @@ impl ItemLibrary {
} }
impl DataLibrary<Item> for ItemLibrary { impl DataLibrary<Item> for ItemLibrary {
fn get_cache(&self) -> &spin::rwlock::RwLock<BTreeMap<u32, Item>> { fn get_cache(&self) -> &spin::rwlock::RwLock<hashbrown::HashMap<u32, Item>> {
&self.inner.cache &self.inner.cache
} }

View File

@ -1,5 +1,4 @@
use crate::{cached_value, cached_value_getters, ExternRef, ExternalReferenceType, StringKey}; use crate::{cached_value, cached_value_getters, ExternRef, ExternalReferenceType, StringKey};
use alloc::collections::BTreeMap;
use alloc::rc::Rc; use alloc::rc::Rc;
use move_library::MoveLibrary; use move_library::MoveLibrary;
use spin::rwlock::RwLock; use spin::rwlock::RwLock;
@ -51,6 +50,11 @@ impl StaticData {
type_library: cached_value!({ type_library: cached_value!({
static_data_get_type_library(reference).get_value().unwrap() 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"))] #[cfg(not(feature = "mock_data"))]
impl ExternalReferenceType for StaticData { impl ExternalReferenceType for StaticData {
fn from_extern_value(reference: ExternRef<Self>) -> Self { fn from_extern_value(reference: ExternRef<Self>) -> 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>) -> Self {
LibrarySettings::new(reference)
}
}
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
extern "wasm" { extern "wasm" {
fn static_data_get_move_library(ptr: ExternRef<StaticData>) -> ExternRef<MoveLibrary>; fn static_data_get_move_library(ptr: ExternRef<StaticData>) -> ExternRef<MoveLibrary>;
fn static_data_get_item_library(ptr: ExternRef<StaticData>) -> ExternRef<ItemLibrary>; fn static_data_get_item_library(ptr: ExternRef<StaticData>) -> ExternRef<ItemLibrary>;
fn static_data_get_species_library(ptr: ExternRef<StaticData>) -> ExternRef<SpeciesLibrary>; fn static_data_get_species_library(ptr: ExternRef<StaticData>) -> ExternRef<SpeciesLibrary>;
fn static_data_get_type_library(ptr: ExternRef<StaticData>) -> ExternRef<TypeLibrary>; fn static_data_get_type_library(ptr: ExternRef<StaticData>) -> ExternRef<TypeLibrary>;
fn static_data_get_library_settings(ptr: ExternRef<StaticData>) -> ExternRef<LibrarySettings>;
fn library_settings_get_maximum_level(ptr: ExternRef<LibrarySettings>) -> LevelInt; fn library_settings_get_maximum_level(ptr: ExternRef<LibrarySettings>) -> LevelInt;
} }
@ -141,7 +153,7 @@ where
T: ExternalReferenceType, T: ExternalReferenceType,
T: Clone, T: Clone,
{ {
fn get_cache(&self) -> &RwLock<BTreeMap<u32, T>>; fn get_cache(&self) -> &RwLock<hashbrown::HashMap<u32, T>>;
fn get_self_ref(&self) -> ExternRef<Self> fn get_self_ref(&self) -> ExternRef<Self>
where where
Self: Sized; Self: Sized;
@ -188,7 +200,7 @@ pub trait DataLibrary<T>: Cacheable
where where
T: Clone, T: Clone,
{ {
fn get_cache(&self) -> &RwLock<BTreeMap<u32, T>>; fn get_cache(&self) -> &RwLock<hashbrown::HashMap<u32, T>>;
fn get_self_ref(&self) -> ExternRef<Self> fn get_self_ref(&self) -> ExternRef<Self>
where where
Self: Sized; Self: Sized;

View File

@ -1,13 +1,12 @@
use crate::app_interface::data_libraries::DataLibrary; use crate::app_interface::data_libraries::DataLibrary;
use crate::app_interface::{MoveData, StringKey}; use crate::app_interface::{MoveData, StringKey};
use crate::{ExternRef, ExternalReferenceType}; use crate::{ExternRef, ExternalReferenceType};
use alloc::collections::BTreeMap;
use alloc::rc::Rc; use alloc::rc::Rc;
use spin::RwLock; use spin::RwLock;
struct MoveLibraryInner { struct MoveLibraryInner {
ptr: ExternRef<MoveLibrary>, ptr: ExternRef<MoveLibrary>,
cache: RwLock<BTreeMap<u32, MoveData>>, cache: RwLock<hashbrown::HashMap<u32, MoveData>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -38,7 +37,7 @@ impl MoveLibrary {
} }
impl DataLibrary<MoveData> for MoveLibrary { impl DataLibrary<MoveData> for MoveLibrary {
fn get_cache(&self) -> &spin::rwlock::RwLock<BTreeMap<u32, MoveData>> { fn get_cache(&self) -> &spin::rwlock::RwLock<hashbrown::HashMap<u32, MoveData>> {
&self.inner.cache &self.inner.cache
} }

View File

@ -1,12 +1,11 @@
use crate::app_interface::{DataLibrary, Species}; use crate::app_interface::{DataLibrary, Species};
use crate::{ExternRef, ExternalReferenceType, StringKey}; use crate::{ExternRef, ExternalReferenceType, StringKey};
use alloc::collections::BTreeMap;
use alloc::rc::Rc; use alloc::rc::Rc;
use spin::RwLock; use spin::RwLock;
struct SpeciesLibraryInner { struct SpeciesLibraryInner {
ptr: ExternRef<SpeciesLibrary>, ptr: ExternRef<SpeciesLibrary>,
cache: RwLock<BTreeMap<u32, Species>>, cache: RwLock<hashbrown::HashMap<u32, Species>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -37,7 +36,7 @@ impl SpeciesLibrary {
} }
impl DataLibrary<Species> for SpeciesLibrary { impl DataLibrary<Species> for SpeciesLibrary {
fn get_cache(&self) -> &spin::rwlock::RwLock<BTreeMap<u32, Species>> { fn get_cache(&self) -> &spin::rwlock::RwLock<hashbrown::HashMap<u32, Species>> {
&self.inner.cache &self.inner.cache
} }

View File

@ -1,5 +1,4 @@
use crate::{ExternRef, ExternalReferenceType}; use crate::{ExternRef, ExternalReferenceType, TypeIdentifier};
use alloc::collections::BTreeMap;
use alloc::rc::Rc; use alloc::rc::Rc;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use cstr_core::{c_char, CString}; use cstr_core::{c_char, CString};
@ -7,8 +6,8 @@ use spin::RwLock;
struct TypeLibraryInner { struct TypeLibraryInner {
reference: ExternRef<TypeLibrary>, reference: ExternRef<TypeLibrary>,
name_to_type_cache: RwLock<BTreeMap<String, u8>>, name_to_type_cache: RwLock<hashbrown::HashMap<String, TypeIdentifier>>,
effectiveness_cache: RwLock<BTreeMap<(u8, u8), f32>>, effectiveness_cache: RwLock<hashbrown::HashMap<(TypeIdentifier, TypeIdentifier), f32>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -39,7 +38,7 @@ impl TypeLibrary {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn get_type_from_name(&self, name: &str) -> Option<u8> { pub fn get_type_from_name(&self, name: &str) -> Option<TypeIdentifier> {
if let Some(cached) = self.inner.name_to_type_cache.read().get(name) { if let Some(cached) = self.inner.name_to_type_cache.read().get(name) {
return Some(*cached); return Some(*cached);
} }
@ -48,6 +47,7 @@ impl TypeLibrary {
if v == 255 { if v == 255 {
return None; return None;
} }
let v = v.into();
self.inner self.inner
.name_to_type_cache .name_to_type_cache
.write() .write()
@ -56,7 +56,11 @@ impl TypeLibrary {
} }
#[cfg(not(feature = "mock_data"))] #[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 if let Some(cached) = self
.inner .inner
.effectiveness_cache .effectiveness_cache
@ -68,8 +72,8 @@ impl TypeLibrary {
let effectiveness = unsafe { let effectiveness = unsafe {
type_library_get_single_effectiveness( type_library_get_single_effectiveness(
self.inner.reference, self.inner.reference,
attacking_type, attacking_type.into(),
defending_type, defending_type.into(),
) )
}; };
self.inner self.inner
@ -80,7 +84,11 @@ impl TypeLibrary {
} }
#[cfg(not(feature = "mock_data"))] #[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; let mut f = 1.0;
for defending_type in defending_types { for defending_type in defending_types {
f *= self.get_single_effectiveness(attacking_type, *defending_type); f *= self.get_single_effectiveness(attacking_type, *defending_type);

View File

@ -96,7 +96,7 @@ crate::handling::cacheable::cacheable!(Item);
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
impl ExternalReferenceType for Item { impl ExternalReferenceType for Item {
fn from_extern_value(reference: ExternRef<Self>) -> Self { fn from_extern_value(reference: ExternRef<Self>) -> Self {
Item::mock(reference) Item::new(reference)
} }
} }

View File

@ -14,3 +14,23 @@ pub use nature::*;
pub use species::*; pub use species::*;
pub type LevelInt = u8; 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<u8> for TypeIdentifier {
fn from(val: u8) -> Self {
Self { val }
}
}
impl From<TypeIdentifier> for u8 {
fn from(id: TypeIdentifier) -> Self {
id.val
}
}

View File

@ -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::cached_value::CachedValue;
use crate::handling::Cacheable; use crate::handling::Cacheable;
use crate::{cached_value, cached_value_getters, ExternRef, ExternalReferenceType}; use crate::{cached_value, cached_value_getters, ExternRef, ExternalReferenceType};
@ -104,8 +104,8 @@ impl MoveData {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn has_flag<const N: usize>(&self, flag: &[u8; N]) -> bool { pub fn has_flag(&self, flag: &str) -> bool {
let hash = get_hash_const(flag); let hash = get_hash(flag);
unsafe { move_data_has_flag_by_hash(self.inner.ptr, hash) } unsafe { move_data_has_flag_by_hash(self.inner.ptr, hash) }
} }
} }

View File

@ -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::cached_value::CachedValue;
use crate::handling::Cacheable; use crate::handling::Cacheable;
use crate::{ use crate::{
cached_value, cached_value_getters, ExternRef, ExternalReferenceType, FFIArray, ImmutableList, cached_value, cached_value_getters, ExternRef, ExternalReferenceType, FFIArray, ImmutableList,
StringKey, VecExternRef, StringKey, VecExternRef,
}; };
use alloc::collections::BTreeMap;
use alloc::rc::Rc; use alloc::rc::Rc;
use alloc::vec::Vec; use alloc::vec::Vec;
use spin::RwLock; use spin::RwLock;
@ -147,8 +146,8 @@ impl Form {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn has_flag<const N: usize>(&self, flag: &[u8; N]) -> bool { pub fn has_flag(&self, flag: &str) -> bool {
let hash = get_hash_const(flag); let hash = get_hash(flag);
unsafe { form_has_flag_by_hash(self.inner.reference, hash) } unsafe { form_has_flag_by_hash(self.inner.reference, hash) }
} }
} }
@ -160,7 +159,7 @@ pub struct SpeciesInner {
gender_rate: CachedValue<f32>, gender_rate: CachedValue<f32>,
growth_rate: CachedValue<StringKey>, growth_rate: CachedValue<StringKey>,
capture_rate: CachedValue<u8>, capture_rate: CachedValue<u8>,
forms: RwLock<BTreeMap<u32, Option<Form>>>, forms: RwLock<hashbrown::HashMap<u32, Option<Form>>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -205,8 +204,8 @@ impl Species {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn get_form<const N: usize>(&self, form_name: &[u8; N]) -> Option<Form> { pub fn get_form(&self, form_name: &str) -> Option<Form> {
let hash = get_hash_const(form_name); let hash = get_hash(form_name);
unsafe { unsafe {
if let Some(v) = self.inner.forms.read().get(&hash) { if let Some(v) = self.inner.forms.read().get(&hash) {
v.clone() v.clone()
@ -220,8 +219,8 @@ impl Species {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub fn has_flag<const N: usize>(&self, flag: &[u8; N]) -> bool { pub fn has_flag(&self, flag: &str) -> bool {
let hash = get_hash_const(flag); let hash = get_hash(flag);
unsafe { species_has_flag_by_hash(self.inner.reference, hash) } unsafe { species_has_flag_by_hash(self.inner.reference, hash) }
} }
} }

View File

@ -31,7 +31,7 @@ impl StringKey {
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub(super) fn ptr(&self) -> ExternRef<Self> { pub(crate) fn ptr(&self) -> ExternRef<Self> {
self.data.ptr 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); crate::handling::cacheable::cacheable!(StringKey);
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
@ -134,23 +140,12 @@ const fn to_lower(c: u8) -> u8 {
c c
} }
pub const fn get_hash_const<const N: usize>(s: &[u8; N]) -> u32 { pub const fn get_hash(s: &str) -> 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 {
let mut crc: u32 = 0xffffffff; let mut crc: u32 = 0xffffffff;
let mut i: usize = 0; let mut i: usize = 0;
while i < s.len() { 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; i += 1;
} }
crc ^ 0xffffffff crc ^ 0xffffffff

View File

@ -1,11 +1,9 @@
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
use crate::ExternRef; use crate::ExternRef;
#[cfg(not(feature = "mock_data"))]
use alloc::collections::BTreeMap;
pub trait Cacheable { pub trait Cacheable {
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
fn get_cache<'a>() -> &'a mut BTreeMap<ExternRef<Self>, Self> fn get_cache<'a>() -> &'a mut hashbrown::HashMap<ExternRef<Self>, Self>
where where
Self: Sized; Self: Sized;
@ -19,21 +17,28 @@ pub trait Cacheable {
if let Some(v) = opt { if let Some(v) = opt {
return v.clone(); return v.clone();
} }
Self::get_cache().insert(ptr, ctor(ptr)); let value = ctor(ptr);
return Self::get_cache().get(&ptr).unwrap().clone(); Self::get_cache().insert(ptr, value.clone());
value
} }
} }
macro_rules! cacheable { macro_rules! cacheable {
($type: ty) => { ($type: ty) => {
paste::paste!{ paste::paste! {
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
static mut [<$type:upper _CACHE>]: alloc::collections::BTreeMap<ExternRef<$type>, $type> = static mut [<$type:upper _CACHE>]: Option<hashbrown::HashMap<ExternRef<$type>, $type>> =
alloc::collections::BTreeMap::new(); None;
impl crate::handling::Cacheable for $type { impl crate::handling::Cacheable for $type {
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
fn get_cache<'a>() -> &'a mut alloc::collections::BTreeMap<ExternRef<$type>, $type> { fn get_cache<'a>() -> &'a mut hashbrown::HashMap<ExternRef<$type>, $type> {
unsafe { &mut [<$type:upper _CACHE>] } unsafe {
if let None = [<$type:upper _CACHE>] {
[<$type:upper _CACHE>] = Some(hashbrown::HashMap::new());
}
[<$type:upper _CACHE>].as_mut().unwrap()
}
} }
} }
} }

View File

@ -1,59 +1,62 @@
#[repr(u8)] #[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ScriptCapabilities { pub enum ScriptCapabilities {
None = 0,
Initialize = 1, Initialize = 1,
OnStack, OnStack,
OnRemove, OnRemove,
OnBeforeTurn, OnBeforeTurn,
ChangeAttack, ChangeMove,
ModifyNumberOfHits, ChangeNumberOfHits,
PreventAttack, PreventMove,
FailAttack, FailMove,
StopBeforeAttack, StopBeforeMove,
OnBeforeAttack, OnBeforeMove,
FailIncomingAttack, FailIncomingMove,
IsInvulnerable, IsInvulnerable,
OnAttackMiss, OnMoveMiss,
ChangeAttackType, ChangeMoveType,
ChangeEffectiveness, ChangeEffectiveness,
BlockCritical, BlockCritical,
OnIncomingHit, OnIncomingHit,
OnFaintingOpponent, OnFaintingOpponent,
PreventStatBoostChange, PreventStatBoostChange,
ModifyStatBoostChange, ChangeStatBoostChange,
PreventSecondaryEffects, PreventSecondaryEffects,
OnSecondaryEffect, OnSecondaryEffect,
OnAfterHits, OnAfterHits,
PreventSelfSwitch, PreventSelfSwitch,
ModifyEffectChance, ChangeEffectChance,
ModifyIncomingEffectChance, ChangeIncomingEffectChance,
OverrideBasePower, ChangeBasePower,
ChangeDamageStatsUser, ChangeDamageStatsUser,
BypassDefensiveStat, BypassDefensiveStat,
BypassOffensiveStat, BypassOffensiveStat,
ModifyStatModifier, ChangeStatModifier,
ModifyDamageModifier, ChangeDamageModifier,
OverrideDamage, ChangeDamage,
OverrideIncomingDamage, ChangeIncomingDamage,
ChangeSpeed, ChangeSpeed,
ChangePriority, ChangePriority,
OnFail, OnFail,
OnOpponentFail, OnOpponentFail,
PreventRunAway, PreventSelfRunAway,
PreventOpponentRunAway, PreventOpponentRunAway,
PreventOpponentSwitch, PreventOpponentSwitch,
OnEndTurn, OnEndTurn,
OnDamage, OnDamage,
OnFaint, OnFaint,
OnAfterHeldItemConsume, OnAfterHeldItemConsume,
PreventIncomingCritical, BlockIncomingCritical,
ModifyCriticalStage, ChangeAccuracy,
OverrideCriticalModifier, ChangeCriticalStage,
OverrideSTABModifier, ChangeCriticalModifier,
ModifyExperienceGain, ChangeSTABModifier,
ChangeExperienceGain,
DoesShareExperience, DoesShareExperience,
BlockWeather, BlockWeather,
OnSwitchIn, OnSwitchIn,
ModifyOffensiveStatValue, ChangeOffensiveStatValue,
ModifyDefensiveStatValue, ChangeDefensiveStatValue,
ChangeCaptureRate,
} }

View File

@ -1,5 +1,6 @@
use crate::ImmutableList; use crate::ImmutableList;
use core::cmp::Ordering; use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::intrinsics::transmute; use core::intrinsics::transmute;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -28,6 +29,16 @@ impl<T> ExternRef<T> {
T::instantiate_from_extern_value(*self) 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] #[inline]
pub(crate) fn cast<TCast>(&self) -> ExternRef<TCast> { pub(crate) fn cast<TCast>(&self) -> ExternRef<TCast> {
ExternRef::<TCast> { ExternRef::<TCast> {
@ -76,6 +87,12 @@ impl<T> Ord for ExternRef<T> {
} }
} }
impl<T> Hash for ExternRef<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.p.hash(state)
}
}
#[repr(C)] #[repr(C)]
pub struct VecExternRef<T> { pub struct VecExternRef<T> {
v: u64, v: u64,

View File

@ -1,7 +1,9 @@
use crate::app_interface::list::ImmutableList; 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::handling::ScriptCapabilities;
use crate::{ExternRef, TurnChoice}; use crate::{ExternRef, StringKey, TurnChoice, TypeIdentifier};
use core::ffi::c_void; use core::ffi::c_void;
use core::fmt::Debug; use core::fmt::Debug;
@ -9,17 +11,338 @@ pub trait Script {
fn new() -> Self fn new() -> Self
where where
Self: Sized; Self: Sized;
fn destroy(&self); fn get_name() -> &'static str
fn get_name(&self) -> &str; where
Self: Sized;
fn get_capabilities(&self) -> &[ScriptCapabilities]; 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( fn on_initialize(
&self, &self,
_library: &DynamicLibrary, _library: &DynamicLibrary,
_parameters: Option<ImmutableList<EffectParameter>>, _parameters: Option<ImmutableList<EffectParameter>>,
) { ) {
} }
/// 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) {} 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 { impl Debug for dyn Script {

View File

@ -1,80 +1,63 @@
use crate::{ExternRef, ExternalReferenceType}; use alloc::rc::Rc;
use alloc::boxed::Box; use core::sync::atomic::{AtomicBool, Ordering};
use core::cell::Cell;
use core::mem::forget;
struct TemporaryData<T> {
use_count: Cell<usize>,
is_deleted: bool,
value: T,
}
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
pub struct Temporary<T> { pub struct Temporary<T> {
value: *mut TemporaryData<T>, is_deleted: Rc<AtomicBool>,
value: Rc<T>,
} }
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
static mut TEMPORARIES: alloc::collections::BTreeMap<u32, *const u8> = static mut TEMPORARIES: Option<hashbrown::HashMap<u32, Rc<AtomicBool>>> = None;
alloc::collections::BTreeMap::new();
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
impl<T> Temporary<T> impl<T> Temporary<T> {
where
T: ExternalReferenceType,
T: Clone,
{
#[inline] #[inline]
pub fn from_reference(reference: ExternRef<T>) -> Self { pub fn new(reference: u32, value: T) -> Self {
let temporaries = unsafe { &mut TEMPORARIES };
let existing = temporaries.get(&reference.get_internal_index());
unsafe { unsafe {
if let Some(v) = existing { if let None = TEMPORARIES {
let rc = (*v as *mut TemporaryData<T>).as_mut().unwrap(); TEMPORARIES = Some(hashbrown::HashMap::new());
if !rc.is_deleted {
*rc.use_count.get_mut() += 1;
return Self {
value: rc as *mut TemporaryData<T>,
};
}
} }
} }
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 value = Rc::new(value);
let mut reference_counter = TemporaryData { let is_deleted = Rc::new(AtomicBool::new(false));
use_count: Cell::new(1), temporaries.insert(reference, is_deleted.clone());
is_deleted: false, Self { is_deleted, value }
value,
};
let ptr = &mut reference_counter as *mut TemporaryData<T>;
forget(reference_counter);
temporaries.insert(reference.get_internal_index(), ptr as *const u8);
Self { value: ptr }
} }
#[inline] #[inline]
pub fn value(&self) -> T { pub fn value(&self) -> Rc<T> {
unsafe { self.value.as_ref().unwrap().value.clone() } if self.is_deleted.load(Ordering::SeqCst) {
panic!("Accessed value after it had been deleted");
}
self.value.clone()
} }
#[inline] #[inline]
pub fn value_ref(&self) -> &T { 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<T>) { #[cfg(not(feature = "mock_data"))]
impl Temporary<u8> {
pub(crate) fn mark_as_deleted(reference: u32) {
let temporaries = unsafe { &mut TEMPORARIES }; let temporaries = unsafe { &mut TEMPORARIES };
let existing = temporaries.get(&reference.get_internal_index()); let existing = temporaries.as_mut().unwrap().get_mut(&reference);
unsafe { if let Some(v) = existing {
if let Some(v) = existing { v.store(false, Ordering::SeqCst);
crate::utils::print_raw(b"Dropping temporary");
let rc = (*v as *mut TemporaryData<T>).as_mut().unwrap();
rc.is_deleted = true;
if rc.use_count.get() == 0 {
drop(Box::from(*v as *mut TemporaryData<T>))
}
temporaries.remove(&reference.get_internal_index());
}
} }
} }
} }
@ -82,32 +65,28 @@ where
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
impl<T> Clone for Temporary<T> { impl<T> Clone for Temporary<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*unsafe { self.value.as_mut() }.unwrap().use_count.get_mut() += 1; Self {
Self { value: self.value } is_deleted: self.is_deleted.clone(),
} value: self.value.clone(),
}
#[cfg(not(feature = "mock_data"))]
impl<T> Drop for Temporary<T> {
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))
} }
} }
} }
#[cfg(feature = "mock_data")] #[no_mangle]
pub struct Temporary<T: Clone> { #[cfg(not(feature = "mock_data"))]
value: T, unsafe extern "wasm" fn _mark_deleted(r: u32) {
Temporary::<u8>::mark_as_deleted(r)
} }
#[cfg(feature = "mock_data")] #[cfg(feature = "mock_data")]
impl<T: Clone> Temporary<T> { pub struct Temporary<T> {
value: Rc<T>,
}
#[cfg(feature = "mock_data")]
impl<T> Temporary<T> {
#[inline] #[inline]
pub fn value(&self) -> T { pub fn value(&self) -> Rc<T> {
self.value.clone() self.value.clone()
} }
@ -116,3 +95,12 @@ impl<T: Clone> Temporary<T> {
&self.value &self.value
} }
} }
#[cfg(feature = "mock_data")]
impl<T> Clone for Temporary<T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
}
}
}

View File

@ -12,6 +12,7 @@
#![feature(wasm_abi)] #![feature(wasm_abi)]
#![feature(thread_local)] #![feature(thread_local)]
#![feature(build_hasher_simple_hash_one)] #![feature(build_hasher_simple_hash_one)]
#![feature(adt_const_params)]
#![cfg_attr(not(feature = "mock_data"), no_std)] #![cfg_attr(not(feature = "mock_data"), no_std)]
#![allow(incomplete_features)] #![allow(incomplete_features)]
@ -23,7 +24,12 @@ extern crate wee_alloc;
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
use crate::app_interface::list::ImmutableList; 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::*; pub(crate) use crate::handling::extern_ref::*;
use crate::handling::ffi_array::FFIArray; use crate::handling::ffi_array::FFIArray;
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
@ -47,61 +53,565 @@ pub fn set_load_script_fn(f: LoadScriptFnType) {
} }
} }
#[no_mangle] macro_rules! exported_functions {
#[cfg(not(feature = "mock_data"))] (
extern "wasm" fn load_script(category: ScriptCategory, name: ExternRef<StringKey>) -> u32 { $(
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<dyn Script>,
}
impl ScriptPtr {
pub fn new(ptr: *const Box<dyn Script>) -> Self {
Self { ptr }
}
pub fn val(&self) -> &dyn Script {
unsafe { self.ptr.as_ref().unwrap().as_ref() }
}
pub fn ptr(&self) -> *const Box<dyn Script> {
self.ptr
}
}
exported_functions! {
fn load_script(category: ScriptCategory, name: ExternRef<StringKey>) -> ScriptPtr {
let name_c = StringKey::new(name); let name_c = StringKey::new(name);
let boxed_script = unsafe { &LOAD_SCRIPT_FN }.as_ref().unwrap()(category, &name_c); let boxed_script = unsafe { &LOAD_SCRIPT_FN }.as_ref().unwrap()(category, &name_c);
if boxed_script.is_none() { if boxed_script.is_none() {
return 0; return ScriptPtr::new(core::ptr::null());
} }
let b = Box::new(boxed_script.unwrap()); let b = Box::new(boxed_script.unwrap());
Box::into_raw(b) as u32 ScriptPtr::new(Box::into_raw(b))
} }
#[no_mangle] fn destroy_script(script: *mut Box<dyn Script>) {
#[cfg(not(feature = "mock_data"))]
unsafe extern "wasm" fn destroy_script(script: *mut Box<dyn Script>) {
// By turning it from a raw pointer back into a Box with from_raw, we give ownership back to rust. // 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. // This lets Rust do the cleanup.
let boxed_script = Box::from_raw(script); let boxed_script = Box::from_raw(script);
boxed_script.destroy(); drop(boxed_script);
} }
#[no_mangle] fn get_script_capabilities(script: ScriptPtr) -> FFIArray<ScriptCapabilities> {
#[cfg(not(feature = "mock_data"))] let c = script.val().get_capabilities();
unsafe extern "wasm" fn get_script_capabilities(
script: *const Box<dyn Script>,
) -> FFIArray<ScriptCapabilities> {
let c = script.as_ref().unwrap().get_capabilities();
FFIArray::new(c) FFIArray::new(c)
} }
#[no_mangle] fn script_stack(script: ScriptPtr) {
#[cfg(not(feature = "mock_data"))] script.val().stack();
unsafe extern "wasm" fn script_on_initialize( }
script: *const Box<dyn Script>,
fn script_on_remove(script: ScriptPtr) {
script.val().on_remove();
}
fn script_on_initialize(
script: ScriptPtr,
library: ExternRef<DynamicLibrary>, library: ExternRef<DynamicLibrary>,
parameters: VecExternRef<EffectParameter>, parameters: VecExternRef<EffectParameter>,
) { ) {
let lib = DynamicLibrary::new(library);
let parameters = ImmutableList::from_ref(parameters); let parameters = ImmutableList::from_ref(parameters);
script script.val().on_initialize(&library.not_null(), Some(parameters));
.as_ref()
.unwrap()
.as_ref()
.on_initialize(&lib, Some(parameters));
} }
#[no_mangle] fn script_on_before_turn(
#[cfg(not(feature = "mock_data"))] script: ScriptPtr,
unsafe extern "wasm" fn script_on_before_turn(
script: *const Box<dyn Script>,
choice: ExternRef<TurnChoice>, choice: ExternRef<TurnChoice>,
) { ) {
script script.val().on_before_turn(choice.not_null())
.as_ref() }
.unwrap()
.as_ref() fn script_change_speed(
.on_before_turn(choice.get_value().unwrap()) script:ScriptPtr,
choice: ExternRef<TurnChoice>,
speed: *mut u32,
) {
script.val().change_speed(choice.not_null(), speed.as_mut().unwrap())
}
fn script_change_priority(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
priority: *mut i8,
) {
script.val().change_priority(choice.not_null(), priority.as_mut().unwrap())
}
fn script_change_move(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
mv: *mut ExternRef<StringKey>,
) {
let old = mv.as_ref().unwrap().not_null();
let mut new = old.clone();
script.val().change_move(choice.not_null(), &mut new);
if old != new {
*mv = new.ptr();
}
}
fn script_change_number_of_hits(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
out: *mut u8,
) {
script.val().change_number_of_hits(choice.not_null(), out.as_mut().unwrap());
}
fn script_prevent_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
out: *mut bool,
) {
script.val().prevent_move(mv.not_null(), out.as_mut().unwrap());
}
fn script_fail_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
out: *mut bool,
) {
script.val().fail_move(mv.not_null(), out.as_mut().unwrap());
}
fn script_stop_before_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
out: *mut bool,
) {
script.val().stop_before_move(mv.not_null(), out.as_mut().unwrap());
}
fn script_on_before_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
) {
script.val().on_before_move(mv.not_null());
}
fn script_fail_incoming_move(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
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<ExecutingMove>,
target: ExternRef<Pokemon>,
) {
script.val().on_move_miss(mv.not_null(), target.not_null());
}
fn script_change_move_type(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut TypeIdentifier,
) {
script.val().change_move_type(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_effectiveness(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_effectiveness(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_block_critical(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut bool,
) {
script.val().block_critical(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_block_incoming_critical(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut bool,
) {
script.val().block_incoming_critical(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_accuracy(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u8,
) {
script.val().change_accuracy(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_critical_stage(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u8,
) {
script.val().change_critical_stage(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_critical_modifier(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_critical_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_stab_modifier(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_stab_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_base_power(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u8,
) {
script.val().change_base_power(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_bypass_defensive_stat_boost(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut bool,
) {
script.val().bypass_defensive_stat_boost(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_bypass_offensive_stat_boost(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut bool,
) {
script.val().bypass_offensive_stat_boost(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_defensive_stat_value(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u32,
) {
script.val().change_defensive_stat_value(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_offensive_stat_value(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u32,
) {
script.val().change_offensive_stat_value(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_damage_stat_modifier(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_damage_stat_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_damage_modifier(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_damage_modifier(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_damage(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u32,
) {
script.val().change_damage(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_incoming_damage(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut u32,
) {
script.val().change_incoming_damage(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_on_incoming_hit(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
) {
script.val().on_incoming_hit(mv.not_null(), target.not_null(), hit);
}
fn script_on_opponent_faints(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
) {
script.val().on_opponent_faints(mv.not_null(), target.not_null(), hit);
}
fn script_prevent_stat_boost_change(
script: ScriptPtr,
target: ExternRef<Pokemon>,
stat: Statistic,
amount: i8,
self_inflicted: u8,
out: *mut bool,
) {
script.val().prevent_stat_boost_change(target.not_null(), stat, amount, self_inflicted == 1, out.as_mut().unwrap());
}
fn script_prevent_secondary_effect(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut bool,
) {
script.val().prevent_secondary_effect(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_effect_chance(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_effect_chance(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_change_incoming_effect_chance(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
out: *mut f32,
) {
script.val().change_incoming_effect_chance(mv.not_null(), target.not_null(), hit, out.as_mut().unwrap());
}
fn script_on_secondary_effect(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8,
) {
script.val().on_secondary_effect(mv.not_null(), target.not_null(), hit);
}
fn script_on_after_hits(
script: ScriptPtr,
mv: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
) {
script.val().on_after_hits(mv.not_null(), target.not_null());
}
fn script_prevent_self_switch(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
out: *mut bool,
) {
script.val().prevent_self_switch(choice.not_null(), out.as_mut().unwrap());
}
fn script_prevent_opponent_switch(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
out: *mut bool,
) {
script.val().prevent_opponent_switch(choice.not_null(), out.as_mut().unwrap());
}
fn script_on_fail(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
) {
script.val().on_fail(pokemon.not_null());
}
fn script_on_opponent_fail(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
) {
script.val().on_opponent_fail(pokemon.not_null());
}
fn script_prevent_self_run_away(
script: ScriptPtr,
choice: ExternRef<TurnChoice>,
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<TurnChoice>,
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<Pokemon>,
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<Pokemon>,
source: DamageSource,
) {
script.val().on_faint(pokemon.not_null(), source);
}
fn script_on_switch_in(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
) {
script.val().on_switch_in(pokemon.not_null());
}
fn script_on_after_held_item_consume(
script: ScriptPtr,
pokemon: ExternRef<Pokemon>,
item: ExternRef<Item>
) {
script.val().on_after_held_item_consume(pokemon.not_null(), &item.not_null());
}
fn script_change_experience_gained(
script: ScriptPtr,
fainted_pokemon: ExternRef<Pokemon>,
winning_pokemon: ExternRef<Pokemon>,
out: *mut u32
) {
script.val().change_experience_gained(fainted_pokemon.not_null(), winning_pokemon.not_null(), out.as_mut().unwrap());
}
fn script_share_experience(
script: ScriptPtr,
fainted_pokemon: ExternRef<Pokemon>,
winning_pokemon: ExternRef<Pokemon>,
out: *mut bool,
) {
script.val().share_experience(fainted_pokemon.not_null(), winning_pokemon.not_null(), out.as_mut().unwrap());
}
fn script_block_weather(
script: ScriptPtr,
battle: ExternRef<Battle>,
out: *mut bool,
) {
script.val().block_weather(battle.not_null(), out.as_mut().unwrap());
}
fn script_change_capture_rate_bonus(
script: ScriptPtr,
target: ExternRef<Pokemon>,
pokeball: ExternRef<Item>,
out: *mut u8,
) {
script.val().change_capture_rate_bonus(target.not_null(), pokeball.not_null(), out.as_mut().unwrap());
}
} }

View File

@ -2,7 +2,9 @@ use alloc::alloc::alloc;
use core::alloc::Layout; use core::alloc::Layout;
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]
use core::panic::PanicInfo; 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"))]
#[cfg(not(feature = "mock_data"))] #[cfg(not(feature = "mock_data"))]