Support for errors from scripts through separate script error handling.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user