diff --git a/Cargo.toml b/Cargo.toml index 0beca31..f833868 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ rpath = false [dependencies] # Used for PrimInt, so we can check if a generic is an integer num-traits = "0.2" +atomic_prim_traits = "0.2.0" # Allow us to assert whether floats are approximately a value assert_approx_eq = "1.1.0" # Used for time based code (i.e. randomness) diff --git a/src/dynamic_data/choices/mod.rs b/src/dynamic_data/choices/mod.rs index 8af5e0b..4c833d8 100644 --- a/src/dynamic_data/choices/mod.rs +++ b/src/dynamic_data/choices/mod.rs @@ -124,7 +124,7 @@ impl<'user, 'library> ScriptSource<'user> for TurnChoice<'user, 'library> { #[derive(Debug)] pub struct MoveChoice<'user, 'library> { - used_move: Arc>>, + used_move: Arc>, target_side: u8, target_index: u8, script: ScriptContainer, @@ -135,7 +135,7 @@ pub struct MoveChoice<'user, 'library> { impl<'user, 'library> MoveChoice<'user, 'library> { pub fn new( user: Arc>>, - used_move: Arc>>, + used_move: Arc>, target_side: u8, target_index: u8, ) -> Self { @@ -155,7 +155,7 @@ impl<'user, 'library> MoveChoice<'user, 'library> { } } - pub fn used_move(&self) -> &Arc>> { + pub fn used_move(&self) -> &Arc> { &self.used_move } diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index f89146c..37daa5a 100644 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -68,14 +68,15 @@ impl<'own, 'library> Battle<'own, 'library> { if self.has_ended() { return Ok(()); } - let user = choice.user().read(); - if !user.is_usable() { - return Ok(()); + { + let user = choice.user().read(); + if !user.is_usable() { + return Ok(()); + } + if !user.is_on_battlefield() { + return Ok(()); + } } - if !user.is_on_battlefield() { - return Ok(()); - } - if !self.can_use(&choice) { return Ok(()); } @@ -92,12 +93,13 @@ impl<'own, 'library> Battle<'own, 'library> { fn execute_move_choice<'func>(&'func self, choice: &'func TurnChoice<'own, 'library>) -> PkmnResult<()> { let choice = choice.get_move_turn_data(); let used_move = choice.used_move(); - let move_data_lock = used_move.read(); - let move_data = move_data_lock.move_data(); - let mut move_name = move_data.name().clone(); - script_hook!(change_move, choice, choice, &mut move_name); - let move_data = self.library().static_data().moves().get(&move_name).unwrap(); - drop(move_data_lock); + let move_data = { + let move_data_lock = used_move; + let move_data = move_data_lock.move_data(); + let mut move_name = move_data.name().clone(); + script_hook!(change_move, choice, choice, &mut move_name); + self.library().static_data().moves().get(&move_name).unwrap() + }; // FIXME: also change the script on the choice if changed; let target_type = move_data.target(); let targets = resolve_targets(choice.target_side(), choice.target_index(), target_type, self); @@ -120,7 +122,7 @@ impl<'own, 'library> Battle<'own, 'library> { if prevented { return Ok(()); } - if !executing_move.chosen_move().write().try_use(1) { + if !executing_move.chosen_move().try_use(1) { return Ok(()); } self.event_hook().trigger(Event::MoveUse { diff --git a/src/dynamic_data/libraries/battle_stat_calculator.rs b/src/dynamic_data/libraries/battle_stat_calculator.rs index 9d97cf7..2730258 100644 --- a/src/dynamic_data/libraries/battle_stat_calculator.rs +++ b/src/dynamic_data/libraries/battle_stat_calculator.rs @@ -1,13 +1,14 @@ use crate::dynamic_data::models::pokemon::Pokemon; use crate::static_data::statistic_set::StatisticSet; use crate::static_data::statistics::Statistic; +use std::sync::atomic::AtomicU32; #[derive(Debug)] pub struct BattleStatCalculator {} impl BattleStatCalculator { - pub fn calculate_flat_stats(&self, pokemon: &Pokemon) -> StatisticSet { - StatisticSet::::new( + pub fn calculate_flat_stats(&self, pokemon: &Pokemon) -> StatisticSet { + StatisticSet::::new( self.calculate_health_stat(pokemon), self.calculate_other_stat(pokemon, Statistic::Attack), self.calculate_other_stat(pokemon, Statistic::Defense), @@ -25,8 +26,8 @@ impl BattleStatCalculator { } } - pub fn calculate_boosted_stats(&self, pokemon: &Pokemon) -> StatisticSet { - StatisticSet::::new( + pub fn calculate_boosted_stats(&self, pokemon: &Pokemon) -> StatisticSet { + StatisticSet::::new( self.calculate_boosted_stat(pokemon, Statistic::HP), self.calculate_boosted_stat(pokemon, Statistic::Attack), self.calculate_boosted_stat(pokemon, Statistic::Defense), diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs index 51f9220..efe05da 100644 --- a/src/dynamic_data/libraries/misc_library.rs +++ b/src/dynamic_data/libraries/misc_library.rs @@ -39,7 +39,7 @@ impl<'library> Debug for dyn MiscLibrary<'library> { #[derive(Debug)] pub struct Gen7MiscLibrary<'library> { struggle_data: *const MoveData, - struggle_learned_move: Arc>>, + struggle_learned_move: Arc>, } impl<'library> Gen7MiscLibrary<'library> { @@ -50,17 +50,14 @@ impl<'library> Gen7MiscLibrary<'library> { MoveCategory::Physical, 50, 255, - 10, + 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(RwLock::new(LearnedMove::new( - unsafe { &*struggle_ptr }, - MoveLearnMethod::Unknown, - ))); + let struggle_learned_move = Arc::new(LearnedMove::new(unsafe { &*struggle_ptr }, MoveLearnMethod::Unknown)); Self { struggle_data: struggle_ptr, struggle_learned_move, diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 70abf3a..2ad09ad 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -197,13 +197,13 @@ impl<'own, 'library> Battle<'own, 'library> { } if let TurnChoice::Move(data) = choice { // TODO: Hook to change number of PP needed. - if data.used_move().read().remaining_pp() < 1 { + if data.used_move().remaining_pp() < 1 { return false; } if !is_valid_target( data.target_side(), data.target_index(), - data.used_move().read().move_data().target(), + data.used_move().move_data().target(), choice.user().read().deref(), ) { return false; diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs index 87bc631..e21bcd9 100644 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -63,7 +63,7 @@ pub struct ExecutingMove<'own, 'battle, 'library> { number_of_hits: u8, hits: Vec, user: Arc>>, - chosen_move: Arc>>, + chosen_move: Arc>, use_move: &'own MoveData, script: ScriptContainer, targets: &'own TargetList<'battle, 'library>, @@ -75,7 +75,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> { targets: &'own TargetList<'battle, 'library>, number_of_hits: u8, user: Arc>>, - chosen_move: Arc>>, + chosen_move: Arc>, use_move: &'own MoveData, script: ScriptContainer, ) -> Self { @@ -104,7 +104,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> { pub fn user(&self) -> &Arc>> { &self.user } - pub fn chosen_move(&self) -> &Arc>> { + pub fn chosen_move(&self) -> &Arc> { &self.chosen_move } diff --git a/src/dynamic_data/models/learned_move.rs b/src/dynamic_data/models/learned_move.rs index 215cba9..d2938a0 100644 --- a/src/dynamic_data/models/learned_move.rs +++ b/src/dynamic_data/models/learned_move.rs @@ -1,10 +1,11 @@ use crate::static_data::MoveData; +use std::sync::atomic::{AtomicU8, Ordering}; #[derive(Debug)] pub struct LearnedMove<'library> { move_data: &'library MoveData, max_pp: u8, - remaining_pp: u8, + remaining_pp: AtomicU8, learn_method: MoveLearnMethod, } @@ -19,7 +20,7 @@ impl<'a> LearnedMove<'a> { Self { move_data, max_pp: move_data.base_usages(), - remaining_pp: move_data.base_usages(), + remaining_pp: AtomicU8::new(move_data.base_usages()), learn_method, } } @@ -32,17 +33,17 @@ impl<'a> LearnedMove<'a> { self.max_pp } pub fn remaining_pp(&self) -> u8 { - self.remaining_pp + self.remaining_pp.load(Ordering::Relaxed) } pub fn learn_method(&self) -> MoveLearnMethod { self.learn_method } - pub fn try_use(&mut self, amount: u8) -> bool { - if amount > self.remaining_pp { + pub fn try_use(&self, amount: u8) -> bool { + if amount > self.remaining_pp() { return false; } - self.remaining_pp -= amount; - return true; + self.remaining_pp.fetch_sub(amount, Ordering::SeqCst); + true } } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index b2f7630..c58b4ad 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -20,6 +20,7 @@ use crate::static_data::DataLibrary; use crate::utils::random::Random; use crate::{script_hook, PkmnResult, ScriptCategory, StringKey}; use parking_lot::RwLock; +use std::sync::atomic::{AtomicI8, AtomicU32, AtomicU8, Ordering}; use std::sync::{Arc, Weak}; #[derive(Debug)] @@ -63,21 +64,21 @@ where display_form: Option<&'own Form>, level: LevelInt, - experience: u32, + experience: AtomicU32, unique_identifier: u32, gender: Gender, coloring: u8, held_item: Option<&'own Item>, - current_health: u32, + current_health: AtomicU32, weight: f32, height: f32, - stat_boost: ClampedStatisticSet, - flat_stats: StatisticSet, - boosted_stats: StatisticSet, - individual_values: ClampedStatisticSet, - effort_values: ClampedStatisticSet, + stat_boost: ClampedStatisticSet, + flat_stats: StatisticSet, + boosted_stats: StatisticSet, + individual_values: ClampedStatisticSet, + effort_values: ClampedStatisticSet, nature: &'own Nature, nickname: Option, @@ -88,7 +89,7 @@ where battle_data: Option>, - moves: [Option>>>; MAX_MOVES], + moves: [Option>>; MAX_MOVES], allowed_experience: bool, types: Vec, @@ -125,7 +126,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { let nature = library .static_data() .natures() - .get_nature(&nature) + .get_nature(nature) .unwrap_or_else(|| panic!("Unknown nature name was given: {}.", &nature)); let mut pokemon = Self { library, @@ -134,12 +135,12 @@ impl<'own, 'library> Pokemon<'own, 'library> { display_species: None, display_form: None, level, - experience, + experience: AtomicU32::new(experience), unique_identifier, gender, coloring, held_item: None, - current_health: 1, + current_health: AtomicU32::new(1), weight, height, stat_boost: Default::default(), @@ -166,7 +167,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { }; pokemon.recalculate_flat_stats(); let health = pokemon.flat_stats().hp(); - pokemon.current_health = health; + pokemon.current_health = AtomicU32::new(health); pokemon } @@ -199,7 +200,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { self.level } pub fn experience(&self) -> u32 { - self.experience + self.experience.load(Ordering::Relaxed) } pub fn unique_identifier(&self) -> u32 { self.unique_identifier @@ -240,7 +241,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } pub fn current_health(&self) -> u32 { - self.current_health + self.current_health.load(Ordering::Relaxed) } pub fn max_health(&self) -> u32 { self.boosted_stats.hp() @@ -260,31 +261,31 @@ impl<'own, 'library> Pokemon<'own, 'library> { pub fn types(&self) -> &Vec { &self.types } - pub fn learned_moves(&self) -> &[Option>>>; MAX_MOVES] { + pub fn learned_moves(&self) -> &[Option>>; MAX_MOVES] { &self.moves } pub fn status(&self) -> &ScriptContainer { &self.status_script } - pub fn flat_stats(&self) -> &StatisticSet { + pub fn flat_stats(&self) -> &StatisticSet { &self.flat_stats } - pub fn boosted_stats(&self) -> &StatisticSet { + pub fn boosted_stats(&self) -> &StatisticSet { &self.boosted_stats } - pub fn stat_boost(&self) -> &ClampedStatisticSet { + pub fn stat_boost(&self) -> &ClampedStatisticSet { &self.stat_boost } - pub fn individual_values(&self) -> &ClampedStatisticSet { + pub fn individual_values(&self) -> &ClampedStatisticSet { &self.individual_values } - pub fn effort_values(&self) -> &ClampedStatisticSet { + pub fn effort_values(&self) -> &ClampedStatisticSet { &self.effort_values } pub fn get_battle(&self) -> Option<&Battle<'own, 'library>> { if let Some(data) = &self.battle_data { - Some(&data.battle().unwrap()) + Some(data.battle().unwrap()) } else { None } @@ -401,10 +402,10 @@ impl<'own, 'library> Pokemon<'own, 'library> { let old_health = self.max_health(); self.recalculate_flat_stats(); let diff_health = (self.max_health() - old_health) as i32; - if self.current_health == 0 && (self.current_health as i32) < -diff_health { - self.current_health = 0; + if self.current_health() == 0 && (self.current_health() as i32) < -diff_health { + self.current_health.store(0, Ordering::SeqCst); } else { - self.current_health = self.current_health() + diff_health as u32; + self.current_health.fetch_add(diff_health as u32, Ordering::Acquire); } // TODO: consider form specific attacks? @@ -420,7 +421,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } pub fn is_fainted(&self) -> bool { - self.current_health == 0 + self.current_health() == 0 } pub fn set_battle_data(&mut self, battle: *mut Battle<'own, 'library>, battle_side_index: u8) { @@ -475,8 +476,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { } pub fn damage(&mut self, mut damage: u32, source: DamageSource) { - if damage > self.current_health { - damage = self.current_health; + if damage > self.current_health() { + damage = self.current_health(); } if damage == 0 { return; @@ -491,10 +492,10 @@ impl<'own, 'library> Pokemon<'own, 'library> { new_health, }); // TODO: register history - script_hook!(on_damage, self, self, source, self.current_health, new_health); + script_hook!(on_damage, self, self, source, self.current_health(), new_health); } } - self.current_health = new_health; + self.current_health.store(new_health, Ordering::SeqCst); if self.is_fainted() && damage > 0 { self.on_faint(source); } @@ -540,7 +541,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { panic!("No more moves with an empty space found."); } let move_data = self.library.static_data().moves().get(move_name).unwrap(); - self.moves[move_pos.unwrap()] = Some(Arc::new(RwLock::new(LearnedMove::new(move_data, learn_method)))); + self.moves[move_pos.unwrap()] = Some(Arc::new(LearnedMove::new(move_data, learn_method))); } } diff --git a/src/lib.rs b/src/lib.rs index d08a3a2..c2f9b8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ #![feature(bench_black_box)] #![feature(let_chains)] #![feature(once_cell)] +#![feature(const_option)] extern crate core; diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs index 62d1736..58e802f 100644 --- a/src/static_data/libraries/species_library.rs +++ b/src/static_data/libraries/species_library.rs @@ -39,7 +39,7 @@ pub mod tests { use crate::static_data::species_data::form::Form; use crate::static_data::species_data::learnable_moves::LearnableMoves; use crate::static_data::species_data::species::Species; - use crate::static_data::statistic_set::StatisticSet; + use crate::static_data::StaticStatisticSet; use hashbrown::HashSet; fn build_species<'a>() -> Species { @@ -55,7 +55,7 @@ pub mod tests { 0.0, 0, Vec::new(), - StatisticSet::default(), + StaticStatisticSet::default(), Vec::new(), Vec::new(), LearnableMoves::new(), diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs index 5e822a6..6453f7c 100644 --- a/src/static_data/species_data/form.rs +++ b/src/static_data/species_data/form.rs @@ -1,8 +1,7 @@ -use crate::static_data::Ability; use crate::static_data::AbilityIndex; use crate::static_data::LearnableMoves; use crate::static_data::Statistic; -use crate::static_data::StatisticSet; +use crate::static_data::{Ability, StaticStatisticSet}; use crate::Random; use crate::StringKey; use hashbrown::HashSet; @@ -14,7 +13,7 @@ pub struct Form { weight: f32, base_experience: u32, types: Vec, - base_stats: StatisticSet, + base_stats: StaticStatisticSet, abilities: Vec, hidden_abilities: Vec, moves: LearnableMoves, @@ -28,7 +27,7 @@ impl Form { weight: f32, base_experience: u32, types: Vec, - base_stats: StatisticSet, + base_stats: StaticStatisticSet, abilities: Vec, hidden_abilities: Vec, moves: LearnableMoves, @@ -63,8 +62,8 @@ impl Form { pub fn types(&self) -> &Vec { &self.types } - pub fn base_stats(&self) -> StatisticSet { - self.base_stats + pub fn base_stats(&self) -> &StaticStatisticSet { + &self.base_stats } pub fn abilities(&self) -> &Vec { &self.abilities diff --git a/src/static_data/statistic_set.rs b/src/static_data/statistic_set.rs index 5ac3e7e..74da68c 100644 --- a/src/static_data/statistic_set.rs +++ b/src/static_data/statistic_set.rs @@ -1,10 +1,12 @@ use super::statistics::Statistic; -use num_traits::{cast, clamp, PrimInt}; +use atomic_prim_traits::AtomicInt; +use num_traits::{clamp, NumCast, PrimInt}; +use std::sync::atomic::Ordering; -#[derive(Default, Eq, PartialEq, Copy, Clone, Debug)] +#[derive(Default, Eq, PartialEq, Clone, Debug)] pub struct StatisticSet where - T: PrimInt, + T: AtomicInt, { hp: T, attack: T, @@ -16,85 +18,92 @@ where impl StatisticSet where - T: PrimInt, + T: AtomicInt, { - pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self { + pub fn new( + hp: T::Prim, + attack: T::Prim, + defense: T::Prim, + special_attack: T::Prim, + special_defense: T::Prim, + speed: T::Prim, + ) -> Self { Self { - hp, - attack, - defense, - special_attack, - special_defense, - speed, + hp: T::new(hp), + attack: T::new(attack), + defense: T::new(defense), + special_attack: T::new(special_attack), + special_defense: T::new(special_defense), + speed: T::new(speed), } } - pub fn hp(&self) -> T { - self.hp + pub fn hp(&self) -> T::Prim { + self.hp.load(Ordering::Relaxed) } - pub fn attack(&self) -> T { - self.attack + pub fn attack(&self) -> T::Prim { + self.attack.load(Ordering::Relaxed) } - pub fn defense(&self) -> T { - self.defense + pub fn defense(&self) -> T::Prim { + self.defense.load(Ordering::Relaxed) } - pub fn special_attack(&self) -> T { - self.special_attack + pub fn special_attack(&self) -> T::Prim { + self.special_attack.load(Ordering::Relaxed) } - pub fn special_defense(&self) -> T { - self.special_defense + pub fn special_defense(&self) -> T::Prim { + self.special_defense.load(Ordering::Relaxed) } - pub fn speed(&self) -> T { - self.speed + pub fn speed(&self) -> T::Prim { + self.speed.load(Ordering::Relaxed) } - pub const fn get_stat(&self, stat: Statistic) -> T { + pub fn get_stat(&self, stat: Statistic) -> T::Prim { match stat { - Statistic::HP => self.hp, - Statistic::Attack => self.attack, - Statistic::Defense => self.defense, - Statistic::SpecialAttack => self.special_attack, - Statistic::SpecialDefense => self.special_defense, - Statistic::Speed => self.speed, + Statistic::HP => self.hp.load(Ordering::Relaxed), + Statistic::Attack => self.attack.load(Ordering::Relaxed), + Statistic::Defense => self.defense.load(Ordering::Relaxed), + Statistic::SpecialAttack => self.special_attack.load(Ordering::Relaxed), + Statistic::SpecialDefense => self.special_defense.load(Ordering::Relaxed), + Statistic::Speed => self.speed.load(Ordering::Relaxed), } } - pub fn set_stat(&mut self, stat: Statistic, value: T) { + pub fn set_stat(&self, stat: Statistic, value: T::Prim) { match stat { - Statistic::HP => self.hp = value, - Statistic::Attack => self.attack = value, - Statistic::Defense => self.defense = value, - Statistic::SpecialAttack => self.special_attack = value, - Statistic::SpecialDefense => self.special_defense = value, - Statistic::Speed => self.speed = value, + Statistic::HP => self.hp.store(value, Ordering::SeqCst), + Statistic::Attack => self.attack.store(value, Ordering::SeqCst), + Statistic::Defense => self.defense.store(value, Ordering::SeqCst), + Statistic::SpecialAttack => self.special_attack.store(value, Ordering::SeqCst), + Statistic::SpecialDefense => self.special_defense.store(value, Ordering::SeqCst), + Statistic::Speed => self.speed.store(value, Ordering::SeqCst), } } - pub fn increase_stat(&mut self, stat: Statistic, value: T) { + pub fn increase_stat(&self, stat: Statistic, value: T::Prim) { match stat { - Statistic::HP => self.hp = self.hp + value, - Statistic::Attack => self.attack = self.attack + value, - Statistic::Defense => self.defense = self.defense + value, - Statistic::SpecialAttack => self.special_attack = self.special_attack + value, - Statistic::SpecialDefense => self.special_defense = self.special_defense + value, - Statistic::Speed => self.speed = self.speed + value, - } + Statistic::HP => self.hp.fetch_add(value, Ordering::SeqCst), + Statistic::Attack => self.attack.fetch_add(value, Ordering::SeqCst), + Statistic::Defense => self.defense.fetch_add(value, Ordering::SeqCst), + Statistic::SpecialAttack => self.special_attack.fetch_add(value, Ordering::SeqCst), + Statistic::SpecialDefense => self.special_defense.fetch_add(value, Ordering::SeqCst), + Statistic::Speed => self.speed.fetch_add(value, Ordering::SeqCst), + }; } - pub fn decrease_stat(&mut self, stat: Statistic, value: T) { + pub fn decrease_stat(&self, stat: Statistic, value: T::Prim) { match stat { - Statistic::HP => self.hp = self.hp - value, - Statistic::Attack => self.attack = self.attack - value, - Statistic::Defense => self.defense = self.defense - value, - Statistic::SpecialAttack => self.special_attack = self.special_attack - value, - Statistic::SpecialDefense => self.special_defense = self.special_defense - value, - Statistic::Speed => self.speed = self.speed - value, - } + Statistic::HP => self.hp.fetch_sub(value, Ordering::SeqCst), + Statistic::Attack => self.attack.fetch_sub(value, Ordering::SeqCst), + Statistic::Defense => self.defense.fetch_sub(value, Ordering::SeqCst), + Statistic::SpecialAttack => self.special_attack.fetch_sub(value, Ordering::SeqCst), + Statistic::SpecialDefense => self.special_defense.fetch_sub(value, Ordering::SeqCst), + Statistic::Speed => self.speed.fetch_sub(value, Ordering::SeqCst), + }; } } -#[derive(Default, Eq, PartialEq, Copy, Clone, Debug)] -pub struct ClampedStatisticSet +#[derive(Default, Eq, PartialEq, Clone, Debug)] +pub struct StaticStatisticSet where T: PrimInt, { @@ -106,37 +115,37 @@ where speed: T, } -impl ClampedStatisticSet +impl StaticStatisticSet where T: PrimInt, { - pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self { + pub const fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self { Self { - hp: cast(clamp(cast::(hp).unwrap(), MIN, MAX)).unwrap(), - attack: cast(clamp(cast::(attack).unwrap(), MIN, MAX)).unwrap(), - defense: cast(clamp(cast::(defense).unwrap(), MIN, MAX)).unwrap(), - special_attack: cast(clamp(cast::(special_attack).unwrap(), MIN, MAX)).unwrap(), - special_defense: cast(clamp(cast::(special_defense).unwrap(), MIN, MAX)).unwrap(), - speed: cast(clamp(cast::(speed).unwrap(), MIN, MAX)).unwrap(), + hp, + attack, + defense, + special_attack, + special_defense, + speed, } } - pub fn hp(&self) -> T { + pub const fn hp(&self) -> T { self.hp } - pub fn attack(&self) -> T { + pub const fn attack(&self) -> T { self.attack } - pub fn defense(&self) -> T { + pub const fn defense(&self) -> T { self.defense } - pub fn special_attack(&self) -> T { + pub const fn special_attack(&self) -> T { self.special_attack } - pub fn special_defense(&self) -> T { + pub const fn special_defense(&self) -> T { self.special_defense } - pub fn speed(&self) -> T { + pub const fn speed(&self) -> T { self.speed } @@ -150,55 +159,143 @@ where Statistic::Speed => self.speed, } } +} - pub fn set_stat(&mut self, stat: Statistic, mut value: T) { - if value < T::from(MIN).unwrap() { - value = T::from(MIN).unwrap(); - } else if value > T::from(MAX).unwrap() { - value = T::from(MAX).unwrap(); - } - match stat { - Statistic::HP => self.hp = value, - Statistic::Attack => self.attack = value, - Statistic::Defense => self.defense = value, - Statistic::SpecialAttack => self.special_attack = value, - Statistic::SpecialDefense => self.special_defense = value, - Statistic::Speed => self.speed = value, +#[derive(Default, Eq, PartialEq, Clone, Debug)] +pub struct ClampedStatisticSet +where + T: AtomicInt, +{ + hp: T, + attack: T, + defense: T, + special_attack: T, + special_defense: T, + speed: T, +} + +impl ClampedStatisticSet +where + T: AtomicInt, + ::Prim: NumCast, + ::Prim: PrimInt, +{ + pub fn min() -> T::Prim { + <::Prim as NumCast>::from(MIN).unwrap() + } + pub fn max() -> T::Prim { + <::Prim as NumCast>::from(MAX).unwrap() + } + + fn clamped_cast(v: T::Prim) -> T { + let v = clamp(v, Self::min(), Self::max()); + T::new(v) + } + + pub fn new( + hp: T::Prim, + attack: T::Prim, + defense: T::Prim, + special_attack: T::Prim, + special_defense: T::Prim, + speed: T::Prim, + ) -> Self { + Self { + hp: Self::clamped_cast(hp), + attack: Self::clamped_cast(attack), + defense: Self::clamped_cast(defense), + special_attack: Self::clamped_cast(special_attack), + special_defense: Self::clamped_cast(special_defense), + speed: Self::clamped_cast(speed), } } - fn change_stat(mut new_value: T, original_value: &mut T) -> bool { - if new_value < T::from(MIN).unwrap() { - new_value = T::from(MIN).unwrap(); - } else if new_value > T::from(MAX).unwrap() { - new_value = T::from(MAX).unwrap(); + pub fn hp(&self) -> T::Prim { + self.hp.load(Ordering::Relaxed) + } + pub fn attack(&self) -> T::Prim { + self.attack.load(Ordering::Relaxed) + } + pub fn defense(&self) -> T::Prim { + self.defense.load(Ordering::Relaxed) + } + pub fn special_attack(&self) -> T::Prim { + self.special_attack.load(Ordering::Relaxed) + } + pub fn special_defense(&self) -> T::Prim { + self.special_defense.load(Ordering::Relaxed) + } + pub fn speed(&self) -> T::Prim { + self.speed.load(Ordering::Relaxed) + } + + pub fn get_stat(&self, stat: Statistic) -> T::Prim { + match stat { + Statistic::HP => self.hp.load(Ordering::Relaxed), + Statistic::Attack => self.attack.load(Ordering::Relaxed), + Statistic::Defense => self.defense.load(Ordering::Relaxed), + Statistic::SpecialAttack => self.special_attack.load(Ordering::Relaxed), + Statistic::SpecialDefense => self.special_defense.load(Ordering::Relaxed), + Statistic::Speed => self.speed.load(Ordering::Relaxed), } - if *original_value == new_value { + } + + pub fn set_stat(&self, stat: Statistic, mut value: T::Prim) { + if value < Self::min() { + value = Self::min(); + } else if value > Self::max() { + value = Self::max(); + } + match stat { + Statistic::HP => self.hp.store(value, Ordering::SeqCst), + Statistic::Attack => self.attack.store(value, Ordering::SeqCst), + Statistic::Defense => self.defense.store(value, Ordering::SeqCst), + Statistic::SpecialAttack => self.special_attack.store(value, Ordering::SeqCst), + Statistic::SpecialDefense => self.special_defense.store(value, Ordering::SeqCst), + Statistic::Speed => self.speed.store(value, Ordering::SeqCst), + } + } + + fn change_stat(v: &T, f: F) -> bool + where + F: FnOnce(T::Prim) -> T::Prim, + { + let original_value = v.load(Ordering::Relaxed); + let mut new_value = f(original_value); + if new_value < Self::min() { + new_value = Self::min(); + } else if new_value > Self::max() { + new_value = Self::max(); + } + if original_value == new_value { return false; } - *original_value = new_value; + v.store(new_value, Ordering::SeqCst); true } - pub fn increase_stat(&mut self, stat: Statistic, value: T) -> bool { + pub fn increase_stat(&self, stat: Statistic, value: T::Prim) -> bool + where + ::Prim: PrimInt, + { match stat { - Statistic::HP => Self::change_stat(self.hp + value, &mut self.hp), - Statistic::Attack => Self::change_stat(self.attack + value, &mut self.attack), - Statistic::Defense => Self::change_stat(self.defense + value, &mut self.defense), - Statistic::SpecialAttack => Self::change_stat(self.special_attack + value, &mut self.special_attack), - Statistic::SpecialDefense => Self::change_stat(self.special_defense + value, &mut self.special_defense), - Statistic::Speed => Self::change_stat(self.speed + value, &mut self.speed), + Statistic::HP => Self::change_stat(&self.hp, |a| a + value), + Statistic::Attack => Self::change_stat(&self.attack, |a| a + value), + Statistic::Defense => Self::change_stat(&self.defense, |a| a + value), + Statistic::SpecialAttack => Self::change_stat(&self.special_attack, |a| a + value), + Statistic::SpecialDefense => Self::change_stat(&self.special_defense, |a| a + value), + Statistic::Speed => Self::change_stat(&self.speed, |a| a + value), } } - pub fn decrease_stat(&mut self, stat: Statistic, value: T) -> bool { + pub fn decrease_stat(&self, stat: Statistic, value: T::Prim) -> bool { match stat { - Statistic::HP => Self::change_stat(self.hp - value, &mut self.hp), - Statistic::Attack => Self::change_stat(self.attack - value, &mut self.attack), - Statistic::Defense => Self::change_stat(self.defense - value, &mut self.defense), - Statistic::SpecialAttack => Self::change_stat(self.special_attack - value, &mut self.special_attack), - Statistic::SpecialDefense => Self::change_stat(self.special_defense - value, &mut self.special_defense), - Statistic::Speed => Self::change_stat(self.speed - value, &mut self.speed), + Statistic::HP => Self::change_stat(&self.hp, |a| a - value), + Statistic::Attack => Self::change_stat(&self.attack, |a| a - value), + Statistic::Defense => Self::change_stat(&self.defense, |a| a - value), + Statistic::SpecialAttack => Self::change_stat(&self.special_attack, |a| a - value), + Statistic::SpecialDefense => Self::change_stat(&self.special_defense, |a| a - value), + Statistic::Speed => Self::change_stat(&self.speed, |a| a - value), } } } @@ -206,10 +303,11 @@ where #[cfg(test)] mod tests { use super::*; + use std::sync::atomic::AtomicI32; #[test] fn create_get_values() { - let set = StatisticSet::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); assert_eq!(set.hp(), 1); assert_eq!(set.get_stat(Statistic::HP), 1); assert_eq!(set.attack(), 2); @@ -226,7 +324,7 @@ mod tests { #[test] fn create_set_value() { - let mut set = StatisticSet::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); set.set_stat(Statistic::HP, 20); assert_eq!(set.hp(), 20); @@ -244,21 +342,21 @@ mod tests { #[test] fn create_increase_value() { - let mut set = StatisticSet::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); set.increase_stat(Statistic::SpecialAttack, 5); assert_eq!(set.special_attack(), 9); } #[test] fn create_decrease_value() { - let mut set = StatisticSet::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); set.decrease_stat(Statistic::SpecialAttack, 5); assert_eq!(set.special_attack(), -1); } #[test] fn create_clamped_get_values() { - let set = ClampedStatisticSet::::new(-5, 2, 3, 4, 5, 6); + let set = ClampedStatisticSet::::new(-5, 2, 3, 4, 5, 6); assert_eq!(set.hp(), -2); assert_eq!(set.get_stat(Statistic::HP), -2); assert_eq!(set.attack(), 2); @@ -275,7 +373,7 @@ mod tests { #[test] fn create_clamped_set_value() { - let mut set = ClampedStatisticSet::::new(1, 2, 3, 4, 5, 6); + let set = ClampedStatisticSet::::new(1, 2, 3, 4, 5, 6); set.set_stat(Statistic::SpecialAttack, 20); assert_eq!(set.special_attack(), 4); set.set_stat(Statistic::SpecialAttack, -10); @@ -284,7 +382,7 @@ mod tests { #[test] fn create_clamped_increase_value() { - let mut set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); + let set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); let mut has_changed = set.increase_stat(Statistic::SpecialAttack, 20); assert!(has_changed); assert_eq!(set.special_attack(), 4); @@ -295,7 +393,7 @@ mod tests { #[test] fn create_clamped_decrease_value() { - let mut set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); + let set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); let mut has_changed = set.decrease_stat(Statistic::SpecialAttack, 20); assert!(has_changed); assert_eq!(set.special_attack(), -2); diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs index 0b32111..ceb738e 100644 --- a/tests/common/library_loader.rs +++ b/tests/common/library_loader.rs @@ -8,7 +8,7 @@ use pkmn_lib::dynamic_data::libraries::misc_library::Gen7MiscLibrary; use pkmn_lib::static_data::{ Ability, AbilityLibrary, BattleItemCategory, DataLibrary, EffectParameter, Form, GrowthRateLibrary, Item, ItemLibrary, LearnableMoves, LibrarySettings, LookupGrowthRate, MoveData, MoveLibrary, Nature, NatureLibrary, - SecondaryEffect, Species, StaticData, Statistic, StatisticSet, TypeLibrary, + SecondaryEffect, Species, StaticData, StaticStatisticSet, Statistic, TypeLibrary, }; use pkmn_lib::StringKey; use project_root::get_project_root; @@ -301,12 +301,12 @@ fn parse_form(name: StringKey, value: &Value, library: &mut StaticData) -> Form ) } -fn parse_statistics(value: &Value) -> StatisticSet +fn parse_statistics(value: &Value) -> StaticStatisticSet where T: PrimInt + TryFrom, >::Error: Debug, { - StatisticSet::new( + StaticStatisticSet::new( >::try_from(value.get("hp").unwrap_or(&Value::Number(0.into())).as_u64().unwrap()).unwrap(), >::try_from( value diff --git a/tests/common/test_step.rs b/tests/common/test_step.rs index fe5fbc6..04e5568 100644 --- a/tests/common/test_step.rs +++ b/tests/common/test_step.rs @@ -49,7 +49,7 @@ impl TestStep { let mut used_move = None; let pokemon_guard = p.read(); for learned_move in pokemon_guard.learned_moves().iter().flatten() { - if learned_move.read().move_data().name() == &StringKey::new(use_move) { + if learned_move.move_data().name() == &StringKey::new(use_move) { used_move = Some(learned_move.clone()); break; }