Implements every script function
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2022-08-27 18:04:56 +02:00
parent bd62c1ac62
commit ba5992e925
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
19 changed files with 919 additions and 158 deletions

View File

@ -92,8 +92,8 @@ impl Battle {
/// Executes a move choice.
fn execute_move_choice<'func>(&'func self, choice: &'func TurnChoice) -> PkmnResult<()> {
let choice = choice.get_move_turn_data();
let used_move = choice.used_move();
let move_choice = choice.get_move_turn_data();
let used_move = move_choice.used_move();
let move_data = {
let move_data_lock = used_move;
let move_data = move_data_lock.move_data();
@ -103,7 +103,7 @@ impl Battle {
};
// FIXME: also change the script on the choice if changed;
let target_type = move_data.target();
let targets = resolve_targets(choice.target_side(), choice.target_index(), target_type, self);
let targets = resolve_targets(move_choice.target_side(), move_choice.target_index(), target_type, self);
let mut number_of_hits: u8 = 1;
script_hook!(change_number_of_hits, choice, choice, &mut number_of_hits);
@ -116,7 +116,7 @@ impl Battle {
choice.user().clone(),
used_move.clone(),
move_data.clone(),
choice.script().clone(),
move_choice.script().clone(),
);
let mut prevented = false;
script_hook!(prevent_move, executing_move, &executing_move, &mut prevented);

View File

@ -22,6 +22,7 @@ use crate::{script_hook, PkmnResult, StringKey};
/// A pokemon battle, with any amount of sides and pokemon per side.
#[derive(Debug)]
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
pub struct Battle {
/// The library the battle uses for handling.
library: Arc<DynamicLibrary>,

View File

@ -84,6 +84,7 @@ impl HitData {
/// An executing move is the data of the move for while it is executing.
#[derive(Debug)]
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
pub struct ExecutingMove {
/// The number of hits this move has.
number_of_hits: u8,

View File

@ -7,7 +7,7 @@ use std::thread::JoinHandle;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use crate::dynamic_data::choices::{MoveChoice, TurnChoice};
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::DamageSource;
use crate::dynamic_data::ExecutingMove;
use crate::dynamic_data::Pokemon;
@ -79,11 +79,11 @@ pub trait Script: Send + Sync {
/// 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: &MoveChoice, _move_name: &mut StringKey) {}
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: &MoveChoice, _number_of_hits: &mut u8) {}
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.

View File

@ -13,7 +13,7 @@ register! {
turn_choice: ExternRef<LearnedMove>,
) -> u8 {
unsafe {
transmute(turn_choice.value(&env).unwrap().learn_method())
transmute(turn_choice.value_func(&env).unwrap().learn_method())
}
}
@ -21,6 +21,6 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<LearnedMove>,
) -> ExternRef<MoveData> {
ExternRef::func_new(&env, turn_choice.value(&env).unwrap().move_data())
ExternRef::func_new(&env, turn_choice.value_func(&env).unwrap().move_data())
}
}

View File

@ -18,7 +18,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
dynamic_lib: ExternRef<DynamicLibrary>,
) -> ExternRef<StaticData> {
ExternRef::func_new(&env, dynamic_lib.value(&env).unwrap().static_data())
ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data())
}
manual manual_register
}

View File

@ -13,7 +13,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<DynamicLibrary> {
let lib = pokemon.value(&env).unwrap().library();
let lib = pokemon.value_func(&env).unwrap().library();
ExternRef::func_new(&env, lib)
}
@ -21,7 +21,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<StatisticSet<u32>> {
let statistic_set = pokemon.value(&env).unwrap().boosted_stats();
let statistic_set = pokemon.value_func(&env).unwrap().boosted_stats();
ExternRef::func_new(&env, statistic_set)
}
@ -29,7 +29,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<StatisticSet<u32>> {
let statistic_set = pokemon.value(&env).unwrap().flat_stats();
let statistic_set = pokemon.value_func(&env).unwrap().flat_stats();
ExternRef::func_new(&env, statistic_set)
}
@ -37,7 +37,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<ClampedStatisticSet<i8, -6, 6>> {
let statistic_set = pokemon.value(&env).unwrap().stat_boosts();
let statistic_set = pokemon.value_func(&env).unwrap().stat_boosts();
ExternRef::func_new(&env, statistic_set)
}
@ -45,7 +45,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<ClampedStatisticSet<u8, 0, 31>> {
let statistic_set = pokemon.value(&env).unwrap().individual_values();
let statistic_set = pokemon.value_func(&env).unwrap().individual_values();
ExternRef::func_new(&env, statistic_set)
}
@ -53,7 +53,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<ClampedStatisticSet<u8, 0, 252>> {
let statistic_set = pokemon.value(&env).unwrap().effort_values();
let statistic_set = pokemon.value_func(&env).unwrap().effort_values();
ExternRef::func_new(&env, statistic_set)
}
@ -61,7 +61,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<Species> {
let species = pokemon.value(&env).unwrap().species();
let species = pokemon.value_func(&env).unwrap().species();
ExternRef::func_new(&env, species)
}
@ -72,7 +72,7 @@ register! {
source: u8
) {
unsafe{
pokemon.value(&env).unwrap().damage(damage, transmute(source));
pokemon.value_func(&env).unwrap().damage(damage, transmute(source));
}
}
}

View File

@ -12,7 +12,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<TurnChoice>,
) -> ExternRef<Pokemon> {
let turn_choice = turn_choice.value(&env).unwrap();
let turn_choice = turn_choice.value_func(&env).unwrap();
ExternRef::func_new(&env, turn_choice.user().as_ref().deref())
}
@ -20,7 +20,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<TurnChoice>,
) -> u8 {
match turn_choice.value(&env).unwrap() {
match turn_choice.value_func(&env).unwrap() {
TurnChoice::Move(_) => 0,
TurnChoice::Item(_) => 1,
TurnChoice::Switch(_) => 2,
@ -33,7 +33,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<TurnChoice>,
) -> ExternRef<LearnedMove> {
if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() {
if let TurnChoice::Move(d) = turn_choice.value_func(&env).unwrap() {
return ExternRef::func_new(&env, d.used_move().as_ref());
}
panic!("Invalid turn choice");
@ -43,7 +43,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<TurnChoice>,
) -> u8 {
if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() {
if let TurnChoice::Move(d) = turn_choice.value_func(&env).unwrap() {
return d.target_side();
}
panic!("Invalid turn choice");
@ -53,7 +53,7 @@ register! {
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<TurnChoice>,
) -> u8 {
if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() {
if let TurnChoice::Move(d) = turn_choice.value_func(&env).unwrap() {
return d.target_index();
}
panic!("Invalid turn choice");

View File

@ -119,13 +119,13 @@ fn _vec_extern_ref_get_value(env: FunctionEnvMut<WebAssemblyEnv>, reference: u32
/// Gets the hash value of a StringKey.
fn string_key_get_hash(env: FunctionEnvMut<WebAssemblyEnv>, string_key: ExternRef<StringKey>) -> u32 {
string_key.value(&env).unwrap().hash()
string_key.value_func(&env).unwrap().hash()
}
/// Get a null-terminated C string from a StringKey. Note that this involves a copy into WASM
/// memory, so this is relatively heavy.
fn string_key_get_str(env: FunctionEnvMut<WebAssemblyEnv>, string_key: ExternRef<StringKey>) -> u32 {
let string_key = string_key.value(&env).unwrap().str();
let string_key = string_key.value_func(&env).unwrap().str();
let wasm_string_ptr = env
.data()
.data()
@ -141,7 +141,7 @@ fn string_key_get_str(env: FunctionEnvMut<WebAssemblyEnv>, string_key: ExternRef
/// Gets the type of an EffectParameter
fn effect_parameter_get_type(env: FunctionEnvMut<WebAssemblyEnv>, parameter: ExternRef<EffectParameter>) -> u8 {
let v = parameter.value(&env).unwrap();
let v = parameter.value_func(&env).unwrap();
match v {
EffectParameter::Bool(_) => 1,
EffectParameter::Int(_) => 2,
@ -152,7 +152,7 @@ fn effect_parameter_get_type(env: FunctionEnvMut<WebAssemblyEnv>, parameter: Ext
/// Gets the inner bool data of an EffectParameter. Panics if it's not a bool.
fn effect_parameter_as_bool(env: FunctionEnvMut<WebAssemblyEnv>, parameter: ExternRef<EffectParameter>) -> u8 {
let v = parameter.value(&env).unwrap();
let v = parameter.value_func(&env).unwrap();
match v {
EffectParameter::Bool(b) => {
if *b {
@ -167,7 +167,7 @@ fn effect_parameter_as_bool(env: FunctionEnvMut<WebAssemblyEnv>, parameter: Exte
/// Gets the inner int data of an EffectParameter. Panics if it's not an int.
fn effect_parameter_as_int(env: FunctionEnvMut<WebAssemblyEnv>, parameter: ExternRef<EffectParameter>) -> i64 {
let v = parameter.value(&env).unwrap();
let v = parameter.value_func(&env).unwrap();
match v {
EffectParameter::Int(i) => *i,
_ => panic!("Unexpected parameter type!"),
@ -176,7 +176,7 @@ fn effect_parameter_as_int(env: FunctionEnvMut<WebAssemblyEnv>, parameter: Exter
/// Gets the inner float data of an EffectParameter. Panics if it's not a float.
fn effect_parameter_as_float(env: FunctionEnvMut<WebAssemblyEnv>, parameter: ExternRef<EffectParameter>) -> f32 {
let v = parameter.value(&env).unwrap();
let v = parameter.value_func(&env).unwrap();
match v {
EffectParameter::Float(f) => *f,
_ => panic!("Unexpected parameter type!"),
@ -188,7 +188,7 @@ fn effect_parameter_as_string(
env: FunctionEnvMut<WebAssemblyEnv>,
parameter: ExternRef<EffectParameter>,
) -> ExternRef<StringKey> {
let v = parameter.value(&env).unwrap();
let v = parameter.value_func(&env).unwrap();
match v {
EffectParameter::String(s) => ExternRef::func_new(&env, s),
_ => panic!("Unexpected parameter type!"),

View File

@ -13,33 +13,33 @@ mod species;
register! {
fn static_data_get_move_library(env: FunctionEnvMut<WebAssemblyEnv>, data_library: ExternRef<StaticData>) -> ExternRef<MoveLibrary> {
ExternRef::func_new(&env, data_library.value(&env).unwrap().moves())
ExternRef::func_new(&env, data_library.value_func(&env).unwrap().moves())
}
fn static_data_get_species_library(
env: FunctionEnvMut<WebAssemblyEnv>,
data_library: ExternRef<StaticData>,
) -> ExternRef<SpeciesLibrary> {
ExternRef::func_new(&env, data_library.value(&env).unwrap().species())
ExternRef::func_new(&env, data_library.value_func(&env).unwrap().species())
}
fn static_data_get_item_library(env: FunctionEnvMut<WebAssemblyEnv>, data_library: ExternRef<StaticData>) -> ExternRef<ItemLibrary> {
ExternRef::func_new(&env, data_library.value(&env).unwrap().items())
ExternRef::func_new(&env, data_library.value_func(&env).unwrap().items())
}
fn static_data_get_type_library(env: FunctionEnvMut<WebAssemblyEnv>, data_library: ExternRef<StaticData>) -> ExternRef<TypeLibrary> {
ExternRef::func_new(&env, data_library.value(&env).unwrap().types())
ExternRef::func_new(&env, data_library.value_func(&env).unwrap().types())
}
fn static_data_get_library_settings(
env: FunctionEnvMut<WebAssemblyEnv>,
data_library: ExternRef<StaticData>,
) -> ExternRef<LibrarySettings> {
ExternRef::func_new(&env, data_library.value(&env).unwrap().settings())
ExternRef::func_new(&env, data_library.value_func(&env).unwrap().settings())
}
fn library_settings_get_maximum_level(env: FunctionEnvMut<WebAssemblyEnv>, data_library: ExternRef<LibrarySettings>) -> LevelInt {
data_library.value(&env).unwrap().maximum_level()
data_library.value_func(&env).unwrap().maximum_level()
}
manual manual_registration

View File

@ -11,8 +11,8 @@ fn move_library_get_move(
lib: ExternRef<MoveLibrary>,
string_key: ExternRef<StringKey>,
) -> ExternRef<MoveData> {
let lib = lib.value(&env).unwrap();
let m = lib.get(string_key.value(&env).unwrap());
let lib = lib.value_func(&env).unwrap();
let m = lib.get(string_key.value_func(&env).unwrap());
if let Some(v) = m {
ExternRef::func_new(&env, v)
} else {
@ -21,7 +21,7 @@ fn move_library_get_move(
}
fn move_library_get_move_by_hash(env: FunctionEnvMut<WebAssemblyEnv>, lib: ExternRef<MoveLibrary>, hash: u32) -> ExternRef<MoveData> {
let lib = lib.value(&env).unwrap();
let lib = lib.value_func(&env).unwrap();
let m = lib.get_by_hash(hash);
if let Some(v) = m {
ExternRef::func_new(&env, v)
@ -31,39 +31,39 @@ fn move_library_get_move_by_hash(env: FunctionEnvMut<WebAssemblyEnv>, lib: Exter
}
fn move_data_get_name(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> ExternRef<StringKey> {
ExternRef::func_new(&env, move_data.value(&env).unwrap().name())
ExternRef::func_new(&env, move_data.value_func(&env).unwrap().name())
}
fn move_data_get_type(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> u8 {
move_data.value(&env).unwrap().move_type().into()
move_data.value_func(&env).unwrap().move_type().into()
}
fn move_data_get_category(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> u8 {
move_data.value(&env).unwrap().category() as u8
move_data.value_func(&env).unwrap().category() as u8
}
fn move_data_get_base_power(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> u8 {
move_data.value(&env).unwrap().base_power()
move_data.value_func(&env).unwrap().base_power()
}
fn move_data_get_accuracy(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> u8 {
move_data.value(&env).unwrap().accuracy()
move_data.value_func(&env).unwrap().accuracy()
}
fn move_data_get_base_usages(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> u8 {
move_data.value(&env).unwrap().base_usages()
move_data.value_func(&env).unwrap().base_usages()
}
fn move_data_get_target(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> u8 {
move_data.value(&env).unwrap().target() as u8
move_data.value_func(&env).unwrap().target() as u8
}
fn move_data_get_priority(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>) -> i8 {
move_data.value(&env).unwrap().priority()
move_data.value_func(&env).unwrap().priority()
}
fn move_data_has_flag(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>, flag: ExternRef<StringKey>) -> u8 {
if move_data.value(&env).unwrap().has_flag(flag.value(&env).unwrap()) {
if move_data.value_func(&env).unwrap().has_flag(flag.value_func(&env).unwrap()) {
1
} else {
0
}
}
fn move_data_has_flag_by_hash(env: FunctionEnvMut<WebAssemblyEnv>, move_data: ExternRef<MoveData>, flag_hash: u32) -> u8 {
if move_data.value(&env).unwrap().has_flag_by_hash(flag_hash) {
if move_data.value_func(&env).unwrap().has_flag_by_hash(flag_hash) {
1
} else {
0

View File

@ -12,8 +12,8 @@ fn species_library_get_species(
lib: ExternRef<SpeciesLibrary>,
string_key: ExternRef<StringKey>,
) -> ExternRef<Species> {
let lib = lib.value(&env).unwrap();
let m = lib.get(string_key.value(&env).unwrap());
let lib = lib.value_func(&env).unwrap();
let m = lib.get(string_key.value_func(&env).unwrap());
if let Some(v) = m {
ExternRef::func_new(&env, v)
} else {
@ -25,14 +25,14 @@ fn species_get_capture_rate(
env: FunctionEnvMut<WebAssemblyEnv>,
species: ExternRef<Species>,
) -> u8 {
species.value(&env).unwrap().capture_rate()
species.value_func(&env).unwrap().capture_rate()
}
fn species_get_growth_rate(
env: FunctionEnvMut<WebAssemblyEnv>,
species: ExternRef<Species>,
) -> ExternRef<StringKey> {
let species = species.value(&env).unwrap();
let species = species.value_func(&env).unwrap();
ExternRef::func_new(&env, species.growth_rate())
}
@ -40,14 +40,14 @@ fn species_get_gender_rate(
env: FunctionEnvMut<WebAssemblyEnv>,
species: ExternRef<Species>,
) -> f32 {
species.value(&env).unwrap().gender_rate()
species.value_func(&env).unwrap().gender_rate()
}
fn species_get_name(
env: FunctionEnvMut<WebAssemblyEnv>,
species: ExternRef<Species>,
) -> ExternRef<StringKey> {
let species = species.value(&env).unwrap();
let species = species.value_func(&env).unwrap();
ExternRef::func_new(&env, species.name())
}
@ -55,7 +55,7 @@ fn species_get_id(
env: FunctionEnvMut<WebAssemblyEnv>,
species: ExternRef<Species>,
) -> u16 {
species.value(&env).unwrap().id()
species.value_func(&env).unwrap().id()
}

View File

@ -1,5 +1,6 @@
use std::marker::PhantomData;
use std::mem::transmute;
use std::sync::Arc;
use crate::script_implementations::wasm::script_resolver::{
WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver,
@ -11,6 +12,7 @@ use wasmer::FunctionEnvMut;
/// An Extern Ref allows us to pass objects to WASM without actually passing raw memory, or
/// requiring us to make copies. Instead, we pass a simple increment index, that we can then use
/// to find the relevant data.
#[derive(Copy, Clone)]
pub(crate) struct ExternRef<T: UniqueTypeId<u64>> {
/// The lookup index we can use to find the data.
index: u32,
@ -55,8 +57,14 @@ impl<T: UniqueTypeId<u64>> ExternRef<T> {
/// Returns the real value for a given ExternRef. Note that the requested type must be the same as the type of the
/// value when it was passed before. If these types do not match, this will panic.
pub fn value<'a, 'b>(&'a self, env: &'b FunctionEnvMut<WebAssemblyEnv>) -> Option<&'b T> {
let ptr = env.data().data().get_extern_ref_value(self.index) as *const T;
pub fn value_func<'a, 'b>(&'a self, env: &'b FunctionEnvMut<WebAssemblyEnv>) -> Option<&'b T> {
self.value(&env.data().data())
}
/// Returns the real value for a given ExternRef. Note that the requested type must be the same as the type of the
/// value when it was passed before. If these types do not match, this will panic.
pub fn value<'a, 'b, 'c>(&'a self, env: &'b Arc<WebAssemblyEnvironmentData>) -> Option<&'c T> {
let ptr = env.get_extern_ref_value(self.index) as *const T;
unsafe { ptr.as_ref() }
}
}

View File

@ -16,61 +16,64 @@ mod temp_wasm_allocator;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[allow(missing_docs)]
#[allow(clippy::missing_docs_in_private_items)]
#[repr(u8)]
pub enum WebAssemblyScriptCapabilities {
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,
}

View File

@ -1,14 +1,14 @@
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
use std::sync::{Arc, Weak};
use std::sync::Arc;
use hashbrown::HashSet;
use crate::dynamic_data::{DynamicLibrary, Script, TurnChoice};
use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, TurnChoice};
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
use crate::static_data::EffectParameter;
use crate::static_data::{EffectParameter, Item, Statistic, TypeIdentifier};
use crate::StringKey;
/// A WebAssemblyScript is there to implement the Script trait within WebAssemblyScript.
@ -29,7 +29,7 @@ pub struct WebAssemblyScript {
/// Capabilities define which functions we actually implement.
capabilities: Arc<HashSet<WebAssemblyScriptCapabilities>>,
/// The global runtime environment data.
environment: Weak<WebAssemblyEnvironmentData>,
environment: Arc<WebAssemblyEnvironmentData>,
}
impl WebAssemblyScript {
@ -38,7 +38,7 @@ impl WebAssemblyScript {
owner_ptr: *mut u8,
self_ptr: u32,
capabilities: Arc<HashSet<WebAssemblyScriptCapabilities>>,
environment: Weak<WebAssemblyEnvironmentData>,
environment: Arc<WebAssemblyEnvironmentData>,
name: StringKey,
) -> Self {
Self {
@ -51,6 +51,33 @@ impl WebAssemblyScript {
environment,
}
}
/// Check if this script implements a certain capability.
#[inline(always)]
fn has_capability(&self, cap: &WebAssemblyScriptCapabilities) -> bool {
self.capabilities.contains(cap)
}
}
/// Util macro to reduce function call verbosity.
macro_rules! call_func {
($func:ident, $env:ident, $self:ident $(, $par_name:expr)*) => {
$func.call(&mut $env.store_mut(), $self.self_ptr $(, $par_name)*).unwrap();
}
}
/// Util macro to reduce extern ref instantiation verbosity.
macro_rules! ex_ref {
($env:ident, $value:expr) => {
ExternRef::new($env.as_ref(), $value)
};
}
/// Util macro to reduce vec extern ref instantiation verbosity.
macro_rules! vec_ex_ref {
($env:ident, $value:expr) => {
VecExternRef::new($env.as_ref(), $value)
};
}
impl Script for WebAssemblyScript {
@ -67,104 +94,721 @@ impl Script for WebAssemblyScript {
}
fn stack(&self) {
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnStack) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnStack) {
return;
}
let env = self.environment.upgrade().unwrap();
let func = env.script_function_cache().stack(&env);
if let Some(func) = func {
func.call(&mut env.store_mut(), self.self_ptr).unwrap();
let env = &self.environment;
if let Some(func) = env.script_function_cache().stack(env) {
call_func!(func, env, self);
}
}
fn on_remove(&self) {
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnRemove) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnRemove) {
return;
}
let env = self.environment.upgrade().unwrap();
let func = env.script_function_cache().on_remove(&env);
if let Some(func) = func {
func.call(&mut env.store_mut(), self.self_ptr).unwrap();
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_remove(env) {
call_func!(func, env, self);
}
}
fn on_initialize(&self, library: &DynamicLibrary, pars: &[EffectParameter]) {
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::Initialize) {
if !self.has_capability(&WebAssemblyScriptCapabilities::Initialize) {
return;
}
let env = self.environment.upgrade().unwrap();
let func = env.script_function_cache().on_initialize(&env);
if let Some(func) = func {
func.call(
&mut env.store_mut(),
self.self_ptr,
ExternRef::new(env.as_ref(), library),
VecExternRef::new(env.as_ref(), pars),
)
.unwrap();
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_initialize(env) {
call_func!(func, env, self, ex_ref!(env, library), vec_ex_ref!(env, pars));
}
}
fn on_before_turn(&self, choice: &TurnChoice) {
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::OnBeforeTurn) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeTurn) {
return;
}
let env = self.environment.upgrade().unwrap();
let func = env.script_function_cache().on_before_turn(&env);
if let Some(func) = func {
func.call(
&mut env.store_mut(),
self.self_ptr,
ExternRef::new(env.as_ref(), choice),
)
.unwrap();
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_before_turn(env) {
call_func!(func, env, self, ex_ref!(env, choice));
}
}
fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) {
if !self.capabilities.contains(&WebAssemblyScriptCapabilities::ChangeSpeed) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSpeed) {
return;
}
let env = self.environment.upgrade().unwrap();
let func = env.script_function_cache().change_speed(&env);
if let Some(func) = func {
let ptr = env.temp_allocate_mem_typed::<u32>(*speed);
func.call(
&mut env.store_mut(),
self.self_ptr,
ExternRef::new(env.as_ref(), choice),
ptr.wasm_pointer,
)
.unwrap();
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_speed(env) {
let ptr = env.allocate_temp::<u32>(*speed);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*speed = *ptr.value();
}
}
fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) {
if !self
.capabilities
.contains(&WebAssemblyScriptCapabilities::ChangePriority)
{
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangePriority) {
return;
}
let env = self.environment.upgrade().unwrap();
let func = env.script_function_cache().change_priority(&env);
if let Some(func) = func {
let ptr = env.temp_allocate_mem_typed::<i8>(*priority);
func.call(
&mut env.store_mut(),
self.self_ptr,
ExternRef::new(env.as_ref(), choice),
ptr.wasm_pointer,
)
.unwrap();
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_priority(env) {
let ptr = env.allocate_temp::<i8>(*priority);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*priority = *ptr.value();
}
}
fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMove) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_move(env) {
let move_ref = ex_ref!(env, move_name);
let ptr = env.allocate_temp::<ExternRef<StringKey>>(move_ref);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*move_name = ptr.value().value(env).unwrap().clone();
}
}
fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeNumberOfHits) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_number_of_hits(env) {
let ptr = env.allocate_temp::<u8>(*number_of_hits);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*number_of_hits = *ptr.value();
}
}
fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventMove) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_move(env) {
let ptr = env.allocate_temp::<bool>(*prevent);
call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr);
*prevent = *ptr.value();
}
}
fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::FailMove) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().fail_move(env) {
let ptr = env.allocate_temp::<bool>(*fail);
call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr);
*fail = *ptr.value();
}
}
fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::StopBeforeMove) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().stop_before_move(env) {
let ptr = env.allocate_temp::<bool>(*stop);
call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr);
*stop = *ptr.value();
}
}
fn on_before_move(&self, mv: &ExecutingMove) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeMove) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_before_move(env) {
call_func!(func, env, self, ex_ref!(env, mv));
}
}
fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, fail: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::FailIncomingMove) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().fail_incoming_move(env) {
let ptr = env.allocate_temp::<bool>(*fail);
call_func!(
func,
env,
self,
ex_ref!(env, mv),
ex_ref!(env, target.as_ref()),
ptr.wasm_ptr
);
*fail = *ptr.value();
}
}
fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, invulnerable: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::IsInvulnerable) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().is_invulnerable(env) {
let ptr = env.allocate_temp::<bool>(*invulnerable);
call_func!(
func,
env,
self,
ex_ref!(env, mv),
ex_ref!(env, target.as_ref()),
ptr.wasm_ptr
);
*invulnerable = *ptr.value();
}
}
fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc<Pokemon>) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnMoveMiss) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_move_miss(env) {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target.as_ref()));
}
}
fn change_move_type(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, move_type: &mut TypeIdentifier) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMoveType) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_move_type(env) {
let ptr = env.allocate_temp::<TypeIdentifier>(*move_type);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*move_type = *ptr.value();
}
}
fn change_effectiveness(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, effectiveness: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectiveness) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_effectiveness(env) {
let ptr = env.allocate_temp(*effectiveness);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*effectiveness = *ptr.value();
}
}
fn block_critical(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, block_critical: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::BlockCritical) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().block_critical(env) {
let ptr = env.allocate_temp(*block_critical);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*block_critical = *ptr.value();
}
}
fn block_incoming_critical(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, block_critical: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::BlockIncomingCritical) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().block_incoming_critical(env) {
let ptr = env.allocate_temp(*block_critical);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*block_critical = *ptr.value();
}
}
fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, accuracy: &mut u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeAccuracy) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_accuracy(env) {
let ptr = env.allocate_temp(*accuracy);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*accuracy = *ptr.value();
}
}
fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, stage: &mut u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalStage) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_critical_stage(env) {
let ptr = env.allocate_temp(*stage);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*stage = *ptr.value();
}
}
fn change_critical_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalModifier) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_critical_modifier(env) {
let ptr = env.allocate_temp(*modifier);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
}
fn change_stab_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSTABModifier) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_stab_modifier(env) {
let ptr = env.allocate_temp(*modifier);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
}
fn change_base_power(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, base_power: &mut u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeBasePower) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_base_power(env) {
let ptr = env.allocate_temp(*base_power);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*base_power = *ptr.value();
}
}
fn bypass_defensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, bypass: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::BypassDefensiveStat) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().bypass_defensive_stat_boost(env) {
let ptr = env.allocate_temp(*bypass);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*bypass = *ptr.value();
}
}
fn bypass_offensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, bypass: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::BypassOffensiveStat) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().bypass_offensive_stat_boost(env) {
let ptr = env.allocate_temp(*bypass);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*bypass = *ptr.value();
}
}
fn change_offensive_stat_value(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, amount: &mut u32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_offensive_stat_value(env) {
let ptr = env.allocate_temp(*amount);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*amount = *ptr.value();
}
}
fn change_defensive_stat_value(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, amount: &mut u32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_defensive_stat_value(env) {
let ptr = env.allocate_temp(*amount);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*amount = *ptr.value();
}
}
fn change_damage_stat_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeStatModifier) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_damage_stat_modifier(env) {
let ptr = env.allocate_temp(*modifier);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
}
fn change_damage_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamageModifier) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_damage_modifier(env) {
let ptr = env.allocate_temp(*modifier);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
}
fn change_damage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, damage: &mut u32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamage) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_damage(env) {
let ptr = env.allocate_temp(*damage);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*damage = *ptr.value();
}
}
fn change_incoming_damage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, damage: &mut u32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingDamage) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_incoming_damage(env) {
let ptr = env.allocate_temp(*damage);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*damage = *ptr.value();
}
}
fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnIncomingHit) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_incoming_hit(env) {
let target = target.as_ref();
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit);
}
}
fn on_opponent_faints(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_opponent_faints(env) {
let target = target.as_ref();
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit);
}
}
fn prevent_stat_boost_change(
&self,
target: &Pokemon,
stat: Statistic,
amount: i8,
self_inflicted: bool,
prevent: &mut bool,
) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventStatBoostChange) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_stat_boost_change(env) {
let ptr = env.allocate_temp(*prevent);
let self_inflicted = if self_inflicted { 1_u8 } else { 0_u8 };
call_func!(
func,
env,
self,
ex_ref!(env, target),
stat as u8,
amount,
self_inflicted,
ptr.wasm_ptr
);
*prevent = *ptr.value();
}
}
fn prevent_secondary_effect(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, prevent: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSecondaryEffects) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_secondary_effect(env) {
let ptr = env.allocate_temp(*prevent);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*prevent = *ptr.value();
}
}
fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, chance: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectChance) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_effect_chance(env) {
let ptr = env.allocate_temp(*chance);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*chance = *ptr.value();
}
}
fn change_incoming_effect_chance(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, chance: &mut f32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingEffectChance) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_incoming_effect_chance(env) {
let ptr = env.allocate_temp(*chance);
let target = target.as_ref();
let w_ptr = ptr.wasm_ptr;
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*chance = *ptr.value();
}
}
fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_secondary_effect(env) {
let target = target.as_ref();
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit);
}
}
fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc<Pokemon>) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHits) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_after_hits(env) {
let target = target.as_ref();
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target));
}
}
fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_self_switch(env) {
let ptr = env.allocate_temp(*prevent);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
}
fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_opponent_switch(env) {
let ptr = env.allocate_temp(*prevent);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
}
fn on_fail(&self, pokemon: &Pokemon) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_fail(env) {
call_func!(func, env, self, ex_ref!(env, pokemon));
}
}
fn on_opponent_fail(&self, pokemon: &Pokemon) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_opponent_fail(env) {
call_func!(func, env, self, ex_ref!(env, pokemon));
}
}
fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfRunAway) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_self_run_away(env) {
let ptr = env.allocate_temp(*prevent);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
}
fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventOpponentRunAway) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_opponent_run_away(env) {
let ptr = env.allocate_temp(*prevent);
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
}
fn on_end_turn(&self) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnEndTurn) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_end_turn(env) {
call_func!(func, env, self);
}
}
fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnDamage) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_damage(env) {
let r = ex_ref!(env, pokemon);
call_func!(func, env, self, r, source as u8, old_health, new_health);
}
}
fn on_faint(&self, pokemon: &Pokemon, source: DamageSource) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaint) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_faint(env) {
call_func!(func, env, self, ex_ref!(env, pokemon), source as u8);
}
}
fn on_switch_in(&self, pokemon: &Pokemon) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnSwitchIn) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_switch_in(env) {
call_func!(func, env, self, ex_ref!(env, pokemon));
}
}
fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &Item) {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHeldItemConsume) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_after_held_item_consume(env) {
call_func!(func, env, self, ex_ref!(env, pokemon), ex_ref!(env, item));
}
}
fn change_experience_gained(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, amount: &mut u32) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeExperienceGain) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_experience_gained(env) {
let ptr = env.allocate_temp(*amount);
let fainted_mon = ex_ref!(env, fainted_mon);
let winning_mon = ex_ref!(env, winning_mon);
call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr);
*amount = *ptr.value();
}
}
fn share_experience(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, shares: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::DoesShareExperience) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().share_experience(env) {
let ptr = env.allocate_temp(*shares);
let fainted_mon = ex_ref!(env, fainted_mon);
let winning_mon = ex_ref!(env, winning_mon);
call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr);
*shares = *ptr.value();
}
}
fn block_weather(&self, battle: &Battle, blocked: &mut bool) {
if !self.has_capability(&WebAssemblyScriptCapabilities::BlockWeather) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().block_weather(env) {
let ptr = env.allocate_temp(*blocked);
call_func!(func, env, self, ex_ref!(env, battle), ptr.wasm_ptr);
*blocked = *ptr.value();
}
}
fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &Item, modifier: &mut u8) {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCaptureRate) {
return;
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_capture_rate_bonus(env) {
let ptr = env.allocate_temp(*modifier);
let target = ex_ref!(env, target);
let pokeball = ex_ref!(env, pokeball);
call_func!(func, env, self, target, pokeball, ptr.wasm_ptr);
*modifier = *ptr.value();
}
}
fn as_any(&self) -> &dyn Any {
self
}

View File

@ -1,14 +1,15 @@
use std::marker::PhantomData;
use std::sync::Arc;
use parking_lot::RwLock;
use paste::paste;
use crate::dynamic_data::{DynamicLibrary, TurnChoice};
use crate::dynamic_data::{Battle, DynamicLibrary, ExecutingMove, Pokemon, TurnChoice};
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
use crate::static_data::EffectParameter;
use crate::static_data::{EffectParameter, Item, TypeIdentifier};
use crate::StringKey;
use wasmer::TypedFunction;
use wasmer::{FromToNativeWasmType, TypedFunction};
/// A macro to generate the script function cache a bit easier.
macro_rules! script_function_cache {
@ -19,7 +20,7 @@ macro_rules! script_function_cache {
) => {
#[derive(Default)]
#[allow(unused_parens)]
pub struct ScriptFunctionCache {
pub(super) struct ScriptFunctionCache {
$(
$name: RwLock<Option<TypedFunction<(u32$(, $par_type)*), ()>>>,
)*
@ -60,11 +61,112 @@ macro_rules! script_function_cache {
}
}
/// Helper struct to indicate WASM client pointer type. This makes it harder to pass the wrong type
/// to a function by accident.
#[derive(Ord, PartialOrd, Eq, PartialEq)]
pub(super) struct WasmPtr<T> {
/// The memory size
v: u32,
/// Phantom data to store type.
_phantom: PhantomData<T>,
}
impl<T> Clone for WasmPtr<T> {
fn clone(&self) -> Self {
Self {
v: self.v,
_phantom: Default::default(),
}
}
}
impl<T> Copy for WasmPtr<T> {}
impl<T> From<u32> for WasmPtr<T> {
fn from(v: u32) -> Self {
Self {
v,
_phantom: Default::default(),
}
}
}
impl<T> From<WasmPtr<T>> for u32 {
fn from(v: WasmPtr<T>) -> Self {
v.v
}
}
unsafe impl<T> FromToNativeWasmType for WasmPtr<T> {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
Self {
v: native as u32,
_phantom: Default::default(),
}
}
fn to_native(self) -> Self::Native {
self.v as i32
}
}
script_function_cache! {
stack()
on_remove()
on_initialize(ExternRef<DynamicLibrary>, VecExternRef<EffectParameter>)
on_before_turn(ExternRef<TurnChoice>)
change_speed(ExternRef<TurnChoice>, u32)
change_priority(ExternRef<TurnChoice>, u32)
change_speed(ExternRef<TurnChoice>, WasmPtr<u32>)
change_priority(ExternRef<TurnChoice>, WasmPtr<i8>)
change_move(ExternRef<TurnChoice>, WasmPtr<ExternRef<StringKey>>)
change_number_of_hits(ExternRef<TurnChoice>, WasmPtr<u8>)
prevent_move(ExternRef<ExecutingMove>, WasmPtr<bool>)
fail_move(ExternRef<ExecutingMove>, WasmPtr<bool>)
stop_before_move(ExternRef<ExecutingMove>, WasmPtr<bool>)
on_before_move(ExternRef<ExecutingMove>)
fail_incoming_move(ExternRef<ExecutingMove>, ExternRef<Pokemon>, WasmPtr<bool>)
is_invulnerable(ExternRef<ExecutingMove>, ExternRef<Pokemon>, WasmPtr<bool>)
on_move_miss(ExternRef<ExecutingMove>, ExternRef<Pokemon>)
change_move_type(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<TypeIdentifier>)
change_effectiveness(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
block_critical(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
block_incoming_critical(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
change_accuracy(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u8>)
change_critical_stage(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u8>)
change_critical_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
change_stab_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
change_base_power(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u8>)
bypass_defensive_stat_boost(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
bypass_offensive_stat_boost(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
change_offensive_stat_value(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
change_defensive_stat_value(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
change_damage_stat_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
change_damage_modifier(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
change_damage(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
change_incoming_damage(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<u32>)
on_incoming_hit(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8)
on_opponent_faints(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8)
prevent_stat_boost_change(ExternRef<Pokemon>, u8, i8, u8, WasmPtr<bool>)
prevent_secondary_effect(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<bool>)
change_effect_chance(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
change_incoming_effect_chance(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8, WasmPtr<f32>)
on_secondary_effect(ExternRef<ExecutingMove>, ExternRef<Pokemon>, u8)
on_after_hits(ExternRef<ExecutingMove>, ExternRef<Pokemon>)
prevent_self_switch(ExternRef<TurnChoice>, WasmPtr<bool>)
prevent_opponent_switch(ExternRef<TurnChoice>, WasmPtr<bool>)
on_fail(ExternRef<Pokemon>)
on_opponent_fail(ExternRef<Pokemon>)
prevent_self_run_away(ExternRef<TurnChoice>, WasmPtr<bool>)
prevent_opponent_run_away(ExternRef<TurnChoice>, WasmPtr<bool>)
on_end_turn()
on_damage(ExternRef<Pokemon>, u8, u32, u32)
on_faint(ExternRef<Pokemon>, u8)
on_switch_in(ExternRef<Pokemon>)
on_after_held_item_consume(ExternRef<Pokemon>, ExternRef<Item>)
change_experience_gained(ExternRef<Pokemon>, ExternRef<Pokemon>, WasmPtr<u32>)
share_experience(ExternRef<Pokemon>, ExternRef<Pokemon>, WasmPtr<bool>)
block_weather(ExternRef<Battle>, WasmPtr<bool>)
change_capture_rate_bonus(ExternRef<Pokemon>, ExternRef<Item>, WasmPtr<u8>)
}

View File

@ -223,7 +223,7 @@ impl ScriptResolver for WebAssemblyScriptResolver {
owner as *mut u8,
script,
capabilities.clone(),
Arc::downgrade(&self.environment_data),
self.environment_data.clone(),
script_key.clone(),
))))
}
@ -324,7 +324,7 @@ impl WebAssemblyEnvironmentData {
}
///
pub fn script_function_cache(&self) -> &ScriptFunctionCache {
pub(super) fn script_function_cache(&self) -> &ScriptFunctionCache {
&self.script_function_cache
}
@ -361,7 +361,7 @@ impl WebAssemblyEnvironmentData {
/// Allocate a piece of memory inside WASM with a very short lifespan. This is mainly used for
/// rapid allocation of script function parameters, where WASM needs to write to a specific
/// pointer.
pub fn temp_allocate_mem_typed<T>(&self, value: T) -> AllocatedObject<T> {
pub(super) fn allocate_temp<T>(&self, value: T) -> AllocatedObject<T> {
self.temp_allocator.read().as_ref().unwrap().alloc::<T>(value)
}

View File

@ -1,3 +1,4 @@
use crate::script_implementations::wasm::script_function_cache::WasmPtr;
use std::mem::size_of;
use std::sync::atomic::{AtomicUsize, Ordering};
@ -40,7 +41,7 @@ impl TempWasmAllocator {
}
AllocatedObject::<T> {
ptr,
wasm_pointer: self.wasm_pointer + ptr_offset as u32,
wasm_ptr: (self.wasm_pointer + ptr_offset as u32).into(),
allocator: self as *const TempWasmAllocator,
}
}
@ -56,11 +57,11 @@ impl TempWasmAllocator {
}
/// A value allocated within WASM memory. Once this goes out of scope, it gets deallocated,
pub struct AllocatedObject<T> {
pub(super) struct AllocatedObject<T> {
/// The pointer in host memory to where the object lives.
pub ptr: *mut T,
/// The pointer in client memory to where the object lives.
pub wasm_pointer: u32,
pub wasm_ptr: WasmPtr<T>,
/// A raw pointer to the allocator we use, so we know where to deallocate to.
allocator: *const TempWasmAllocator,
}

View File

@ -46,6 +46,7 @@ pub enum BattleItemCategory {
/// An item is an object which the player can pick up, keep in their Bag, and use in some manner
#[derive(Debug)]
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
pub struct Item {
/// The name of the item.
name: StringKey,