use std::ops::Deref; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU8, Ordering}; use std::sync::Arc; use atomig::Atomic; use parking_lot::RwLock; use crate::dynamic_data::models::learned_move::LearnedMove; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::dynamic_data::ScriptContainer; use crate::dynamic_data::TargetList; use crate::static_data::{MoveData, TypeIdentifier}; use crate::{PkmnResult, PokemonError}; /// A hit data is the data for a single hit, on a single target. #[derive(Default, Debug)] #[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct HitData { /// Whether or not the hit is critical. critical: AtomicBool, /// The base power of the hit. base_power: AtomicU8, /// The type effectiveness of the hit. effectiveness: Atomic, /// The actual damage of the hit. damage: AtomicU32, /// The type id of the type used for the hit. move_type: Atomic, /// Whether or not the hit has failed. has_failed: AtomicBool, } impl HitData { /// Whether or not the hit is critical. pub fn is_critical(&self) -> bool { self.critical.load(Ordering::Relaxed) } /// The base power of the hit. pub fn base_power(&self) -> u8 { self.base_power.load(Ordering::Relaxed) } /// The type effectiveness of the hit. pub fn effectiveness(&self) -> f32 { self.effectiveness.load(Ordering::Relaxed) } /// The actual damage of the hit. pub fn damage(&self) -> u32 { self.damage.load(Ordering::Relaxed) } /// The type id of the type used for the hit. pub fn move_type(&self) -> TypeIdentifier { self.move_type.load(Ordering::Relaxed) } /// Whether or not the hit has failed. pub fn has_failed(&self) -> bool { self.has_failed.load(Ordering::Relaxed) } /// Sets whether or not the hit is critical. pub fn set_critical(&self, value: bool) { self.critical.store(value, Ordering::SeqCst); } /// Sets the base power of the hit. pub fn set_base_power(&self, value: u8) { self.base_power.store(value, Ordering::SeqCst); } /// Sets the type effectiveness of the hit. pub fn set_effectiveness(&self, value: f32) { self.effectiveness.store(value, Ordering::SeqCst); } /// Sets the actual damage of the hit. pub fn set_damage(&self, value: u32) { self.damage.store(value, Ordering::SeqCst); } /// Sets the move type id of the hit. pub fn set_move_type(&self, value: TypeIdentifier) { self.move_type.store(value, Ordering::SeqCst); } /// Marks the hit as failed. pub fn fail(&self) { self.has_failed.store(true, Ordering::SeqCst); } } /// An executing move is the data of the move for while it is executing. #[derive(Debug)] #[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct ExecutingMove { /// The number of hits this move has. number_of_hits: u8, /// A list of hits for this move. For multi target multi hit moves, this stores the hits linearly, /// for example: (target1, hit1), (target1, hit2), (target2, hit1), (target2, hit2), etc. hits: Vec, /// The user of the move. user: Arc, /// The move the user has actually chosen to do. chosen_move: Arc, /// The move that the user is actually going to do. use_move: Arc, /// The script of the move. script: ScriptContainer, /// The targets for this move. targets: TargetList, /// Data required for this to be a script source. script_source_data: RwLock, } impl ExecutingMove { /// Instantiates an executing move. pub fn new( targets: TargetList, number_of_hits: u8, user: Arc, chosen_move: Arc, use_move: Arc, script: ScriptContainer, ) -> Self { let total_hits = number_of_hits as usize * targets.len(); let mut hits = Vec::with_capacity(total_hits); for _i in 0..total_hits { hits.push(HitData::default()) } Self { number_of_hits, hits, user, chosen_move, use_move, script, targets, script_source_data: Default::default(), } } /// The number of targets this move has. pub fn target_count(&self) -> usize { self.targets.len() } /// The number of hits this move has per target. pub fn number_of_hits(&self) -> u8 { self.number_of_hits } /// The user of the move. pub fn user(&self) -> &Arc { &self.user } /// The move the user has actually chosen to do. pub fn chosen_move(&self) -> &Arc { &self.chosen_move } /// The move that the user is actually going to do. pub fn use_move(&self) -> &Arc { &self.use_move } /// The script of the move. pub fn script(&self) -> &ScriptContainer { &self.script } /// Gets a hit data for a target, with a specific index. pub fn get_hit_data(&self, for_target: &Pokemon, hit: u8) -> PkmnResult<&HitData> { for (index, target) in self.targets.iter().enumerate() { if let Some(target) = target { if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) { let i = index * self.number_of_hits as usize + hit as usize; return Ok(&self.hits[i]); } } } Err(PokemonError::InvalidTargetRequested) } /// Checks whether a Pokemon is a target for this move. pub fn is_pokemon_target(&self, pokemon: &Arc) -> bool { for target in self.targets.iter().flatten() { if std::ptr::eq(target.deref().deref(), pokemon.deref().deref()) { return true; } } false } /// Gets the index of the hits in this move where the hits for a specific target start. pub(crate) fn get_index_of_target(&self, for_target: &Arc) -> PkmnResult { for (index, target) in self.targets.iter().enumerate() { if let Some(target) = target { if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) { let i = index * self.number_of_hits as usize; return Ok(i); } } } Err(PokemonError::InvalidTargetRequested) } /// Gets a hit based on its raw index. pub(crate) fn get_hit_from_raw_index(&self, index: usize) -> &HitData { &self.hits[index] } } impl ScriptSource for ExecutingMove { fn get_script_count(&self) -> usize { 1 } fn get_script_source_data(&self) -> &RwLock { &self.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.user.get_own_scripts(scripts); } }