diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index 4d6470e..10e9682 100644 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -167,13 +167,6 @@ impl<'own, 'library> Battle<'own, 'library> { } } let number_of_hits = executing_move.number_of_hits(); - if number_of_hits == 0 { - script_hook!(on_move_miss, target, executing_move, target); - self.event_hook().trigger(Event::Miss { - user: executing_move.user().deref(), - }); - return Ok(()); - } let target_hit_stat = executing_move.get_index_of_target(target)?; for hit_index in 0..number_of_hits { @@ -252,6 +245,23 @@ impl<'own, 'library> Battle<'own, 'library> { ); hit_data.set_damage(damage); + let mut accuracy = executing_move.use_move().accuracy(); + script_hook!( + change_accuracy, + executing_move, + executing_move, + target, + hit_index, + &mut accuracy + ); + if accuracy < 100 && self.random().get_max(100) as u8 >= accuracy { + script_hook!(on_move_miss, target, executing_move, target); + self.event_hook().trigger(Event::Miss { + user: executing_move.user().deref(), + }); + break; + } + if used_move.category() == MoveCategory::Status { if let Some(secondary_effect) = used_move.secondary_effect() { let secondary_effect_chance = secondary_effect.chance(); @@ -316,6 +326,13 @@ impl<'own, 'library> Battle<'own, 'library> { } } + if number_of_hits == 0 { + script_hook!(on_move_miss, target, executing_move, target); + self.event_hook().trigger(Event::Miss { + user: executing_move.user().deref(), + }); + } + if !executing_move.user().is_fainted() { script_hook!(on_after_hits, executing_move, executing_move, target); } diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 1b96a2e..106ce90 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -16,46 +16,101 @@ use crate::static_data::EffectParameter; use crate::static_data::{Item, Statistic}; use crate::StringKey; +/// The script trait is used to make changes to how a battle executes, without requiring hardcoded +/// changes. This allows for easily defining generational differences, and add effects that the +/// developer might require. 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; + /// 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). fn get_marked_for_deletion(&self) -> &AtomicBool; + /// This marks the script for deletion, which will dispose of it as soon as possible. fn mark_for_deletion(&self) { self.get_marked_for_deletion().store(true, Ordering::SeqCst); } + /// Helper function to get the value of the marked for deletion bool. fn is_marked_for_deletion(&self) -> bool { self.get_marked_for_deletion().load(Ordering::SeqCst) } + /// A script can be suppressed by other scripts. If a script is suppressed by at least one script + /// we will not execute its methods. This should return the number of suppressions on the script. fn get_suppressed_count(&self) -> &AtomicUsize; + /// Helper function to check if there is at least one suppression on the script fn is_suppressed(&self) -> bool { self.get_suppressed_count().load(Ordering::SeqCst) > 0 } + /// Adds a suppression. This makes the script not run anymore. Note that adding this should also + /// remove the suppression later. + /// + /// A common pattern for this is to run this in the [`Self::on_initialize`] function, and run the + /// remove in the [`Self::on_remove`] function. fn add_suppression(&self) { self.get_suppressed_count().fetch_add(1, Ordering::SeqCst); } + /// Removes a suppression. This allows the script to run again (provided other scripts are not + /// suppressing it). Note that running this should only occur if an add was run before. fn remove_suppression(&self) { self.get_suppressed_count().fetch_sub(1, Ordering::SeqCst); } + /// This function is ran when a volatile effect is added while that volatile effect already is + /// in place. Instead of adding the volatile effect twice, it will execute this function instead. fn stack(&self) {} + /// This function is ran when this script stops being in effect, and is removed from its owner. fn on_remove(&self) {} + /// This function is ran when this script starts being in effect. fn on_initialize(&self, _pars: &[EffectParameter]) {} + /// This function is ran just before the start of the turn. Everyone has made its choices here, + /// and the turn is about to start. This is a great place to initialize data if you need to know + /// something has happened during a turn. fn on_before_turn(&self, _choice: &TurnChoice) {} + /// This function allows you to modify the effective speed of the Pokemon. This is ran before + /// turn ordering, so overriding here will allow you to put certain Pokemon before others. fn change_speed(&self, _choice: &TurnChoice, _speed: &mut u32) {} + /// This function allows you to modify the effective priority of the Pokemon. This is ran before + /// turn ordering, so overriding here will allow you to put certain Pokemon before others. Note + /// that this is only relevant on move choices, as other turn choice types do not have a priority. fn change_priority(&self, _choice: &TurnChoice, _priority: &mut i8) {} + + /// This function allows you to change the move that is used during execution. This is useful for + /// moves such as metronome, where the move chosen actually differs from the move used. fn change_move(&self, _choice: &MoveChoice, _move_name: &mut StringKey) {} + /// This function allows you to change a move into a multi-hit move. The number of hits set here + /// gets used as the number of hits. If set to 0, this will behave as if the move missed on its + /// first hit. fn change_number_of_hits(&self, _choice: &MoveChoice, _number_of_hits: &mut u8) {} + + /// This function allows you to prevent a move from running. If this gets set to true, the move + /// ends execution here. No PP will be decreased in this case. fn prevent_move(&self, _move: &ExecutingMove, _prevent: &mut bool) {} + /// This function makes the move fail. If the fail field gets set to true, the move ends execution, + /// and fail events get triggered. fn fail_move(&self, _move: &ExecutingMove, _fail: &mut bool) {} + /// Similar to [`Self::prevent_move`]. This function will also stop execution, but PP will be + /// decreased. fn stop_before_move(&self, _move: &ExecutingMove, _stop: &mut bool) {} + /// This function runs just before the move starts its execution. fn on_before_move(&self, _move: &ExecutingMove) {} + /// This function allows a script to prevent a move that is targeted at its owner. If set to true + /// the move fails, and fail events get triggered. fn fail_incoming_move(&self, _move: &ExecutingMove, _target: &Arc, _fail: &mut bool) {} + /// This function allows a script to make its owner invulnerable to an incoming move. fn is_invulnerable(&self, _move: &ExecutingMove, _target: &Arc, _invulnerable: &mut bool) {} + /// This function occurs when a move gets missed. This runs on the scripts belonging to the executing + /// move, which include the scripts that are attached to the owner of the script. fn on_move_miss(&self, _move: &ExecutingMove, _target: &Arc) {} + /// This function allows the script to change the actual type that is used for the move on a target. fn change_move_type(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _move_type: &mut u8) {} + /// This function allows the script to change how effective a move is on a target. fn change_effectiveness(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _effectiveness: &mut f32) {} + /// This function allows a script to block an outgoing move from being critical. fn block_critical(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _block_critical: &mut bool) {} + /// This function allows a script to block an incoming move from being critical. fn block_incoming_critical( &self, _move: &ExecutingMove, @@ -64,11 +119,23 @@ pub trait Script: Send + Sync { _block_critical: &mut bool, ) { } + /// This function allows a script to modify the accuracy of a move used. This value represents + /// the percentage accuracy, so anything above 100% will make it always hit. + fn change_accuracy(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _accuracy: &mut u8) {} + + /// This function allows a script to change the critical stage of the move used. fn change_critical_stage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _stage: &mut u8) {} + /// This function allows a script to change the damage modifier of a critical hit. This will only + /// run when a hit is critical. fn change_critical_modifier(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) {} + /// This function allows a script to change the damage modifier of a Same Type Attack Bonus, which + /// occurs when the user has the move type as one of its own types. fn change_stab_modifier(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) {} + /// This function allows a script to change the effective base power of a move hit. fn change_base_power(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _base_power: &mut u8) {} + /// This function allows a script to change which Pokemons stats are used for the offensive side when + /// calculating damage. fn change_damage_stats_user( &self, _move: &ExecutingMove, @@ -77,13 +144,19 @@ pub trait Script: Send + Sync { _stats_user: &mut Arc, ) { } + /// This function allows a script to bypass defensive stat boosts for a move hit. fn bypass_defensive_stat_boost(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _bypass: &mut bool) { } + /// This function allows a script to bypass offensive stat boosts for a move hit. fn bypass_offensive_stat_boost(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _bypass: &mut bool) { } + /// This function allows a script to change the actual offensive stat values used when calculating damage fn change_offensive_stat_value(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _amount: &mut u32) {} + /// This function allows a script to change the actual defensive stat values used when calculating damage. fn change_defensive_stat_value(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _amount: &mut u32) {} + /// This function allows a script to change the raw modifier we retrieved from the stats of the + /// defender and attacker. fn change_damage_stat_modifier( &self, _move: &ExecutingMove,