use std::any::Any; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::Arc; use hashbrown::HashSet; use crate::dynamic_data::{ Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, ScriptOwnerData, 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, Item, Statistic, TypeIdentifier}; use crate::StringKey; /// A WebAssemblyScript is there to implement the Script trait within WebAssemblyScript. pub struct WebAssemblyScript { /// The unique identifier of the script. name: StringKey, /// Returns an atomic bool for internal marking of deletion. This is currently only specifically /// used for deletion of a script while we are holding a reference to it (i.e. executing a script /// hook on it). marked_for_deletion: AtomicBool, /// A script can be suppressed by other scripts. If a script is suppressed by at least one script /// we will not execute its methods. This holds the number of suppressions on the script. suppressed_count: AtomicUsize, /// The owner of this script (where the script is attached to) owner: ScriptOwnerData, /// Pointer inside WebAssembly memory where the data is for this script. self_ptr: u32, /// Capabilities define which functions we actually implement. capabilities: Arc>, /// The global runtime environment data. environment: Arc, } impl WebAssemblyScript { /// Instantiates a new WebAssemblyScript. pub fn new( owner: ScriptOwnerData, self_ptr: u32, capabilities: Arc>, environment: Arc, name: StringKey, ) -> Self { Self { name, marked_for_deletion: Default::default(), suppressed_count: Default::default(), owner, self_ptr, capabilities, environment, } } pub(crate) fn get_wasm_pointer(&self) -> u32 { self.self_ptr } /// Check if this script implements a certain capability. #[inline(always)] fn has_capability(&self, cap: &WebAssemblyScriptCapabilities) -> bool { self.capabilities.contains(cap) } /// Gets the thing the script is owned by. pub fn get_owner(&self) -> &ScriptOwnerData { &self.owner } } /// 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 { fn name(&self) -> &StringKey { &self.name } fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion } fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count } fn stack(&self) { if !self.has_capability(&WebAssemblyScriptCapabilities::OnStack) { return; } 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.has_capability(&WebAssemblyScriptCapabilities::OnRemove) { return; } 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.has_capability(&WebAssemblyScriptCapabilities::Initialize) { return; } 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.has_capability(&WebAssemblyScriptCapabilities::OnBeforeTurn) { return; } 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.has_capability(&WebAssemblyScriptCapabilities::ChangeSpeed) { return; } let env = &self.environment; if let Some(func) = env.script_function_cache().change_speed(env) { let ptr = env.allocate_temp::(*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.has_capability(&WebAssemblyScriptCapabilities::ChangePriority) { return; } let env = &self.environment; if let Some(func) = env.script_function_cache().change_priority(env) { let ptr = env.allocate_temp::(*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::>(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::(*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::(*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::(*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::(*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, 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::(*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, 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::(*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) { 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, 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::(*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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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) { 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 } fn as_any_mut(&mut self) -> &mut dyn Any { self } }