use crate::dynamic_data::models::executing_move::{ExecutingMove, HitData}; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::ScriptSource; use crate::static_data::{MoveCategory, Statistic}; use crate::{script_hook, script_hook_on_lock}; use parking_lot::RwLock; use std::sync::Arc; pub trait DamageLibrary: std::fmt::Debug { fn has_randomness(&self) -> bool; fn get_damage( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, hit_data: &HitData, ) -> u32; fn get_base_power( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, hit_data: &HitData, ) -> u8; fn get_stat_modifier( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, hit_data: &HitData, ) -> f32; fn get_damage_modifier( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, hit_data: &HitData, ) -> f32; } #[derive(Debug)] pub struct Gen7DamageLibrary { has_randomness: bool, } impl Gen7DamageLibrary { pub fn new(has_randomness: bool) -> Self { Self { has_randomness } } } impl DamageLibrary for Gen7DamageLibrary { fn has_randomness(&self) -> bool { self.has_randomness } fn get_damage( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, hit_data: &HitData, ) -> u32 { if executing_move.use_move().category() == MoveCategory::Status { return 0; } let level_modifier = ((2.0 * executing_move.user().read().level() as f32) / 5.0).floor() + 2.0; let base_power = hit_data.base_power(); let stat_modifier = self.get_stat_modifier(executing_move, target, hit_number, hit_data); let damage_modifier = self.get_damage_modifier(executing_move, target, hit_number, hit_data); let mut float_damage = (level_modifier * base_power as f32).floor(); float_damage = (float_damage * stat_modifier).floor(); float_damage = (float_damage / 50.0).floor() + 2.0; float_damage = (float_damage * damage_modifier).floor(); if executing_move.target_count() > 1 { float_damage = (float_damage * 0.75).floor(); } if hit_data.is_critical() { let mut crit_modifier = 1.5; script_hook!( change_critical_modifier, executing_move, executing_move, target, hit_number, &mut crit_modifier ); float_damage = (float_damage * crit_modifier).floor(); } if self.has_randomness { let random_percentage = 85 + executing_move .user() .read() .get_battle() .unwrap() .random() .get_between(0, 16); float_damage = (float_damage * (random_percentage as f32 / 100.0)).floor(); } if executing_move.user().read().types().contains(&hit_data.move_type()) { let mut stab_modifier = 1.5; script_hook!( change_stab_modifier, executing_move, executing_move, target, hit_number, &mut stab_modifier ); float_damage = (float_damage * stab_modifier).floor(); } float_damage = (float_damage * hit_data.effectiveness()).floor(); let mut damage = if float_damage <= 0.0 { if hit_data.effectiveness() == 0.0 { 0 } else { 1 } } else if float_damage >= u32::MAX as f32 { u32::MAX } else { float_damage as u32 }; script_hook!( change_damage, executing_move, executing_move, target, hit_number, &mut damage ); script_hook_on_lock!( change_incoming_damage, target, executing_move, target, hit_number, &mut damage ); damage } fn get_base_power( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, _hit_data: &HitData, ) -> u8 { if executing_move.use_move().category() == MoveCategory::Status { return 0; } let mut base_power = executing_move.use_move().base_power(); script_hook!( change_base_power, executing_move, executing_move, target, hit_number, &mut base_power ); base_power } fn get_stat_modifier( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, hit_data: &HitData, ) -> f32 { let mut user = executing_move.user().clone(); script_hook!( change_damage_stats_user, executing_move, executing_move, target, hit_number, &mut user ); let offensive_stat; let defensive_stat; if executing_move.use_move().category() == MoveCategory::Physical { offensive_stat = Statistic::Attack; defensive_stat = Statistic::Defense; } else { offensive_stat = Statistic::SpecialAttack; defensive_stat = Statistic::SpecialDefense; } let mut bypass_defensive_stat_boost = hit_data.is_critical() && target.read().stat_boost().get_stat(defensive_stat) > 0; script_hook!( bypass_defensive_stat_boost, executing_move, executing_move, target, hit_number, &mut bypass_defensive_stat_boost ); let mut bypass_offensive_stat_boost = hit_data.is_critical() && user.read().stat_boost().get_stat(offensive_stat) > 0; script_hook!( bypass_offensive_stat_boost, executing_move, executing_move, target, hit_number, &mut bypass_offensive_stat_boost ); let mut defensive_stat = if bypass_defensive_stat_boost { target.read().flat_stats().get_stat(offensive_stat) } else { target.read().boosted_stats().get_stat(offensive_stat) }; let mut offensive_stat = if bypass_offensive_stat_boost { user.read().flat_stats().get_stat(offensive_stat) } else { user.read().boosted_stats().get_stat(offensive_stat) }; script_hook!( change_defensive_stat_value, executing_move, executing_move, target, hit_number, &mut defensive_stat ); script_hook!( change_offensive_stat_value, executing_move, executing_move, target, hit_number, &mut offensive_stat ); let mut stat_modifier = offensive_stat as f32 / defensive_stat as f32; script_hook!( change_damage_stat_modifier, executing_move, executing_move, target, hit_number, &mut stat_modifier ); stat_modifier } fn get_damage_modifier( &self, executing_move: &ExecutingMove, target: &Arc>, hit_number: u8, _hit_data: &HitData, ) -> f32 { let mut modifier = 1.0; script_hook!( change_damage_modifier, executing_move, executing_move, target, hit_number, &mut modifier ); modifier } }