use crate::dynamic_data::choices::{MoveChoice, SwitchChoice, TurnChoice}; use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::executing_move::ExecutingMove; use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod}; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::ScriptSource; use crate::static_data::{MoveCategory, MoveData, MoveTarget, SecondaryEffect}; use crate::{script_hook, StringKey}; use hashbrown::HashSet; use std::fmt::{Debug, Formatter}; use std::sync::Arc; pub trait MiscLibrary<'library> { fn is_critical( &self, battle: &Battle, executing_move: &ExecutingMove, target: &Arc, hit_number: u8, ) -> bool; fn can_flee(&self, choice: &SwitchChoice) -> bool; fn replacement_move<'func>( &'func self, user: &Arc>, target_side: u8, target_index: u8, ) -> TurnChoice<'func, 'library>; // TODO: can evolve from level up? // TODO: get time } impl<'library> Debug for dyn MiscLibrary<'library> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("MiscLibrary") } } #[derive(Debug)] pub struct Gen7MiscLibrary<'library> { struggle_data: *const MoveData, struggle_learned_move: Arc>, } impl<'library> Gen7MiscLibrary<'library> { pub fn new() -> Self { let struggle_data = Box::new(MoveData::new( &StringKey::new("struggle"), 0, MoveCategory::Physical, 50, 255, 255, MoveTarget::Any, 0, SecondaryEffect::new(-1.0, StringKey::new("struggle"), vec![]), HashSet::new(), )); let struggle_ptr = Box::into_raw(struggle_data); let struggle_learned_move = Arc::new(LearnedMove::new(unsafe { &*struggle_ptr }, MoveLearnMethod::Unknown)); Self { struggle_data: struggle_ptr, struggle_learned_move, } } } impl<'library> Drop for Gen7MiscLibrary<'library> { fn drop(&mut self) { unsafe { Box::from_raw(self.struggle_data as *mut MoveData); } } } impl<'library> MiscLibrary<'library> for Gen7MiscLibrary<'library> { fn is_critical( &self, battle: &Battle, executing_move: &ExecutingMove, target: &Arc, hit_number: u8, ) -> bool { if executing_move.use_move().category() == MoveCategory::Status { return false; } let mut crit_stage = 0; script_hook!( change_critical_stage, executing_move, executing_move, target, hit_number, &mut crit_stage ); // Crit stage is an unsigned byte, so we only care about values of 0 or higher. // For a critical stage of 3+ we always return true. match crit_stage { 0 => battle.random().get_max(24) == 0, 1 => battle.random().get_max(8) == 0, 2 => battle.random().get_max(2) == 0, _ => true, } } fn can_flee(&self, _choice: &SwitchChoice) -> bool { todo!() } fn replacement_move<'func>( &'func self, user: &Arc>, target_side: u8, target_index: u8, ) -> TurnChoice<'func, 'library> { TurnChoice::Move(MoveChoice::new( user.clone(), self.struggle_learned_move.clone(), target_side, target_index, )) } }