diff --git a/src/dynamic_data/event_hooks.rs b/src/dynamic_data/event_hooks.rs index 35365d6..fde6c1e 100644 --- a/src/dynamic_data/event_hooks.rs +++ b/src/dynamic_data/event_hooks.rs @@ -3,8 +3,8 @@ use std::fmt::{Debug, Formatter}; use crate::dynamic_data::DamageSource; use crate::dynamic_data::ExecutingMove; use crate::dynamic_data::Pokemon; -use crate::static_data::Form; use crate::static_data::Species; +use crate::static_data::{Form, Statistic}; /// The event hook is used to store external functions that listen to events. /// @@ -108,4 +108,15 @@ pub enum Event<'own, 'battle, 'library> { }, /// The turn is finished running, waiting for new input. EndTurn, + /// A pokemon had its stat boost changed + StatBoostChange { + /// The pokemon that had its stat boosts changed. + user: &'own Pokemon<'battle, 'library>, + /// The statistic that changed. + stat: Statistic, + /// The value of the stat before the change. + old_value: i8, + /// The value of the stat after the change. + new_value: i8, + }, } diff --git a/src/dynamic_data/libraries/battle_stat_calculator.rs b/src/dynamic_data/libraries/battle_stat_calculator.rs index 804d72b..7b238e2 100644 --- a/src/dynamic_data/libraries/battle_stat_calculator.rs +++ b/src/dynamic_data/libraries/battle_stat_calculator.rs @@ -43,7 +43,7 @@ impl Gen7BattleStatCalculator { /// This functions returns the modifier we need to do to a stat for a given stat boost. fn get_stat_boost_modifier(&self, pokemon: &Pokemon, stat: Statistic) -> f32 { - let boost = pokemon.stat_boost().get_stat(stat); + let boost = pokemon.stat_boost(stat); match boost { -6 => 2.0 / 8.0, -5 => 2.0 / 7.0, diff --git a/src/dynamic_data/libraries/damage_library.rs b/src/dynamic_data/libraries/damage_library.rs index f871330..a8c6295 100644 --- a/src/dynamic_data/libraries/damage_library.rs +++ b/src/dynamic_data/libraries/damage_library.rs @@ -191,8 +191,7 @@ impl DamageLibrary for Gen7DamageLibrary { offensive_stat = Statistic::SpecialAttack; defensive_stat = Statistic::SpecialDefense; } - let mut bypass_defensive_stat_boost = - hit_data.is_critical() && target.stat_boost().get_stat(defensive_stat) > 0; + let mut bypass_defensive_stat_boost = hit_data.is_critical() && target.stat_boost(defensive_stat) > 0; script_hook!( bypass_defensive_stat_boost, executing_move, @@ -201,7 +200,7 @@ impl DamageLibrary for Gen7DamageLibrary { hit_number, &mut bypass_defensive_stat_boost ); - let mut bypass_offensive_stat_boost = hit_data.is_critical() && user.stat_boost().get_stat(offensive_stat) > 0; + let mut bypass_offensive_stat_boost = hit_data.is_critical() && user.stat_boost(offensive_stat) > 0; script_hook!( bypass_offensive_stat_boost, executing_move, diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index e73c656..1d9b2d2 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -12,7 +12,6 @@ use crate::dynamic_data::models::damage_source::DamageSource; use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod}; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScripts}; -use crate::static_data::Ability; use crate::static_data::AbilityIndex; use crate::static_data::DataLibrary; use crate::static_data::Form; @@ -20,6 +19,7 @@ use crate::static_data::Gender; use crate::static_data::Item; use crate::static_data::Nature; use crate::static_data::Species; +use crate::static_data::{Ability, Statistic}; use crate::static_data::{ClampedStatisticSet, StatisticSet}; use crate::utils::Random; use crate::{script_hook, PkmnResult, StringKey}; @@ -277,9 +277,57 @@ impl<'own, 'library> Pokemon<'own, 'library> { pub fn boosted_stats(&self) -> &StatisticSet { &self.boosted_stats } - pub fn stat_boost(&self) -> &ClampedStatisticSet { - &self.stat_boost + pub fn stat_boost(&self, stat: Statistic) -> i8 { + self.stat_boost.get_stat(stat) } + pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> bool { + let mut prevent = false; + script_hook!( + prevent_stat_boost_change, + self, + self, + stat, + diff_amount, + self_inflicted, + &mut prevent + ); + if prevent { + return false; + } + script_hook!( + change_stat_boost_change, + self, + self, + stat, + self_inflicted, + &mut diff_amount + ); + if diff_amount == 0 { + return false; + } + + let mut changed = false; + let old_value = self.stat_boost.get_stat(stat); + if diff_amount > 0 { + changed = self.stat_boost.increase_stat(stat, diff_amount); + } else if diff_amount < 0 { + changed = self.stat_boost.decrease_stat(stat, -diff_amount); + } + if changed { + if let Some(battle) = self.get_battle() { + let new_value = self.stat_boost(stat); + battle.event_hook().trigger(Event::StatBoostChange { + user: self, + stat, + old_value, + new_value, + }) + } + self.recalculate_boosted_stats(); + } + return changed; + } + pub fn individual_values(&self) -> &ClampedStatisticSet { &self.individual_values } diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 106ce90..0bfbead 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -165,11 +165,19 @@ pub trait Script: Send + Sync { _modifier: &mut f32, ) { } + /// 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, _hit: u8, _modifier: &mut f32) {} + /// This function allows a script to modify the outgoing damage done by a move. fn change_damage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) {} + /// This function allows a script to modify the incoming damage done by a move. fn change_incoming_damage(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) {} + /// 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, _hit: u8) {} + /// This function triggers when an opponent on the field faints. fn on_opponent_faints(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} + /// 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( &self, _target: &Pokemon, @@ -179,9 +187,23 @@ pub trait Script: Send + Sync { _prevent: &mut bool, ) { } - fn change_stat_boost_change(&self, _target: &Pokemon, _stat: Statistic, _self_inflicted: bool, _amount: *mut i8) {} + /// 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) {} + /// 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, _hit: u8, _prevent: &mut bool) {} + /// 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, _hit: u8, _chance: &mut f32) {} + /// 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 + /// or below 0 will make it never hit. fn change_incoming_effect_chance( &self, _move: &ExecutingMove, @@ -190,25 +212,55 @@ pub trait Script: Send + Sync { _chance: &mut f32, ) { } + /// 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, _hit: u8) {} + /// 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) {} + /// 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) {} + /// This function allows the prevention of switching for any opponent. fn prevent_opponent_switch(&self, _choice: &TurnChoice, _prevent: &mut bool) {} + /// This function is called on a move and its parents when the move fails. fn on_fail(&self, _target: &Pokemon) {} + /// This function is called on a script when an opponent fails. fn on_opponent_fail(&self, _target: &Pokemon) {} + /// This function allows preventing the running away of the Pokemon its attached to fn prevent_self_run_away(&self, _choice: &TurnChoice, _prevent: &mut bool) {} + /// 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) {} + /// 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) {} + /// 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) {} + /// This function is triggered on a Pokemon and its parents when the given Pokemon faints. fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) {} + /// 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) {} + /// 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: &Item) {} + /// 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) {} + /// 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) {} + /// 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) {} + /// 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: &Item, _modifier: &mut u8) {} + /// Helper function to turn the self into an Any for downcasting. fn as_any(&self) -> &dyn Any; + /// Helper function to turn the self into an Any for downcasting. fn as_any_mut(&mut self) -> &mut dyn Any; }