Support for errors from scripts through separate script error handling.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2023-04-21 10:35:46 +02:00
parent a6f4b1d5c5
commit eb68977290
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
9 changed files with 677 additions and 222 deletions

View File

@ -353,7 +353,7 @@ impl Battle {
if let Some(script) = self.weather.get() {
let lock = script.read();
Ok(Some(
lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name().clone(),
lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name()?.clone(),
))
} else {
Ok(None)

View File

@ -535,11 +535,18 @@ impl Pokemon {
.library
.load_script(self.into(), ScriptCategory::Ability, ability.name())?;
if let Some(ability_script) = ability_script {
self.ability_script
let script_result = self
.ability_script
.set(ability_script)
.as_ref()
// Ensure the ability script gets initialized with the parameters for the ability.
.on_initialize(&self.library, ability.parameters().to_vec())
.on_initialize(&self.library, ability.parameters().to_vec());
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
} else {
self.ability_script.clear();
}

View File

@ -1,5 +1,5 @@
use anyhow::{anyhow, Result};
use std::sync::{Arc, Weak};
use std::sync::{Arc, LazyLock, Weak};
use parking_lot::RwLock;
@ -33,7 +33,12 @@ macro_rules! script_hook {
if let Some(script) = script {
if let Some(script) = script.read().as_deref() {
if !script.is_suppressed() {
script.$hook_name($($parameters),*);
match script.$hook_name($($parameters),*) {
Ok(_) => {}
Err(e) => {
$crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
}
}
}
@ -53,7 +58,12 @@ macro_rules! run_scripts {
let s = s.read();
if let Some(s) = s.deref() {
if !s.is_suppressed() {
s.$hook_name($($parameters),*);
match s.$hook_name($($parameters),*) {
Ok(_) => {}
Err(e) => {
$crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
}
}
}
@ -65,8 +75,13 @@ macro_rules! run_scripts {
if let Some(s) = s.get() {
let s = s.read();
if let Some(s) = s.deref() {
if !s.is_suppressed() && set.has(s.name()) {
s.$hook_name($($parameters),*);
if !s.is_suppressed() && set.has(s.name()?) {
match s.$hook_name($($parameters),*) {
Ok(_) => {}
Err(e) => {
$crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
}
}
}
@ -78,6 +93,35 @@ macro_rules! run_scripts {
};
}
/// When a script encounters an error, this function should be called. This allows the implementation
/// to define how to handle script errors.
pub(crate) fn handle_script_error(e: &anyhow_ext::Error) {
unsafe {
(HANDLE_SCRIPT_ERROR_IMPL.read())(e);
}
}
/// The type of the function that is called when a script encounters an error.
type ScriptErrorFunction = Box<dyn Fn(&anyhow_ext::Error)>;
/// The currently set script error handler.
static mut HANDLE_SCRIPT_ERROR_IMPL: LazyLock<RwLock<ScriptErrorFunction>> =
LazyLock::new(|| RwLock::new(Box::new(default_script_error_handler)));
/// The default script error handler. This will panic with the error message. As this can (and should)
/// be changed by the implementation, we allow panics here.
#[allow(clippy::panic)]
fn default_script_error_handler(e: &anyhow_ext::Error) {
panic!("Script error: {}", e);
}
/// Sets the script error handler to the given function.
pub fn set_script_error_handler(f: ScriptErrorFunction) {
unsafe {
*HANDLE_SCRIPT_ERROR_IMPL.write() = f;
}
}
/// The script source data is the basic data required for any script source.
#[derive(Default, Debug)]
pub struct ScriptSourceData {
@ -279,8 +323,8 @@ mod tests {
}
impl Script for TestScript {
fn name(&self) -> &StringKey {
&self.name
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn get_marked_for_deletion(&self) -> &AtomicBool {
@ -295,8 +339,9 @@ mod tests {
fn remove_suppression(&self) {}
fn stack(&self) {
fn stack(&self) -> Result<()> {
self.test_count.fetch_add(1, Ordering::SeqCst);
Ok(())
}
fn as_any(&self) -> &dyn Any {
@ -314,7 +359,7 @@ mod tests {
let scripts = vec![ScriptWrapper::from(&script)];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
v.get().unwrap().read().as_ref().unwrap().stack().unwrap();
}
let a = script.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), 1);
@ -328,7 +373,7 @@ mod tests {
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
v.get().unwrap().read().as_ref().unwrap().stack().unwrap();
}
let a = script.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), i);
@ -347,7 +392,7 @@ mod tests {
];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
v.get().unwrap().read().as_ref().unwrap().stack().unwrap();
}
let a = script1.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), 1);
@ -371,7 +416,7 @@ mod tests {
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
v.get().unwrap().read().as_ref().unwrap().stack().unwrap();
}
let a = script1.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), i);
@ -385,38 +430,38 @@ mod tests {
#[test]
fn script_aggregator_property_iterates_script_set() {
let set = Arc::new(ScriptSet::default());
set.add(Arc::new(TestScript::new_with_name("test_a")));
set.add(Arc::new(TestScript::new_with_name("test_b")));
set.add(Arc::new(TestScript::new_with_name("test_c")));
set.add(Arc::new(TestScript::new_with_name("test_a"))).unwrap();
set.add(Arc::new(TestScript::new_with_name("test_b"))).unwrap();
set.add(Arc::new(TestScript::new_with_name("test_c"))).unwrap();
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
v.get().unwrap().read().as_ref().unwrap().stack().unwrap();
}
let s = set.at(0).unwrap();
let s = s.get_as::<TestScript>().unwrap();
assert_eq!(s.test_count.load(Ordering::Relaxed), i);
assert_eq!(s.name().str(), "test_a");
assert_eq!(s.name().unwrap().str(), "test_a");
let s = set.at(1).unwrap();
let s = s.get_as::<TestScript>().unwrap();
assert_eq!(s.test_count.load(Ordering::Relaxed), i);
assert_eq!(s.name().str(), "test_b");
assert_eq!(s.name().unwrap().str(), "test_b");
let s = set.at(2).unwrap();
let s = s.get_as::<TestScript>().unwrap();
assert_eq!(s.test_count.load(Ordering::Relaxed), i);
assert_eq!(s.name().str(), "test_c");
assert_eq!(s.name().unwrap().str(), "test_c");
}
}
#[test]
fn script_aggregator_property_iterates_script_set_when_removing_last() {
let set = Arc::new(ScriptSet::default());
set.add(Arc::new(TestScript::new_with_name("test_a")));
set.add(Arc::new(TestScript::new_with_name("test_b")));
set.add(Arc::new(TestScript::new_with_name("test_c")));
set.add(Arc::new(TestScript::new_with_name("test_a"))).unwrap();
set.add(Arc::new(TestScript::new_with_name("test_b"))).unwrap();
set.add(Arc::new(TestScript::new_with_name("test_c"))).unwrap();
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
@ -431,6 +476,7 @@ mod tests {
.as_ref()
.unwrap()
.name()
.unwrap()
.str(),
"test_a"
);
@ -445,6 +491,7 @@ mod tests {
.as_ref()
.unwrap()
.name()
.unwrap()
.str(),
"test_b"
);
@ -456,9 +503,9 @@ mod tests {
#[test]
fn script_aggregator_property_iterates_script_set_when_removing_middle() {
let set = Arc::new(ScriptSet::default());
set.add(Arc::new(TestScript::new_with_name("test_a")));
set.add(Arc::new(TestScript::new_with_name("test_b")));
set.add(Arc::new(TestScript::new_with_name("test_c")));
set.add(Arc::new(TestScript::new_with_name("test_a"))).unwrap();
set.add(Arc::new(TestScript::new_with_name("test_b"))).unwrap();
set.add(Arc::new(TestScript::new_with_name("test_c"))).unwrap();
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
@ -473,6 +520,7 @@ mod tests {
.as_ref()
.unwrap()
.name()
.unwrap()
.str(),
"test_a"
);
@ -490,6 +538,7 @@ mod tests {
.as_ref()
.unwrap()
.name()
.unwrap()
.str(),
"test_c"
);

View File

@ -23,7 +23,7 @@ use crate::StringKey;
pub trait Script: Send + Sync {
/// The name of a script is its unique identifier. This should generally be set on load, and be
/// the same as the key that was used to load it.
fn name(&self) -> &StringKey;
fn name(&self) -> Result<&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).
@ -61,50 +61,80 @@ pub trait Script: Send + Sync {
/// 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) {}
fn stack(&self) -> Result<()> {
Ok(())
}
/// This function is ran when this script stops being in effect, and is removed from its owner.
fn on_remove(&self) {}
fn on_remove(&self) -> Result<()> {
Ok(())
}
/// This function is ran when this script starts being in effect.
fn on_initialize(&self, _library: &Arc<dyn DynamicLibrary>, _pars: Vec<EffectParameter>) {}
fn on_initialize(&self, _library: &Arc<dyn DynamicLibrary>, _pars: Vec<EffectParameter>) -> Result<()> {
Ok(())
}
/// 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) -> Result<()> {
Ok(())
}
/// 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) {}
fn change_speed(&self, _choice: &TurnChoice, _speed: &mut u32) -> Result<()> {
Ok(())
}
/// 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) {}
fn change_priority(&self, _choice: &TurnChoice, _priority: &mut i8) -> Result<()> {
Ok(())
}
/// 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) {}
fn change_move(&self, _choice: &TurnChoice, _move_name: &mut StringKey) -> Result<()> {
Ok(())
}
/// 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) {}
fn change_number_of_hits(&self, _choice: &TurnChoice, _number_of_hits: &mut u8) -> Result<()> {
Ok(())
}
/// 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) {}
fn prevent_move(&self, _move: &ExecutingMove, _prevent: &mut bool) -> Result<()> {
Ok(())
}
/// 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) {}
fn fail_move(&self, _move: &ExecutingMove, _fail: &mut bool) -> Result<()> {
Ok(())
}
/// 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) {}
fn stop_before_move(&self, _move: &ExecutingMove, _stop: &mut bool) -> Result<()> {
Ok(())
}
/// This function runs just before the move starts its execution.
fn on_before_move(&self, _move: &ExecutingMove) {}
fn on_before_move(&self, _move: &ExecutingMove) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _fail: &mut bool) {}
fn fail_incoming_move(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _fail: &mut bool) -> Result<()> {
Ok(())
}
/// This function allows a script to make its owner invulnerable to an incoming move.
fn is_invulnerable(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _invulnerable: &mut bool) {}
fn is_invulnerable(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _invulnerable: &mut bool) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>) {}
fn on_move_miss(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>) -> Result<()> {
Ok(())
}
/// This function allows the script to change the actual type that is used for the move on a target.
fn change_move_type(
&self,
@ -112,12 +142,29 @@ pub trait Script: Send + Sync {
_target: &Arc<Pokemon>,
_hit: u8,
_move_type: &mut TypeIdentifier,
) {
) -> Result<()> {
Ok(())
}
/// This function allows the script to change how effective a move is on a target.
fn change_effectiveness(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _effectiveness: &mut f32) {}
fn change_effectiveness(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_effectiveness: &mut f32,
) -> Result<()> {
Ok(())
}
/// This function allows a script to block an outgoing move from being critical.
fn block_critical(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _block_critical: &mut bool) {}
fn block_critical(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_block_critical: &mut bool,
) -> Result<()> {
Ok(())
}
/// This function allows a script to block an incoming move from being critical.
fn block_incoming_critical(
&self,
@ -125,33 +172,104 @@ pub trait Script: Send + Sync {
_target: &Arc<Pokemon>,
_hit: u8,
_block_critical: &mut bool,
) {
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _accuracy: &mut u8) {}
fn change_accuracy(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_accuracy: &mut u8,
) -> Result<()> {
Ok(())
}
/// This function allows a script to change the critical stage of the move used.
fn change_critical_stage(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _stage: &mut u8) {}
fn change_critical_stage(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_stage: &mut u8,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _modifier: &mut f32) {}
fn change_critical_modifier(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_modifier: &mut f32,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _modifier: &mut f32) {}
fn change_stab_modifier(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_modifier: &mut f32,
) -> Result<()> {
Ok(())
}
/// This function allows a script to change the effective base power of a move hit.
fn change_base_power(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _base_power: &mut u8) {}
fn change_base_power(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_base_power: &mut u8,
) -> Result<()> {
Ok(())
}
/// This function allows a script to bypass defensive stat boosts for a move hit.
fn bypass_defensive_stat_boost(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _bypass: &mut bool) {
fn bypass_defensive_stat_boost(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_bypass: &mut bool,
) -> Result<()> {
Ok(())
}
/// This function allows a script to bypass offensive stat boosts for a move hit.
fn bypass_offensive_stat_boost(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _bypass: &mut bool) {
fn bypass_offensive_stat_boost(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_bypass: &mut bool,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _amount: &mut u32) {}
fn change_offensive_stat_value(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_amount: &mut u32,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _amount: &mut u32) {}
fn change_defensive_stat_value(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_amount: &mut u32,
) -> Result<()> {
Ok(())
}
/// This function allows a script to change the raw modifier we retrieved from the stats of the
/// defender and attacker.
@ -161,19 +279,42 @@ pub trait Script: Send + Sync {
_target: &Arc<Pokemon>,
_hit: u8,
_modifier: &mut f32,
) {
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _modifier: &mut f32) {}
fn change_damage_modifier(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_modifier: &mut f32,
) -> Result<()> {
Ok(())
}
/// This function allows a script to modify the outgoing damage done by a move.
fn change_damage(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _damage: &mut u32) {}
fn change_damage(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _damage: &mut u32) -> Result<()> {
Ok(())
}
/// This function allows a script to modify the incoming damage done by a move.
fn change_incoming_damage(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8, _damage: &mut u32) {}
fn change_incoming_damage(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_damage: &mut u32,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8) {}
fn on_incoming_hit(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8) -> Result<()> {
Ok(())
}
/// This function triggers when an opponent on the field faints.
fn on_opponent_faints(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8) {}
fn on_opponent_faints(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8) -> Result<()> {
Ok(())
}
/// 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(
@ -183,21 +324,46 @@ pub trait Script: Send + Sync {
_amount: i8,
_self_inflicted: bool,
_prevent: &mut bool,
) {
) -> Result<()> {
Ok(())
}
/// 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) {}
fn change_stat_boost_change(
&self,
_target: &Pokemon,
_stat: Statistic,
_self_inflicted: bool,
_amount: &mut i8,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _prevent: &mut bool) {}
fn prevent_secondary_effect(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_prevent: &mut bool,
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8, _chance: &mut f32) {}
fn change_effect_chance(
&self,
_move: &ExecutingMove,
_target: &Arc<Pokemon>,
_hit: u8,
_chance: &mut f32,
) -> Result<()> {
Ok(())
}
/// 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
@ -208,53 +374,93 @@ pub trait Script: Send + Sync {
_target: &Arc<Pokemon>,
_hit: u8,
_chance: &mut f32,
) {
) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>, _hit: u8) {}
fn on_secondary_effect(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>, _hit: u8) -> Result<()> {
Ok(())
}
/// 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: &Arc<Pokemon>) {}
fn on_after_hits(&self, _move: &ExecutingMove, _target: &Arc<Pokemon>) -> Result<()> {
Ok(())
}
/// 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) {}
fn prevent_self_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> {
Ok(())
}
/// This function allows the prevention of switching for any opponent.
fn prevent_opponent_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) {}
fn prevent_opponent_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> {
Ok(())
}
/// This function is called on a move and its parents when the move fails.
fn on_fail(&self, _target: &Pokemon) {}
fn on_fail(&self, _target: &Pokemon) -> Result<()> {
Ok(())
}
/// This function is called on a script when an opponent fails.
fn on_opponent_fail(&self, _target: &Pokemon) {}
fn on_opponent_fail(&self, _target: &Pokemon) -> Result<()> {
Ok(())
}
/// This function allows preventing the running away of the Pokemon its attached to
fn prevent_self_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) {}
fn prevent_self_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> {
Ok(())
}
/// 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) {}
fn prevent_opponent_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) -> Result<()> {
Ok(())
}
/// 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) {}
fn on_end_turn(&self) -> Result<()> {
Ok(())
}
/// 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) {}
fn on_damage(&self, _pokemon: &Pokemon, _source: DamageSource, _old_health: u32, _new_health: u32) -> Result<()> {
Ok(())
}
/// This function is triggered on a Pokemon and its parents when the given Pokemon faints.
fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) {}
fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) -> Result<()> {
Ok(())
}
/// 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) {}
fn on_switch_in(&self, _pokemon: &Pokemon) -> Result<()> {
Ok(())
}
/// 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: &dyn Item) {}
fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &dyn Item) -> Result<()> {
Ok(())
}
/// 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) {}
fn change_experience_gained(
&self,
_fainted_mon: &Pokemon,
_winning_mon: &Pokemon,
_amount: &mut u32,
) -> Result<()> {
Ok(())
}
/// 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) {}
fn share_experience(&self, _fainted_mon: &Pokemon, _winning_mon: &Pokemon, _shares: &mut bool) -> Result<()> {
Ok(())
}
/// 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) {}
fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) -> Result<()> {
Ok(())
}
/// 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: &dyn Item, _modifier: &mut u8) {}
fn change_capture_rate_bonus(&self, _target: &Pokemon, _pokeball: &dyn Item, _modifier: &mut u8) -> Result<()> {
Ok(())
}
/// Helper function to turn the self into an Any for downcasting.
fn as_any(&self) -> &dyn Any;
@ -319,7 +525,13 @@ impl ScriptContainer {
/// separate thread, and be replaced immediately after the script stops being active.
pub fn set(&self, script: Arc<dyn Script>) -> Arc<dyn Script> {
if let Some(v) = self.script.read().deref() {
v.on_remove();
let script_result = v.on_remove();
match script_result {
Ok(_) => {}
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
v.mark_for_deletion();
}
// If we have a lock on the script, we can't replace it now. Wait until we can get the lock,
@ -342,7 +554,13 @@ impl ScriptContainer {
/// we just mark it as deleted. If we do this we remove the script later on.
pub fn clear(&self) {
if let Some(v) = self.script.read().deref() {
v.on_remove();
let script_result = v.on_remove();
match script_result {
Ok(_) => {}
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
v.mark_for_deletion();
}
if !self.script.is_locked() {
@ -418,8 +636,8 @@ mod tests {
unsafe impl Send for TestScript {}
impl Script for TestScript {
fn name(&self) -> &StringKey {
&self.name
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn get_marked_for_deletion(&self) -> &AtomicBool {
@ -430,8 +648,9 @@ mod tests {
&self.suppressed_count
}
fn stack(&self) {
fn stack(&self) -> Result<()> {
unsafe { self.container.load(Ordering::Relaxed).as_ref().unwrap().clear() }
Ok(())
}
fn as_any(&self) -> &dyn Any {
@ -458,7 +677,7 @@ mod tests {
drop(w);
// Initialize with the script being taken as read lock. This prevents the script from actually
// removing itself, as it's still doing things.
container.script.read().as_ref().unwrap().stack();
container.script.read().as_ref().unwrap().stack().unwrap();
// If we now try and get the script, it will be none the first time. This has the side effect
// of actually disposing of the script.
assert!(container.get().is_none());
@ -497,8 +716,8 @@ mod tests {
unsafe impl Send for ReplaceTestScript {}
impl Script for ReplaceTestScript {
fn name(&self) -> &StringKey {
&self.name
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn get_marked_for_deletion(&self) -> &AtomicBool {
@ -544,7 +763,10 @@ mod tests {
h.join().unwrap();
}
assert_eq!(container.script.read().as_ref().unwrap().name(), &"script2".into());
assert_eq!(
container.script.read().as_ref().unwrap().name().unwrap(),
&"script2".into()
);
}
}

View File

@ -19,22 +19,22 @@ pub struct ScriptSet {
impl ScriptSet {
/// Adds a script to the set. If the script with that name already exists in this set, this
/// makes that script stack instead. The return value here is that script.
pub fn add(&self, script: Arc<dyn Script>) -> ScriptContainer {
if let Some(lock) = self.scripts.read().get(script.name()) {
pub fn add(&self, script: Arc<dyn Script>) -> Result<ScriptContainer> {
if let Some(lock) = self.scripts.read().get(script.name()?) {
if let Some(existing) = lock.get() {
let existing = existing.read();
if let Some(v) = &*existing {
v.stack();
return lock.clone();
v.stack()?;
return Ok(lock.clone());
}
}
}
// If the existing script does not exist, or is not a script, we can just add the new one.
let name = script.name().clone();
let name = script.name()?.clone();
let container = ScriptContainer::new(script);
self.scripts.write().insert(name, container.clone());
container
Ok(container)
}
/// Adds a script with a name to the set. If the script with that name already exists in this
@ -47,14 +47,20 @@ impl ScriptSet {
if let Some(existing) = lock.get() {
let existing = existing.read();
if let Some(v) = &*existing {
v.stack();
let script_result = v.stack();
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
return Ok(Some(lock.clone()));
}
}
}
let script = instantiation()?;
if let Some(script) = script {
let name = script.name().clone();
let name = script.name()?.clone();
let arc = ScriptContainer::new(script);
self.scripts.write().insert(name, arc.clone());
Ok(Some(arc))
@ -74,7 +80,14 @@ impl ScriptSet {
if let Some(script) = value {
if let Some(script) = script.get() {
let script = script.read();
script.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.on_remove();
let script_result = script.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.on_remove();
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
script
.as_ref()
.ok_or(PkmnError::UnableToAcquireLock)?
@ -90,7 +103,14 @@ impl ScriptSet {
if let Some(script) = script.1.get() {
let script = script.read();
if let Some(script) = &*script {
script.on_remove();
let script_result = script.on_remove();
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
script.mark_for_deletion();
}
}

View File

@ -31,7 +31,7 @@ pub trait VolatileScriptsOwner {
/// Adds a volatile script by name.
fn add_volatile_script_with_script(&self, script: Arc<dyn Script>) -> Result<Option<ScriptContainer>> {
self.volatile_scripts()
.stack_or_add(&script.name().clone(), &|| Ok(Some(script.clone())))
.stack_or_add(&script.name()?.clone(), &|| Ok(Some(script.clone())))
}
/// Removes a volatile script by name.

View File

@ -1,6 +1,17 @@
use std::ffi::{c_char, CString};
/// The foreign function interfaces for the turn choices
mod choices;
/// The foreign function interfaces for the dynamic data libraries
mod libraries;
/// The foreign function interfaces for the dynamic data models
mod models;
/// The library the battle uses for handling.
#[no_mangle]
#[allow(clippy::unwrap_used)]
extern "C" fn set_script_error_handler(func: unsafe extern "C" fn(*mut c_char)) {
crate::dynamic_data::set_script_error_handler(Box::new(move |e: &anyhow_ext::Error| unsafe {
func(CString::new(e.to_string()).unwrap().into_raw())
}));
}

View File

@ -1,3 +1,4 @@
use anyhow_ext::Result;
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::Arc;
@ -93,8 +94,8 @@ macro_rules! vec_ex_ref {
}
impl Script for WebAssemblyScript {
fn name(&self) -> &StringKey {
&self.name
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn get_marked_for_deletion(&self) -> &AtomicBool {
@ -105,50 +106,54 @@ impl Script for WebAssemblyScript {
&self.suppressed_count
}
fn stack(&self) {
fn stack(&self) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnStack) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().stack(env) {
call_func!(func, env, self);
}
Ok(())
}
fn on_remove(&self) {
fn on_remove(&self) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnRemove) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_remove(env) {
call_func!(func, env, self);
}
Ok(())
}
fn on_initialize(&self, library: &Arc<dyn DynamicLibrary>, pars: Vec<EffectParameter>) {
fn on_initialize(&self, library: &Arc<dyn DynamicLibrary>, pars: Vec<EffectParameter>) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::Initialize) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn on_before_turn(&self, choice: &TurnChoice) {
fn on_before_turn(&self, choice: &TurnChoice) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeTurn) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) {
fn change_speed(&self, choice: &TurnChoice, speed: &mut u32) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSpeed) {
return;
return Ok(());
}
let env = &self.environment;
@ -157,11 +162,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*speed = *ptr.value();
}
Ok(())
}
fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) {
fn change_priority(&self, choice: &TurnChoice, priority: &mut i8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangePriority) {
return;
return Ok(());
}
let env = &self.environment;
@ -170,11 +176,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*priority = *ptr.value();
}
Ok(())
}
fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) {
fn change_move(&self, choice: &TurnChoice, move_name: &mut StringKey) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMove) {
return;
return Ok(());
}
let env = &self.environment;
@ -184,11 +191,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*move_name = ptr.value().value(env).unwrap().clone();
}
Ok(())
}
fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) {
fn change_number_of_hits(&self, choice: &TurnChoice, number_of_hits: &mut u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeNumberOfHits) {
return;
return Ok(());
}
let env = &self.environment;
@ -197,11 +205,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*number_of_hits = *ptr.value();
}
Ok(())
}
fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) {
fn prevent_move(&self, mv: &ExecutingMove, prevent: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventMove) {
return;
return Ok(());
}
let env = &self.environment;
@ -210,11 +219,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr);
*prevent = *ptr.value();
}
Ok(())
}
fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) {
fn fail_move(&self, mv: &ExecutingMove, fail: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::FailMove) {
return;
return Ok(());
}
let env = &self.environment;
@ -223,11 +233,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr);
*fail = *ptr.value();
}
Ok(())
}
fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) {
fn stop_before_move(&self, mv: &ExecutingMove, stop: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::StopBeforeMove) {
return;
return Ok(());
}
let env = &self.environment;
@ -236,21 +247,23 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ptr.wasm_ptr);
*stop = *ptr.value();
}
Ok(())
}
fn on_before_move(&self, mv: &ExecutingMove) {
fn on_before_move(&self, mv: &ExecutingMove) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnBeforeMove) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, fail: &mut bool) {
fn fail_incoming_move(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, fail: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::FailIncomingMove) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().fail_incoming_move(env) {
@ -265,11 +278,12 @@ impl Script for WebAssemblyScript {
);
*fail = *ptr.value();
}
Ok(())
}
fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, invulnerable: &mut bool) {
fn is_invulnerable(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, invulnerable: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::IsInvulnerable) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().is_invulnerable(env) {
@ -284,21 +298,29 @@ impl Script for WebAssemblyScript {
);
*invulnerable = *ptr.value();
}
Ok(())
}
fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc<Pokemon>) {
fn on_move_miss(&self, mv: &ExecutingMove, target: &Arc<Pokemon>) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnMoveMiss) {
return;
return Ok(());
}
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()));
}
Ok(())
}
fn change_move_type(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, move_type: &mut TypeIdentifier) {
fn change_move_type(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
move_type: &mut TypeIdentifier,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeMoveType) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_move_type(env) {
@ -308,11 +330,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*move_type = *ptr.value();
}
Ok(())
}
fn change_effectiveness(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, effectiveness: &mut f32) {
fn change_effectiveness(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
effectiveness: &mut f32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectiveness) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_effectiveness(env) {
@ -322,11 +351,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*effectiveness = *ptr.value();
}
Ok(())
}
fn block_critical(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, block_critical: &mut bool) {
fn block_critical(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
block_critical: &mut bool,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::BlockCritical) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().block_critical(env) {
@ -336,11 +372,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*block_critical = *ptr.value();
}
Ok(())
}
fn block_incoming_critical(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, block_critical: &mut bool) {
fn block_incoming_critical(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
block_critical: &mut bool,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::BlockIncomingCritical) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().block_incoming_critical(env) {
@ -350,11 +393,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*block_critical = *ptr.value();
}
Ok(())
}
fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, accuracy: &mut u8) {
fn change_accuracy(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, accuracy: &mut u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeAccuracy) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_accuracy(env) {
@ -364,11 +408,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*accuracy = *ptr.value();
}
Ok(())
}
fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, stage: &mut u8) {
fn change_critical_stage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, stage: &mut u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalStage) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_critical_stage(env) {
@ -378,11 +423,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*stage = *ptr.value();
}
Ok(())
}
fn change_critical_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
fn change_critical_modifier(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
modifier: &mut f32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCriticalModifier) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_critical_modifier(env) {
@ -392,11 +444,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
Ok(())
}
fn change_stab_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
fn change_stab_modifier(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
modifier: &mut f32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeSTABModifier) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_stab_modifier(env) {
@ -406,11 +465,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
Ok(())
}
fn change_base_power(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, base_power: &mut u8) {
fn change_base_power(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, base_power: &mut u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeBasePower) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_base_power(env) {
@ -420,11 +480,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*base_power = *ptr.value();
}
Ok(())
}
fn bypass_defensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, bypass: &mut bool) {
fn bypass_defensive_stat_boost(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
bypass: &mut bool,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::BypassDefensiveStat) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().bypass_defensive_stat_boost(env) {
@ -434,11 +501,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*bypass = *ptr.value();
}
Ok(())
}
fn bypass_offensive_stat_boost(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, bypass: &mut bool) {
fn bypass_offensive_stat_boost(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
bypass: &mut bool,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::BypassOffensiveStat) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().bypass_offensive_stat_boost(env) {
@ -448,11 +522,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*bypass = *ptr.value();
}
Ok(())
}
fn change_offensive_stat_value(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, amount: &mut u32) {
fn change_offensive_stat_value(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
amount: &mut u32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_offensive_stat_value(env) {
@ -462,11 +543,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*amount = *ptr.value();
}
Ok(())
}
fn change_defensive_stat_value(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, amount: &mut u32) {
fn change_defensive_stat_value(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
amount: &mut u32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeOffensiveStatValue) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_defensive_stat_value(env) {
@ -476,11 +564,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*amount = *ptr.value();
}
Ok(())
}
fn change_damage_stat_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
fn change_damage_stat_modifier(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
modifier: &mut f32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeStatModifier) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_damage_stat_modifier(env) {
@ -490,11 +585,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
Ok(())
}
fn change_damage_modifier(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, modifier: &mut f32) {
fn change_damage_modifier(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
modifier: &mut f32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamageModifier) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_damage_modifier(env) {
@ -504,11 +606,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*modifier = *ptr.value();
}
Ok(())
}
fn change_damage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, damage: &mut u32) {
fn change_damage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, damage: &mut u32) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeDamage) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_damage(env) {
@ -518,11 +621,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*damage = *ptr.value();
}
Ok(())
}
fn change_incoming_damage(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, damage: &mut u32) {
fn change_incoming_damage(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
damage: &mut u32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingDamage) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_incoming_damage(env) {
@ -532,28 +642,31 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*damage = *ptr.value();
}
Ok(())
}
fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) {
fn on_incoming_hit(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnIncomingHit) {
return;
return Ok(());
}
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);
}
Ok(())
}
fn on_opponent_faints(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) {
fn on_opponent_faints(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) {
return;
return Ok(());
}
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);
}
Ok(())
}
fn prevent_stat_boost_change(
@ -563,9 +676,9 @@ impl Script for WebAssemblyScript {
amount: i8,
self_inflicted: bool,
prevent: &mut bool,
) {
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventStatBoostChange) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_stat_boost_change(env) {
@ -583,11 +696,18 @@ impl Script for WebAssemblyScript {
);
*prevent = *ptr.value();
}
Ok(())
}
fn prevent_secondary_effect(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, prevent: &mut bool) {
fn prevent_secondary_effect(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
prevent: &mut bool,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSecondaryEffects) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_secondary_effect(env) {
@ -597,11 +717,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*prevent = *ptr.value();
}
Ok(())
}
fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, chance: &mut f32) {
fn change_effect_chance(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, chance: &mut f32) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeEffectChance) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_effect_chance(env) {
@ -611,11 +732,18 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*chance = *ptr.value();
}
Ok(())
}
fn change_incoming_effect_chance(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8, chance: &mut f32) {
fn change_incoming_effect_chance(
&self,
mv: &ExecutingMove,
target: &Arc<Pokemon>,
hit: u8,
chance: &mut f32,
) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeIncomingEffectChance) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_incoming_effect_chance(env) {
@ -625,33 +753,36 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, mv), ex_ref!(env, target), hit, w_ptr);
*chance = *ptr.value();
}
Ok(())
}
fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) {
fn on_secondary_effect(&self, mv: &ExecutingMove, target: &Arc<Pokemon>, hit: u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaintingOpponent) {
return;
return Ok(());
}
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);
}
Ok(())
}
fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc<Pokemon>) {
fn on_after_hits(&self, mv: &ExecutingMove, target: &Arc<Pokemon>) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHits) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) {
fn prevent_self_switch(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_self_switch(env) {
@ -659,11 +790,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
Ok(())
}
fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) {
fn prevent_opponent_switch(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfSwitch) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_opponent_switch(env) {
@ -671,31 +803,34 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
Ok(())
}
fn on_fail(&self, pokemon: &Pokemon) {
fn on_fail(&self, pokemon: &Pokemon) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_fail(env) {
call_func!(func, env, self, ex_ref!(env, pokemon));
}
Ok(())
}
fn on_opponent_fail(&self, pokemon: &Pokemon) {
fn on_opponent_fail(&self, pokemon: &Pokemon) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFail) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) {
fn prevent_self_run_away(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventSelfRunAway) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_self_run_away(env) {
@ -703,11 +838,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
Ok(())
}
fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) {
fn prevent_opponent_run_away(&self, choice: &TurnChoice, prevent: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::PreventOpponentRunAway) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().prevent_opponent_run_away(env) {
@ -715,61 +851,67 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, choice), ptr.wasm_ptr);
*prevent = *ptr.value();
}
Ok(())
}
fn on_end_turn(&self) {
fn on_end_turn(&self) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnEndTurn) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().on_end_turn(env) {
call_func!(func, env, self);
}
Ok(())
}
fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32) {
fn on_damage(&self, pokemon: &Pokemon, source: DamageSource, old_health: u32, new_health: u32) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnDamage) {
return;
return Ok(());
}
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);
}
Ok(())
}
fn on_faint(&self, pokemon: &Pokemon, source: DamageSource) {
fn on_faint(&self, pokemon: &Pokemon, source: DamageSource) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnFaint) {
return;
return Ok(());
}
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);
}
Ok(())
}
fn on_switch_in(&self, pokemon: &Pokemon) {
fn on_switch_in(&self, pokemon: &Pokemon) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnSwitchIn) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &dyn Item) {
fn on_after_held_item_consume(&self, pokemon: &Pokemon, item: &dyn Item) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::OnAfterHeldItemConsume) {
return;
return Ok(());
}
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));
}
Ok(())
}
fn change_experience_gained(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, amount: &mut u32) {
fn change_experience_gained(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, amount: &mut u32) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeExperienceGain) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_experience_gained(env) {
@ -779,11 +921,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr);
*amount = *ptr.value();
}
Ok(())
}
fn share_experience(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, shares: &mut bool) {
fn share_experience(&self, fainted_mon: &Pokemon, winning_mon: &Pokemon, shares: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::DoesShareExperience) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().share_experience(env) {
@ -793,11 +936,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, fainted_mon, winning_mon, ptr.wasm_ptr);
*shares = *ptr.value();
}
Ok(())
}
fn block_weather(&self, battle: &Battle, blocked: &mut bool) {
fn block_weather(&self, battle: &Battle, blocked: &mut bool) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::BlockWeather) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().block_weather(env) {
@ -805,11 +949,12 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, ex_ref!(env, battle), ptr.wasm_ptr);
*blocked = *ptr.value();
}
Ok(())
}
fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &dyn Item, modifier: &mut u8) {
fn change_capture_rate_bonus(&self, target: &Pokemon, pokeball: &dyn Item, modifier: &mut u8) -> Result<()> {
if !self.has_capability(&WebAssemblyScriptCapabilities::ChangeCaptureRate) {
return;
return Ok(());
}
let env = &self.environment;
if let Some(func) = env.script_function_cache().change_capture_rate_bonus(env) {
@ -819,6 +964,7 @@ impl Script for WebAssemblyScript {
call_func!(func, env, self, target, pokeball, ptr.wasm_ptr);
*modifier = *ptr.value();
}
Ok(())
}
fn as_any(&self) -> &dyn Any {

View File

@ -119,7 +119,7 @@ fn validate_assurance() {
let mv = p1.learned_moves().read()[0].as_ref().unwrap().clone();
let choice = TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0));
script.on_before_turn(&choice);
script.on_before_turn(&choice).unwrap();
assert!(battle.sides()[1].has_volatile_script(&"assurance_data".into()));
let executing_move = ExecutingMove::new(
@ -131,19 +131,19 @@ fn validate_assurance() {
ScriptContainer::default(),
);
let mut v = 20_u8;
script.change_base_power(&executing_move, &p2, 0, &mut v);
script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap();
assert_eq!(v, 20_u8);
let s = battle.sides()[1].get_volatile_script(&"assurance_data".into());
let binding = s.as_ref().unwrap().get().unwrap().read();
let data_script = binding.as_ref().unwrap();
data_script.on_damage(&p2, DamageSource::Misc, 100, 50);
data_script.on_damage(&p2, DamageSource::Misc, 100, 50).unwrap();
let mut v = 20_u8;
script.change_base_power(&executing_move, &p2, 0, &mut v);
script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap();
assert_eq!(v, 40_u8);
data_script.on_end_turn();
data_script.on_end_turn().unwrap();
assert!(!battle.sides()[1].has_volatile_script(&"assurance_data".into()));
}