use std::cmp::Ordering; use std::sync::Arc; use parking_lot::RwLock; use crate::dynamic_data::LearnedMove; use crate::dynamic_data::Pokemon; use crate::dynamic_data::ScriptContainer; use crate::dynamic_data::{ScriptSource, ScriptSourceData, ScriptWrapper}; /// The data on a turn choice that should be contained in every turn choice, regardless of type. #[derive(Debug)] struct CommonChoiceData<'user, 'library> { /// The user of the turn choice user: Arc>, /// The speed of the user at the beginning of the turn. speed: u32, /// This random value is set at the beginning of the turn. It is used for tie breaking of the /// turn order in a predictable way, regardless of implementation and hardware. random_value: u32, /// Whether or not the choice has failed. A failed choice will stop running, and execute special /// fail handling during turn execution. has_failed: bool, /// The data we can use to retrieve scripts that are affecting this choice. This will be written /// to once: when we need to initialize it. After that, this is only used to read. To prevent a /// read while we're writing to it, this is a RwLock. script_source_data: RwLock, } /// This enum defines a single choice for a Pokemon for a battle turn. #[derive(Debug)] pub enum TurnChoice<'user, 'library> { /// A move choice tells a Pokemon to use a move on a target for this turn. Move(MoveChoice<'user, 'library>), /// An item choice tells a Pokemon to use an item. Item(ItemChoice<'user, 'library>), /// A switch choice tells a Pokemon to switch with another Pokemon from the party. Switch(SwitchChoice<'user, 'library>), /// A flee choice tells a Pokemon to flee from battle. Flee(FleeChoice<'user, 'library>), /// A pass choice tells the user to do nothing that turn. Pass(PassChoice<'user, 'library>), } impl<'user, 'library> TurnChoice<'user, 'library> { /// The shared choice data between each of the different turn choices. fn choice_data(&self) -> &CommonChoiceData<'user, 'library> { match self { TurnChoice::Move(data) => &data.choice_data, TurnChoice::Item(data) => &data.choice_data, TurnChoice::Switch(data) => &data.choice_data, TurnChoice::Flee(data) => &data.choice_data, TurnChoice::Pass(data) => &data.choice_data, } } /// The shared choice data between each of the different turn choices. fn choice_data_mut(&mut self) -> &mut Box> { match self { TurnChoice::Move(data) => &mut data.choice_data, TurnChoice::Item(data) => &mut data.choice_data, TurnChoice::Switch(data) => &mut data.choice_data, TurnChoice::Flee(data) => &mut data.choice_data, TurnChoice::Pass(data) => &mut data.choice_data, } } /// Get the user of the given choice. pub fn user(&self) -> &Arc> { &self.choice_data().user } /// Get the speed of the user for the choice. Note that this speed is the speed of the Pokemon /// at the start of the turn! pub fn speed(&self) -> u32 { self.choice_data().speed } /// Get the mutable speed of the user for the choice. Note that this speed is the speed of the Pokemon /// at the start of the turn! pub fn speed_mut(&mut self) -> &mut u32 { &mut self.choice_data_mut().speed } /// Gets whether or not the choice has failed. If we notice this when we execute the choice, we /// will not execute it. pub fn has_failed(&self) -> bool { self.choice_data().has_failed } /// Fails the choice. This will prevent it from executing and run a specific fail handling during /// execution. Note that this can not be undone. pub fn fail(&mut self) { self.choice_data_mut().has_failed = true } /// The random value of a turn choice gets set during the start of a choice, and is used for tie /// breaking of turn executions. This means that choices get executed with a predictable order, /// regardless of implementation details. pub(crate) fn random_value(&self) -> u32 { self.choice_data().random_value } /// This sets the above random value. pub(crate) fn set_random_value(&mut self, val: u32) { self.choice_data_mut().random_value = val; } /// Helper function to get the move choice data from a turn. Note that this will panic if not /// used on a move choice. pub(crate) fn get_move_turn_data<'b>(&'b self) -> &'b MoveChoice<'user, 'library> { if let TurnChoice::Move(data) = self { return data; } panic!("Invalid turn choice"); } } impl<'user, 'library> ScriptSource<'user> for TurnChoice<'user, 'library> { fn get_script_count(&self) -> usize { match self { TurnChoice::Move(data) => data.get_script_count(), TurnChoice::Item(data) => data.get_script_count(), TurnChoice::Switch(data) => data.get_script_count(), TurnChoice::Flee(data) => data.get_script_count(), TurnChoice::Pass(data) => data.get_script_count(), } } fn get_script_source_data(&self) -> &RwLock { match self { TurnChoice::Move(data) => data.get_script_source_data(), TurnChoice::Item(data) => data.get_script_source_data(), TurnChoice::Switch(data) => data.get_script_source_data(), TurnChoice::Flee(data) => data.get_script_source_data(), TurnChoice::Pass(data) => data.get_script_source_data(), } } fn get_own_scripts(&self, scripts: &mut Vec) { match self { TurnChoice::Move(data) => data.get_own_scripts(scripts), TurnChoice::Item(data) => data.get_own_scripts(scripts), TurnChoice::Switch(data) => data.get_own_scripts(scripts), TurnChoice::Flee(data) => data.get_own_scripts(scripts), TurnChoice::Pass(data) => data.get_own_scripts(scripts), } } fn collect_scripts(&self, scripts: &mut Vec) { match self { TurnChoice::Move(data) => data.collect_scripts(scripts), TurnChoice::Item(data) => data.collect_scripts(scripts), TurnChoice::Switch(data) => data.collect_scripts(scripts), TurnChoice::Flee(data) => data.collect_scripts(scripts), TurnChoice::Pass(data) => data.collect_scripts(scripts), } } } /// The data attached to a move choice. #[derive(Debug)] pub struct MoveChoice<'user, 'library> { /// The move that is used for this choice. used_move: Arc>, /// The side this move is aimed at. target_side: u8, /// The index of the Pokemon on the side we're aiming at. target_index: u8, /// The move script. script: ScriptContainer, /// The priority of the move choice at the beginning of the turn. priority: i8, /// The common turn choice data. choice_data: Box>, } impl<'user, 'library> MoveChoice<'user, 'library> { /// Initializes the data for a new move choice. pub fn new( user: Arc>, used_move: Arc>, target_side: u8, target_index: u8, ) -> Self { Self { used_move, target_side, target_index, script: Default::default(), priority: 0, choice_data: Box::new(CommonChoiceData { user, speed: 0, random_value: 0, has_failed: false, script_source_data: Default::default(), }), } } /// The actual learned move on the Pokemon we use for this choice. pub fn used_move(&self) -> &Arc> { &self.used_move } /// The target side the move is aimed at. pub fn target_side(&self) -> u8 { self.target_side } /// The Pokemon index on the side we're aiming at. pub fn target_index(&self) -> u8 { self.target_index } /// The priority of the move choice at the beginning of the turn. pub fn priority(&self) -> i8 { self.priority } /// The priority of the move choice at the beginning of the turn. pub fn priority_mut(&mut self) -> &mut i8 { &mut self.priority } /// The user of the choice. pub fn user(&self) -> &Arc> { &self.choice_data.user } /// The move script of the choice. pub fn script(&self) -> &ScriptContainer { &self.script } } impl<'user, 'library> ScriptSource<'user> for MoveChoice<'user, 'library> { fn get_script_count(&self) -> usize { 1 + self.choice_data.user.get_script_count() } fn get_script_source_data(&self) -> &RwLock { &self.choice_data.script_source_data } fn get_own_scripts(&self, scripts: &mut Vec) { scripts.push((&self.script).into()); } fn collect_scripts(&self, scripts: &mut Vec) { self.get_own_scripts(scripts); self.choice_data.user.collect_scripts(scripts); } } /// The data given when we select an item choice. #[derive(Debug)] pub struct ItemChoice<'user, 'library> { /// The shared data of all turn choices. choice_data: Box>, } impl<'user, 'library> ItemChoice<'user, 'library> { /// Initialised a new item choice. pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, speed: 0, random_value: 0, has_failed: false, script_source_data: Default::default(), }), } } } impl<'user, 'library> ScriptSource<'user> for ItemChoice<'user, 'library> { fn get_script_count(&self) -> usize { 0 } fn get_script_source_data(&self) -> &RwLock { &self.choice_data.script_source_data } fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { self.choice_data.user.collect_scripts(scripts); } } /// The data given when we select a switch choice. #[derive(Debug)] pub struct SwitchChoice<'user, 'library> { /// The shared data of all turn choices. choice_data: Box>, } impl<'user, 'library> SwitchChoice<'user, 'library> { /// Initialise the turn choice data. pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, speed: 0, random_value: 0, has_failed: false, script_source_data: Default::default(), }), } } } impl<'user, 'library> ScriptSource<'user> for SwitchChoice<'user, 'library> { fn get_script_count(&self) -> usize { 0 } fn get_script_source_data(&self) -> &RwLock { &self.choice_data.script_source_data } fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { self.choice_data.user.collect_scripts(scripts); } } /// The data given when we select a flee choice. #[derive(Debug)] pub struct FleeChoice<'user, 'library> { /// The common data all turn choices share. choice_data: Box>, } impl<'user, 'library> FleeChoice<'user, 'library> { /// Initialises a new flee choice. pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, speed: 0, random_value: 0, has_failed: false, script_source_data: Default::default(), }), } } } impl<'user, 'library> ScriptSource<'user> for FleeChoice<'user, 'library> { fn get_script_count(&self) -> usize { 0 } fn get_script_source_data(&self) -> &RwLock { &self.choice_data.script_source_data } fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { self.choice_data.user.collect_scripts(scripts); } } /// The data given when we select a pass choice. #[derive(Debug)] pub struct PassChoice<'user, 'library> { /// The common data of all turn choices. choice_data: Box>, } impl<'user, 'library> PassChoice<'user, 'library> { /// Initialised a new pass choice. pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, speed: 0, random_value: 0, has_failed: false, script_source_data: Default::default(), }), } } } impl<'user, 'library> ScriptSource<'user> for PassChoice<'user, 'library> { fn get_script_count(&self) -> usize { 0 } fn get_script_source_data(&self) -> &RwLock { &self.choice_data.script_source_data } fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { self.choice_data.user.collect_scripts(scripts); } } impl<'user, 'library> PartialEq for TurnChoice<'user, 'library> { fn eq(&self, other: &Self) -> bool { std::ptr::eq(self, other) } } impl<'user, 'library> Eq for TurnChoice<'user, 'library> {} impl<'user, 'library> PartialOrd for TurnChoice<'user, 'library> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl<'user, 'library> Ord for TurnChoice<'user, 'library> { fn cmp(&self, other: &Self) -> Ordering { match self { TurnChoice::Move(data) => { if let TurnChoice::Move(other_data) = other { let priority_compare = data.priority.cmp(&other_data.priority); if priority_compare != Ordering::Equal { return priority_compare; } let speed_compare = self.speed().cmp(&other.speed()); if speed_compare != Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } Ordering::Greater } TurnChoice::Item { .. } => { if let TurnChoice::Move { .. } = other { return Ordering::Less; } if let TurnChoice::Item { .. } = other { let speed_compare = self.speed().cmp(&other.speed()); if speed_compare != Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } Ordering::Greater } TurnChoice::Switch { .. } => { if let TurnChoice::Move { .. } = other { return Ordering::Less; } if let TurnChoice::Item { .. } = other { return Ordering::Less; } if let TurnChoice::Switch { .. } = other { let speed_compare = self.speed().cmp(&other.speed()); if speed_compare != Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } Ordering::Greater } TurnChoice::Flee { .. } => { if let TurnChoice::Flee { .. } = other { let speed_compare = self.speed().cmp(&other.speed()); if speed_compare != Ordering::Equal { return speed_compare; } return self.random_value().cmp(&other.random_value()); } Ordering::Less } TurnChoice::Pass(..) => Ordering::Less, } } }