From 5576bc8b804eea9ccb923f4dd85a7d2f292ed836 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 18 Jun 2022 15:52:39 +0200 Subject: [PATCH] More work on using interior mutability instead of exterior mutability for dynamic types (Battle, Pokemon, etc). --- Cargo.toml | 1 + src/dynamic_data/choices/mod.rs | 28 +-- src/dynamic_data/flow/choice_queue.rs | 2 +- src/dynamic_data/flow/target_resolver.rs | 3 +- src/dynamic_data/flow/turn_runner.rs | 50 ++-- .../libraries/battle_stat_calculator.rs | 39 +++- src/dynamic_data/libraries/damage_library.rs | 47 ++-- src/dynamic_data/libraries/misc_library.rs | 9 +- src/dynamic_data/models/battle.rs | 77 ++++--- src/dynamic_data/models/battle_party.rs | 4 +- src/dynamic_data/models/battle_random.rs | 7 +- src/dynamic_data/models/battle_side.rs | 64 +++--- src/dynamic_data/models/executing_move.rs | 24 +- src/dynamic_data/models/pokemon.rs | 213 +++++++++--------- src/dynamic_data/models/pokemon_builder.rs | 2 +- src/dynamic_data/models/pokemon_party.rs | 15 +- src/dynamic_data/script_handling/script.rs | 111 +++------ src/lib.rs | 1 + tests/common/data_getter.rs | 1 - tests/common/test_case.rs | 3 +- tests/common/test_step.rs | 8 +- 21 files changed, 324 insertions(+), 385 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f833868..3778407 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ rand_pcg = "0.3.1" hashbrown = "0.12.1" indexmap = "1.8.2" parking_lot = "0.12.1" +atomic = "0.5.1" serde = { version = "1.0.137", optional = true, features = ["derive"] } [dev-dependencies] diff --git a/src/dynamic_data/choices/mod.rs b/src/dynamic_data/choices/mod.rs index 4c833d8..f91183e 100644 --- a/src/dynamic_data/choices/mod.rs +++ b/src/dynamic_data/choices/mod.rs @@ -8,7 +8,7 @@ use std::sync::Arc; #[derive(Debug)] struct CommonChoiceData<'user, 'library> { - user: Arc>>, + user: Arc>, speed: u32, random_value: u32, has_failed: bool, @@ -44,7 +44,7 @@ impl<'user, 'library> TurnChoice<'user, 'library> { } } - pub fn user(&self) -> &Arc>> { + pub fn user(&self) -> &Arc> { &self.choice_data().user } @@ -134,7 +134,7 @@ pub struct MoveChoice<'user, 'library> { impl<'user, 'library> MoveChoice<'user, 'library> { pub fn new( - user: Arc>>, + user: Arc>, used_move: Arc>, target_side: u8, target_index: u8, @@ -168,7 +168,7 @@ impl<'user, 'library> MoveChoice<'user, 'library> { pub fn priority(&self) -> i8 { self.priority } - pub fn user(&self) -> &Arc>> { + pub fn user(&self) -> &Arc> { &self.choice_data.user } pub fn script(&self) -> &ScriptContainer { @@ -182,7 +182,7 @@ impl<'user, 'library> MoveChoice<'user, 'library> { impl<'user, 'library> ScriptSource<'user> for MoveChoice<'user, 'library> { fn get_script_count(&self) -> usize { - 1 + self.choice_data.user.read().get_script_count() + 1 + self.choice_data.user.get_script_count() } fn get_script_source_data(&self) -> &RwLock { @@ -195,7 +195,7 @@ impl<'user, 'library> ScriptSource<'user> for MoveChoice<'user, 'library> { fn collect_scripts(&self, scripts: &mut Vec) { self.get_own_scripts(scripts); - self.choice_data.user.read().collect_scripts(scripts); + self.choice_data.user.collect_scripts(scripts); } } @@ -205,7 +205,7 @@ pub struct ItemChoice<'user, 'library> { } impl<'user, 'library> ItemChoice<'user, 'library> { - pub fn new(user: Arc>>) -> Self { + pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, @@ -230,7 +230,7 @@ impl<'user, 'library> ScriptSource<'user> for ItemChoice<'user, 'library> { fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { - self.choice_data.user.read().collect_scripts(scripts); + self.choice_data.user.collect_scripts(scripts); } } @@ -240,7 +240,7 @@ pub struct SwitchChoice<'user, 'library> { } impl<'user, 'library> SwitchChoice<'user, 'library> { - pub fn new(user: Arc>>) -> Self { + pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, @@ -265,7 +265,7 @@ impl<'user, 'library> ScriptSource<'user> for SwitchChoice<'user, 'library> { fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { - self.choice_data.user.read().collect_scripts(scripts); + self.choice_data.user.collect_scripts(scripts); } } @@ -275,7 +275,7 @@ pub struct FleeChoice<'user, 'library> { } impl<'user, 'library> FleeChoice<'user, 'library> { - pub fn new(user: Arc>>) -> Self { + pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, @@ -300,7 +300,7 @@ impl<'user, 'library> ScriptSource<'user> for FleeChoice<'user, 'library> { fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { - self.choice_data.user.read().collect_scripts(scripts); + self.choice_data.user.collect_scripts(scripts); } } @@ -310,7 +310,7 @@ pub struct PassChoice<'user, 'library> { } impl<'user, 'library> PassChoice<'user, 'library> { - pub fn new(user: Arc>>) -> Self { + pub fn new(user: Arc>) -> Self { Self { choice_data: Box::new(CommonChoiceData { user, @@ -335,7 +335,7 @@ impl<'user, 'library> ScriptSource<'user> for PassChoice<'user, 'library> { fn get_own_scripts(&self, _scripts: &mut Vec) {} fn collect_scripts(&self, scripts: &mut Vec) { - self.choice_data.user.read().collect_scripts(scripts); + self.choice_data.user.collect_scripts(scripts); } } diff --git a/src/dynamic_data/flow/choice_queue.rs b/src/dynamic_data/flow/choice_queue.rs index 49bd474..cb4f37d 100644 --- a/src/dynamic_data/flow/choice_queue.rs +++ b/src/dynamic_data/flow/choice_queue.rs @@ -19,7 +19,7 @@ impl<'battle, 'library> ChoiceQueue<'battle, 'library> { } pub fn peek(&mut self) -> &'battle TurnChoice { - &self.queue[self.current].as_ref().unwrap() + self.queue[self.current].as_ref().unwrap() } pub fn has_next(&self) -> bool { diff --git a/src/dynamic_data/flow/target_resolver.rs b/src/dynamic_data/flow/target_resolver.rs index e850d2d..a4568a2 100644 --- a/src/dynamic_data/flow/target_resolver.rs +++ b/src/dynamic_data/flow/target_resolver.rs @@ -2,10 +2,9 @@ use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::pokemon::Pokemon; use crate::static_data::MoveTarget; use num_traits::abs; -use parking_lot::RwLock; use std::sync::Arc; -pub type TargetList<'own, 'library> = Vec>>>>; +pub type TargetList<'own, 'library> = Vec>>>; fn get_all_targets<'b, 'library>(battle: &Battle<'b, 'library>) -> TargetList<'b, 'library> { let mut v = Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize); diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index 37daa5a..a41f9c6 100644 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -7,14 +7,13 @@ use crate::dynamic_data::models::executing_move::ExecutingMove; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::{ScriptSource, ScriptWrapper}; use crate::static_data::{DataLibrary, MoveCategory}; -use crate::{run_scripts, script_hook, script_hook_on_lock, PkmnResult}; -use parking_lot::RwLock; +use crate::{run_scripts, script_hook, PkmnResult}; use std::ops::{Deref, DerefMut}; use std::sync::Arc; impl<'own, 'library> Battle<'own, 'library> { - pub(crate) fn run_turn(&mut self) -> PkmnResult<()> { - let mut choice_queue = self.current_turn_queue().as_ref().unwrap(); + pub(crate) fn run_turn(&self) -> PkmnResult<()> { + let choice_queue = self.current_turn_queue(); // We are now at the very beginning of a turn. We have assigned speeds and priorities to all // choices, and put them in the correct order. @@ -23,17 +22,16 @@ impl<'own, 'library> Battle<'own, 'library> { // is primarily intended to be used to reset variables on a script (for example scripts that need // to check whether a pokemon was hit this turn. By resetting here, and setting a variable to true // they can then know this later on.) - for choice in choice_queue.get_queue().iter().flatten() { + for choice in choice_queue.read().as_ref().unwrap().get_queue().iter().flatten() { script_hook!(on_before_turn, choice, choice); } // Now we can properly begin executing choices. // One by one dequeue the turns, and run them. If the battle has ended we do not want to // continue running however. - while choice_queue.has_next() && !self.has_ended() { - let choice = self.current_turn_queue_mut().as_mut().unwrap().dequeue(); + while choice_queue.read().as_ref().unwrap().has_next() && !self.has_ended() { + let choice = choice_queue.write().as_mut().unwrap().dequeue(); self.execute_choice(&choice)?; - choice_queue = self.current_turn_queue().as_ref().unwrap(); } // If the battle is not ended, we have arrived at the normal end of a turn. and thus want @@ -47,7 +45,7 @@ impl<'own, 'library> Battle<'own, 'library> { for side in self.sides() { for pokemon in side.pokemon().iter().flatten() { scripts = Vec::new(); - pokemon.read().get_own_scripts(&mut scripts); + pokemon.get_own_scripts(&mut scripts); run_scripts!(on_end_turn, scripts,); } scripts = Vec::new(); @@ -69,7 +67,7 @@ impl<'own, 'library> Battle<'own, 'library> { return Ok(()); } { - let user = choice.user().read(); + let user = choice.user(); if !user.is_usable() { return Ok(()); } @@ -149,11 +147,11 @@ impl<'own, 'library> Battle<'own, 'library> { fn handle_move_for_target( &self, executing_move: &mut ExecutingMove<'_, 'own, 'library>, - target: &Arc>>, + target: &Arc>, ) -> PkmnResult<()> { { let mut fail = false; - script_hook_on_lock!(fail_incoming_move, target, executing_move, target, &mut fail); + script_hook!(fail_incoming_move, target, executing_move, target, &mut fail); if fail { // TODO: Add fail handling return Ok(()); @@ -161,7 +159,7 @@ impl<'own, 'library> Battle<'own, 'library> { } { let mut invulnerable = false; - script_hook_on_lock!(is_invulnerable, target, executing_move, target, &mut invulnerable); + script_hook!(is_invulnerable, target, executing_move, target, &mut invulnerable); if invulnerable { // TODO: Add fail handling return Ok(()); @@ -169,9 +167,9 @@ impl<'own, 'library> Battle<'own, 'library> { } let number_of_hits = executing_move.number_of_hits(); if number_of_hits == 0 { - script_hook_on_lock!(on_move_miss, target, executing_move, target); + script_hook!(on_move_miss, target, executing_move, target); self.event_hook().trigger(Event::Miss { - user: executing_move.user().read().deref(), + user: executing_move.user().deref(), }); return Ok(()); } @@ -181,10 +179,10 @@ impl<'own, 'library> Battle<'own, 'library> { if self.has_ended() { return Ok(()); } - if executing_move.user().read().is_fainted() { + if executing_move.user().is_fainted() { break; } - if target.read().is_fainted() { + if target.is_fainted() { break; } { @@ -204,7 +202,7 @@ impl<'own, 'library> Battle<'own, 'library> { .library() .static_data() .types() - .get_effectiveness(hit_type, target.read().types()); + .get_effectiveness(hit_type, target.types()); script_hook!( change_effectiveness, executing_move, @@ -225,7 +223,7 @@ impl<'own, 'library> Battle<'own, 'library> { hit_index, &mut block_critical ); - script_hook_on_lock!( + script_hook!( block_incoming_critical, target, executing_move, @@ -278,7 +276,7 @@ impl<'own, 'library> Battle<'own, 'library> { let mut damage = executing_move .get_hit_from_raw_index(target_hit_stat + hit_index as usize) .damage(); - let current_health = target.read().current_health(); + let current_health = target.current_health(); if damage > current_health { damage = current_health; executing_move @@ -286,16 +284,16 @@ impl<'own, 'library> Battle<'own, 'library> { .set_damage(damage); } if damage > 0 { - target.write().damage(damage, DamageSource::AttackDamage); - if !target.read().is_fainted() { - script_hook_on_lock!(on_incoming_hit, target, executing_move, target, hit_index); + target.damage(damage, DamageSource::AttackDamage); + if !target.is_fainted() { + script_hook!(on_incoming_hit, target, executing_move, target, hit_index); } else { script_hook!(on_opponent_faints, executing_move, executing_move, target, hit_index); } - if executing_move.use_move().has_secondary_effect() && !target.read().is_fainted() { + if executing_move.use_move().has_secondary_effect() && !target.is_fainted() { let mut prevent_secondary = false; - script_hook_on_lock!( + script_hook!( prevent_secondary_effect, target, executing_move, @@ -329,7 +327,7 @@ impl<'own, 'library> Battle<'own, 'library> { } } - if !executing_move.user().read().is_fainted() { + if !executing_move.user().is_fainted() { script_hook!(on_after_hits, executing_move, executing_move, target); } diff --git a/src/dynamic_data/libraries/battle_stat_calculator.rs b/src/dynamic_data/libraries/battle_stat_calculator.rs index 2730258..b83ce47 100644 --- a/src/dynamic_data/libraries/battle_stat_calculator.rs +++ b/src/dynamic_data/libraries/battle_stat_calculator.rs @@ -7,15 +7,22 @@ use std::sync::atomic::AtomicU32; pub struct BattleStatCalculator {} impl BattleStatCalculator { - pub fn calculate_flat_stats(&self, pokemon: &Pokemon) -> StatisticSet { - StatisticSet::::new( - self.calculate_health_stat(pokemon), - self.calculate_other_stat(pokemon, Statistic::Attack), + pub fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet) { + stats.set_stat(Statistic::HP, self.calculate_health_stat(pokemon)); + stats.set_stat(Statistic::Attack, self.calculate_other_stat(pokemon, Statistic::Attack)); + stats.set_stat( + Statistic::Defense, self.calculate_other_stat(pokemon, Statistic::Defense), + ); + stats.set_stat( + Statistic::SpecialAttack, self.calculate_other_stat(pokemon, Statistic::SpecialAttack), + ); + stats.set_stat( + Statistic::SpecialDefense, self.calculate_other_stat(pokemon, Statistic::SpecialDefense), - self.calculate_other_stat(pokemon, Statistic::Speed), - ) + ); + stats.set_stat(Statistic::Speed, self.calculate_other_stat(pokemon, Statistic::Speed)); } pub fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 { @@ -26,15 +33,25 @@ impl BattleStatCalculator { } } - pub fn calculate_boosted_stats(&self, pokemon: &Pokemon) -> StatisticSet { - StatisticSet::::new( - self.calculate_boosted_stat(pokemon, Statistic::HP), + pub fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet) { + stats.set_stat(Statistic::HP, self.calculate_boosted_stat(pokemon, Statistic::HP)); + stats.set_stat( + Statistic::Attack, self.calculate_boosted_stat(pokemon, Statistic::Attack), + ); + stats.set_stat( + Statistic::Defense, self.calculate_boosted_stat(pokemon, Statistic::Defense), + ); + stats.set_stat( + Statistic::SpecialAttack, self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack), + ); + stats.set_stat( + Statistic::SpecialDefense, self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense), - self.calculate_boosted_stat(pokemon, Statistic::Speed), - ) + ); + stats.set_stat(Statistic::Speed, self.calculate_boosted_stat(pokemon, Statistic::Speed)); } pub fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 { diff --git a/src/dynamic_data/libraries/damage_library.rs b/src/dynamic_data/libraries/damage_library.rs index 94b861f..86c59b1 100644 --- a/src/dynamic_data/libraries/damage_library.rs +++ b/src/dynamic_data/libraries/damage_library.rs @@ -1,9 +1,8 @@ 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::script_hook; 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 { @@ -11,7 +10,7 @@ pub trait DamageLibrary: std::fmt::Debug { fn get_damage( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, hit_data: &HitData, ) -> u32; @@ -19,7 +18,7 @@ pub trait DamageLibrary: std::fmt::Debug { fn get_base_power( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, hit_data: &HitData, ) -> u8; @@ -27,7 +26,7 @@ pub trait DamageLibrary: std::fmt::Debug { fn get_stat_modifier( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, hit_data: &HitData, ) -> f32; @@ -35,7 +34,7 @@ pub trait DamageLibrary: std::fmt::Debug { fn get_damage_modifier( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, hit_data: &HitData, ) -> f32; @@ -60,7 +59,7 @@ impl DamageLibrary for Gen7DamageLibrary { fn get_damage( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, hit_data: &HitData, ) -> u32 { @@ -68,7 +67,7 @@ impl DamageLibrary for Gen7DamageLibrary { return 0; } - let level_modifier = ((2.0 * executing_move.user().read().level() as f32) / 5.0).floor() + 2.0; + let level_modifier = ((2.0 * executing_move.user().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); @@ -94,18 +93,11 @@ impl DamageLibrary for Gen7DamageLibrary { } if self.has_randomness { - let random_percentage = 85 - + executing_move - .user() - .read() - .get_battle() - .unwrap() - .random() - .get_between(0, 16); + let random_percentage = 85 + executing_move.user().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()) { + if executing_move.user().types().contains(&hit_data.move_type()) { let mut stab_modifier = 1.5; script_hook!( change_stab_modifier, @@ -139,7 +131,7 @@ impl DamageLibrary for Gen7DamageLibrary { hit_number, &mut damage ); - script_hook_on_lock!( + script_hook!( change_incoming_damage, target, executing_move, @@ -153,7 +145,7 @@ impl DamageLibrary for Gen7DamageLibrary { fn get_base_power( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, _hit_data: &HitData, ) -> u8 { @@ -176,7 +168,7 @@ impl DamageLibrary for Gen7DamageLibrary { fn get_stat_modifier( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, hit_data: &HitData, ) -> f32 { @@ -199,7 +191,7 @@ impl DamageLibrary for Gen7DamageLibrary { defensive_stat = Statistic::SpecialDefense; } let mut bypass_defensive_stat_boost = - hit_data.is_critical() && target.read().stat_boost().get_stat(defensive_stat) > 0; + hit_data.is_critical() && target.stat_boost().get_stat(defensive_stat) > 0; script_hook!( bypass_defensive_stat_boost, executing_move, @@ -208,8 +200,7 @@ impl DamageLibrary for Gen7DamageLibrary { 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; + let mut bypass_offensive_stat_boost = hit_data.is_critical() && user.stat_boost().get_stat(offensive_stat) > 0; script_hook!( bypass_offensive_stat_boost, executing_move, @@ -220,15 +211,15 @@ impl DamageLibrary for Gen7DamageLibrary { ); let mut defensive_stat = if bypass_defensive_stat_boost { - target.read().flat_stats().get_stat(offensive_stat) + target.flat_stats().get_stat(offensive_stat) } else { - target.read().boosted_stats().get_stat(offensive_stat) + target.boosted_stats().get_stat(offensive_stat) }; let mut offensive_stat = if bypass_offensive_stat_boost { - user.read().flat_stats().get_stat(offensive_stat) + user.flat_stats().get_stat(offensive_stat) } else { - user.read().boosted_stats().get_stat(offensive_stat) + user.boosted_stats().get_stat(offensive_stat) }; script_hook!( @@ -264,7 +255,7 @@ impl DamageLibrary for Gen7DamageLibrary { fn get_damage_modifier( &self, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, _hit_data: &HitData, ) -> f32 { diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs index efe05da..41b4e1c 100644 --- a/src/dynamic_data/libraries/misc_library.rs +++ b/src/dynamic_data/libraries/misc_library.rs @@ -7,7 +7,6 @@ use crate::dynamic_data::script_handling::ScriptSource; use crate::static_data::{MoveCategory, MoveData, MoveTarget, SecondaryEffect}; use crate::{script_hook, StringKey}; use hashbrown::HashSet; -use parking_lot::RwLock; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -16,13 +15,13 @@ pub trait MiscLibrary<'library> { &self, battle: &Battle, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, ) -> bool; fn can_flee(&self, choice: &SwitchChoice) -> bool; fn replacement_move<'func>( &'func self, - user: &Arc>>, + user: &Arc>, target_side: u8, target_index: u8, ) -> TurnChoice<'func, 'library>; @@ -78,7 +77,7 @@ impl<'library> MiscLibrary<'library> for Gen7MiscLibrary<'library> { &self, battle: &Battle, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, ) -> bool { if executing_move.use_move().category() == MoveCategory::Status { @@ -109,7 +108,7 @@ impl<'library> MiscLibrary<'library> for Gen7MiscLibrary<'library> { fn replacement_move<'func>( &'func self, - user: &Arc>>, + user: &Arc>, target_side: u8, target_index: u8, ) -> TurnChoice<'func, 'library> { diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 2ad09ad..51dc327 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -14,8 +14,10 @@ use crate::dynamic_data::script_handling::script_set::ScriptSet; use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::{script_hook, PkmnResult, ScriptCategory, StringKey}; +use atomic::Atomic; use parking_lot::RwLock; use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::Arc; #[derive(Debug)] @@ -27,14 +29,14 @@ pub struct Battle<'own, 'library> { pokemon_per_side: u8, sides: Vec>, random: BattleRandom, - current_turn_queue: Option>, - has_ended: bool, - result: BattleResult, + current_turn_queue: RwLock>>, + has_ended: AtomicBool, + result: Atomic, event_hook: EventHook, history_holder: Box, - current_turn: u32, + current_turn: AtomicU32, volatile_scripts: Arc>, - last_turn_time: chrono::Duration, + last_turn_time: Atomic, script_source_data: RwLock, } @@ -66,14 +68,14 @@ impl<'own, 'library> Battle<'own, 'library> { pokemon_per_side, sides, random, - current_turn_queue: None, - has_ended: false, - result: BattleResult::Inconclusive, + current_turn_queue: RwLock::new(None), + has_ended: AtomicBool::new(false), + result: Atomic::new(BattleResult::Inconclusive), event_hook: Default::default(), history_holder: Box::new(HistoryHolder {}), - current_turn: 0, + current_turn: AtomicU32::new(0), volatile_scripts: Default::default(), - last_turn_time: chrono::Duration::zero(), + last_turn_time: Atomic::new(chrono::Duration::zero()), script_source_data: Default::default(), }; @@ -111,10 +113,10 @@ impl<'own, 'library> Battle<'own, 'library> { &self.random } pub fn has_ended(&self) -> bool { - self.has_ended + self.has_ended.load(Ordering::Relaxed) } pub fn result(&self) -> BattleResult { - self.result + self.result.load(Ordering::Relaxed) } pub fn event_hook(&self) -> &EventHook { &self.event_hook @@ -123,19 +125,16 @@ impl<'own, 'library> Battle<'own, 'library> { &self.history_holder } pub fn current_turn(&self) -> u32 { - self.current_turn + self.current_turn.load(Ordering::Relaxed) } pub fn last_turn_time(&self) -> chrono::Duration { - self.last_turn_time + self.last_turn_time.load(Ordering::Relaxed) } - pub fn current_turn_queue(&self) -> &Option> { + pub fn current_turn_queue(&self) -> &RwLock>> { &self.current_turn_queue } - pub fn current_turn_queue_mut(&mut self) -> &mut Option> { - &mut self.current_turn_queue - } - pub fn get_pokemon(&self, side: u8, index: u8) -> &Option>>> { + pub fn get_pokemon(&self, side: u8, index: u8) -> &Option>> { let side = self.sides.get(side as usize); if side.is_none() { return &None; @@ -156,8 +155,8 @@ impl<'own, 'library> Battle<'own, 'library> { false } - pub fn validate_battle_state(&mut self) { - if self.has_ended { + pub fn validate_battle_state(&self) { + if self.has_ended() { return; } let mut surviving_side_exists = false; @@ -165,8 +164,8 @@ impl<'own, 'library> Battle<'own, 'library> { for (side_index, side) in self.sides.iter().enumerate() { // If any side has fled, the battle end. if side.has_fled() { - self.result = BattleResult::Inconclusive; - self.has_ended = true; + self.result.store(BattleResult::Inconclusive, Ordering::SeqCst); + self.has_ended.store(true, Ordering::SeqCst); return; } // If the side is not defeated @@ -181,18 +180,19 @@ impl<'own, 'library> Battle<'own, 'library> { } // Everyone died :( if !surviving_side_exists { - self.result = BattleResult::Inconclusive; + self.result.store(BattleResult::Inconclusive, Ordering::SeqCst); } // Someone survived, they won! else { - self.result = BattleResult::Conclusive(winning_side.unwrap()); + self.result + .store(BattleResult::Conclusive(winning_side.unwrap()), Ordering::SeqCst); } - self.has_ended = true; + self.has_ended.store(true, Ordering::SeqCst); } pub fn can_use(&self, choice: &TurnChoice) -> bool { // If the user is not usable, we obviously can;t use the choice. - if !choice.user().read().is_usable() { + if !choice.user().is_usable() { return false; } if let TurnChoice::Move(data) = choice { @@ -204,7 +204,7 @@ impl<'own, 'library> Battle<'own, 'library> { data.target_side(), data.target_index(), data.used_move().move_data().target(), - choice.user().read().deref(), + choice.user().deref(), ) { return false; } @@ -216,10 +216,10 @@ impl<'own, 'library> Battle<'own, 'library> { if !self.can_use(&choice) { return Ok(false); } - if !choice.user().read().is_on_battlefield() { + if !choice.user().is_on_battlefield() { return Ok(false); } - let side = choice.user().read().get_battle_side_index(); + let side = choice.user().get_battle_side_index(); if side.is_none() { return Ok(false); } @@ -228,7 +228,7 @@ impl<'own, 'library> Battle<'own, 'library> { Ok(true) } - fn check_choices_set_and_run(&mut self) -> PkmnResult<()> { + fn check_choices_set_and_run(&self) -> PkmnResult<()> { for side in &self.sides { if !side.all_choices_set() { return Ok(()); @@ -239,8 +239,9 @@ impl<'own, 'library> Battle<'own, 'library> { } let start_time = chrono::Utc::now(); let mut choices = Vec::with_capacity(self.number_of_sides as usize * self.pokemon_per_side as usize); - for side in &mut self.sides { - for choice_opt in side.choices_mut() { + for side in &self.sides { + let mut side_choices = side.choices().write(); + for choice_opt in side_choices.deref_mut() { if choice_opt.is_none() { panic!("Choice was none, but all choices were set? Logic error."); } @@ -264,22 +265,24 @@ impl<'own, 'library> Battle<'own, 'library> { choice.set_random_value(self.random.get() as u32); choices.push(choice_opt.take()); } + // Drop the lock guard, as we need to write into it in reset_choices. + drop(side_choices); side.reset_choices(); } - self.current_turn += 1; + self.current_turn.fetch_add(1, Ordering::SeqCst); choices.sort_unstable_by(|a, b| b.cmp(a)); - self.current_turn_queue = Some(ChoiceQueue::new(choices)); + self.current_turn_queue.write().replace(ChoiceQueue::new(choices)); { self.run_turn()?; } - self.current_turn_queue = None; + self.current_turn_queue.write().take(); self.event_hook.trigger(Event::EndTurn); let end_time = chrono::Utc::now(); let time = end_time - start_time; - self.last_turn_time = time; + self.last_turn_time.store(time, Ordering::SeqCst); Ok(()) } } diff --git a/src/dynamic_data/models/battle_party.rs b/src/dynamic_data/models/battle_party.rs index 91c9f33..87c1aaa 100644 --- a/src/dynamic_data/models/battle_party.rs +++ b/src/dynamic_data/models/battle_party.rs @@ -1,6 +1,5 @@ use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::models::pokemon_party::PokemonParty; -use parking_lot::RwLock; use std::sync::Arc; #[derive(Debug)] @@ -27,7 +26,6 @@ impl<'own, 'library> BattleParty<'own, 'library> { pub fn has_pokemon_not_in_field(&self) -> bool { for pokemon in self.party.pokemon().iter().flatten() { - let pokemon = pokemon.read(); if pokemon.is_usable() && !pokemon.is_on_battlefield() { return true; } @@ -35,7 +33,7 @@ impl<'own, 'library> BattleParty<'own, 'library> { false } - pub fn get_pokemon(&self, index: usize) -> &Option>>> { + pub fn get_pokemon(&self, index: usize) -> &Option>> { self.party.at(index) } } diff --git a/src/dynamic_data/models/battle_random.rs b/src/dynamic_data/models/battle_random.rs index a5501ba..b38b84f 100644 --- a/src/dynamic_data/models/battle_random.rs +++ b/src/dynamic_data/models/battle_random.rs @@ -1,9 +1,8 @@ use crate::dynamic_data::models::executing_move::ExecutingMove; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::ScriptSource; +use crate::script_hook; use crate::utils::random::Random; -use crate::{script_hook, script_hook_on_lock}; -use parking_lot::RwLock; use std::fmt::{Debug, Formatter}; use std::sync::{Arc, Mutex}; @@ -37,7 +36,7 @@ impl BattleRandom { &self, mut chance: f32, executing_move: &ExecutingMove, - target: &Arc>, + target: &Arc, hit_number: u8, ) -> bool { script_hook!( @@ -48,7 +47,7 @@ impl BattleRandom { hit_number, &mut chance ); - script_hook_on_lock!( + script_hook!( change_incoming_effect_chance, target, executing_move, diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index 103da70..fa13f35 100644 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -9,15 +9,17 @@ use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::{script_hook, PkmnResult, StringKey}; use parking_lot::RwLock; +use std::ops::Deref; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; #[derive(Debug)] pub struct BattleSide<'own, 'library> { index: u8, pokemon_per_side: u8, - pokemon: Vec>>>>, - choices: Vec>>, - fillable_slots: Vec, + pokemon: Vec>>>, + choices: RwLock>>>, + fillable_slots: Vec, choices_set: u8, battle: *mut Battle<'own, 'library>, has_fled_battle: bool, @@ -35,8 +37,9 @@ impl<'own, 'library> BattleSide<'own, 'library> { for _i in 0..pokemon_per_side { pokemon.push(None); choices.push(None); - fillable_slots.push(true); + fillable_slots.push(AtomicBool::new(false)); } + let choices = RwLock::new(choices); Self { index, @@ -45,7 +48,7 @@ impl<'own, 'library> BattleSide<'own, 'library> { choices, fillable_slots, choices_set: 0, - battle: 0 as *mut Battle, + battle: std::ptr::null_mut::(), has_fled_battle: false, volatile_scripts: Default::default(), script_source_data: Default::default(), @@ -62,17 +65,14 @@ impl<'own, 'library> BattleSide<'own, 'library> { pub fn pokemon_per_side(&self) -> u8 { self.pokemon_per_side } - pub fn pokemon(&self) -> &Vec>>>> { + pub fn pokemon(&self) -> &Vec>>> { &self.pokemon } - pub fn choices(&self) -> &Vec>> { + pub fn choices(&self) -> &RwLock>>> { &self.choices } - pub fn choices_mut(&mut self) -> &mut Vec>> { - &mut self.choices - } - pub fn fillable_slots(&self) -> &Vec { + pub fn fillable_slots(&self) -> &Vec { &self.fillable_slots } pub fn choices_set(&self) -> u8 { @@ -97,7 +97,7 @@ impl<'own, 'library> BattleSide<'own, 'library> { /// empty, but can't be filled by any party anymore. pub fn all_slots_filled(&self) -> bool { for (i, pokemon) in self.pokemon.iter().enumerate() { - if (!pokemon.is_none() || !pokemon.as_ref().unwrap().read().is_usable()) + if (!pokemon.is_none() || !pokemon.as_ref().unwrap().is_usable()) && self.battle().can_slot_be_filled(self.index, i as u8) { return false; @@ -109,8 +109,8 @@ impl<'own, 'library> BattleSide<'own, 'library> { pub fn set_choice(&mut self, choice: TurnChoice<'own, 'library>) { for (index, pokemon_slot) in self.pokemon.iter().enumerate() { if let Some(pokemon) = pokemon_slot { - if std::ptr::eq(pokemon.data_ptr(), choice.user().data_ptr()) { - self.choices[index] = Some(choice); + if std::ptr::eq(pokemon.deref(), choice.user().deref()) { + self.choices.write()[index] = Some(choice); self.choices_set += 1; return; } @@ -118,9 +118,10 @@ impl<'own, 'library> BattleSide<'own, 'library> { } } - pub fn reset_choices(&mut self) { - for i in 0..self.choices.len() { - self.choices[i] = None; + pub fn reset_choices(&self) { + let len = self.choices.read().len(); + for i in 0..len { + self.choices.write()[i] = None; } } @@ -128,17 +129,15 @@ impl<'own, 'library> BattleSide<'own, 'library> { self.pokemon[index as usize] = None; } - pub fn set_pokemon(&mut self, index: u8, pokemon: Option>>>) { + pub fn set_pokemon(&mut self, index: u8, pokemon: Option>>) { let old = &mut self.pokemon[index as usize]; if let Some(old_pokemon) = old { - let mut p = old_pokemon.write(); - script_hook!(on_remove, p,); - p.set_on_battlefield(false); + script_hook!(on_remove, old_pokemon,); + old_pokemon.set_on_battlefield(false); } self.pokemon[index as usize] = pokemon; let pokemon = &self.pokemon[index as usize]; - if let Some(pokemon_mutex) = pokemon { - let mut pokemon = pokemon_mutex.write(); + if let Some(pokemon) = pokemon { pokemon.set_battle_data(self.battle, self.index); pokemon.set_on_battlefield(true); pokemon.set_battle_index(index); @@ -148,10 +147,9 @@ impl<'own, 'library> BattleSide<'own, 'library> { if side.index() == self.index { continue; } - for opponent_mutex in side.pokemon().iter().flatten() { - let mut opponent = opponent_mutex.write(); - opponent.mark_opponent_as_seen(Arc::downgrade(pokemon_mutex)); - pokemon.mark_opponent_as_seen(Arc::downgrade(opponent_mutex)); + for opponent in side.pokemon().iter().flatten() { + opponent.mark_opponent_as_seen(Arc::downgrade(pokemon)); + pokemon.mark_opponent_as_seen(Arc::downgrade(opponent)); } } battle.event_hook().trigger(Event::Switch { @@ -171,22 +169,22 @@ impl<'own, 'library> BattleSide<'own, 'library> { pub fn is_pokemon_on_side(&self, pokemon: Arc>) -> bool { for p in self.pokemon.iter().flatten() { - if p.read().unique_identifier() == pokemon.unique_identifier() { + if std::ptr::eq(p.deref().deref(), pokemon.deref()) { return true; } } false } - pub fn mark_slot_as_unfillable(&mut self, index: u8) { - self.fillable_slots[index as usize] = false; + pub fn mark_slot_as_unfillable(&self, index: u8) { + self.fillable_slots[index as usize].store(false, Ordering::SeqCst); } pub fn is_slot_unfillable(&self, pokemon: Arc>) -> bool { for (i, slot) in self.pokemon.iter().enumerate() { if let Some(p) = slot { - if p.read().unique_identifier() == pokemon.unique_identifier() { - return self.fillable_slots[i]; + if std::ptr::eq(p.deref().deref(), pokemon.deref()) { + return self.fillable_slots[i].load(Ordering::Relaxed); } } } @@ -195,7 +193,7 @@ impl<'own, 'library> BattleSide<'own, 'library> { pub fn is_defeated(&self) -> bool { for fillable_slot in &self.fillable_slots { - if *fillable_slot { + if fillable_slot.load(Ordering::Relaxed) { return false; } } diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs index e21bcd9..3b1f41c 100644 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -6,6 +6,7 @@ use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, Scrip use crate::static_data::MoveData; use crate::{PkmnResult, PokemonError}; use parking_lot::RwLock; +use std::ops::Deref; use std::sync::Arc; #[derive(Default, Debug)] @@ -62,7 +63,7 @@ impl HitData { pub struct ExecutingMove<'own, 'battle, 'library> { number_of_hits: u8, hits: Vec, - user: Arc>>, + user: Arc>, chosen_move: Arc>, use_move: &'own MoveData, script: ScriptContainer, @@ -74,7 +75,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> { pub fn new( targets: &'own TargetList<'battle, 'library>, number_of_hits: u8, - user: Arc>>, + user: Arc>, chosen_move: Arc>, use_move: &'own MoveData, script: ScriptContainer, @@ -101,7 +102,7 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> { pub fn number_of_hits(&self) -> u8 { self.number_of_hits } - pub fn user(&self) -> &Arc>> { + pub fn user(&self) -> &Arc> { &self.user } pub fn chosen_move(&self) -> &Arc> { @@ -117,12 +118,12 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> { pub fn get_hit_data<'func>( &'func self, - for_target: &'func Arc>>, + for_target: &'func Arc>, hit: u8, ) -> PkmnResult<&'func HitData> { for (index, target) in self.targets.iter().enumerate() { if let Some(target) = target { - if std::ptr::eq(target.data_ptr(), for_target.data_ptr()) { + 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]); } @@ -131,22 +132,19 @@ impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> { Err(PokemonError::InvalidTargetRequested) } - pub fn is_pokemon_target(&self, pokemon: &Arc>>) -> bool { + pub fn is_pokemon_target(&self, pokemon: &Arc>) -> bool { for target in self.targets.iter().flatten() { - if std::ptr::eq(target.data_ptr(), pokemon.data_ptr()) { + if std::ptr::eq(target.deref().deref(), pokemon.deref().deref()) { return true; } } false } - pub(crate) fn get_index_of_target( - &self, - for_target: &Arc>>, - ) -> PkmnResult { + 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.data_ptr(), for_target.data_ptr()) { + if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) { let i = index * self.number_of_hits as usize; return Ok(i); } @@ -178,6 +176,6 @@ impl<'own, 'battle, 'library> ScriptSource<'own> for ExecutingMove<'own, 'battle fn collect_scripts(&self, scripts: &mut Vec) { self.get_own_scripts(scripts); - self.user.read().get_own_scripts(scripts); + self.user.get_own_scripts(scripts); } } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index c58b4ad..3c6daa8 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -19,17 +19,19 @@ use crate::static_data::statistic_set::{ClampedStatisticSet, StatisticSet}; use crate::static_data::DataLibrary; use crate::utils::random::Random; use crate::{script_hook, PkmnResult, ScriptCategory, StringKey}; +use atomic::Atomic; use parking_lot::RwLock; -use std::sync::atomic::{AtomicI8, AtomicU32, AtomicU8, Ordering}; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicBool, AtomicI8, AtomicU32, AtomicU8, Ordering}; use std::sync::{Arc, Weak}; #[derive(Debug)] pub struct PokemonBattleData<'pokemon, 'library> { battle: *mut Battle<'pokemon, 'library>, - battle_side_index: u8, - index: u8, - on_battle_field: bool, - seen_opponents: Vec>>>, + battle_side_index: AtomicU8, + index: AtomicU8, + on_battle_field: AtomicBool, + seen_opponents: RwLock>>>, } impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> { @@ -41,13 +43,16 @@ impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> { } pub fn battle_side_index(&self) -> u8 { - self.battle_side_index + self.battle_side_index.load(Ordering::Relaxed) } - pub fn on_battle_field(&mut self) -> &mut bool { - &mut self.on_battle_field + pub fn index(&self) -> u8 { + self.index.load(Ordering::Relaxed) } - pub fn seen_opponents(&mut self) -> &mut Vec>>> { - &mut self.seen_opponents + pub fn on_battle_field(&self) -> bool { + self.on_battle_field.load(Ordering::Relaxed) + } + pub fn seen_opponents(&self) -> &RwLock>>> { + &self.seen_opponents } } @@ -71,8 +76,8 @@ where held_item: Option<&'own Item>, current_health: AtomicU32, - weight: f32, - height: f32, + weight: Atomic, + height: Atomic, stat_boost: ClampedStatisticSet, flat_stats: StatisticSet, @@ -87,9 +92,9 @@ where is_ability_overridden: bool, override_ability: Option, - battle_data: Option>, + battle_data: RwLock>>, - moves: [Option>>; MAX_MOVES], + moves: RwLock<[Option>>; MAX_MOVES]>, allowed_experience: bool, types: Vec, @@ -141,8 +146,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { coloring, held_item: None, current_health: AtomicU32::new(1), - weight, - height, + weight: Atomic::new(weight), + height: Atomic::new(height), stat_boost: Default::default(), flat_stats: Default::default(), boosted_stats: Default::default(), @@ -153,8 +158,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { ability_index: ability, is_ability_overridden: false, override_ability: None, - battle_data: None, - moves: [None, None, None, None], + battle_data: RwLock::new(None), + moves: RwLock::new([None, None, None, None]), allowed_experience: false, types: form.types().to_vec(), is_egg: false, @@ -247,10 +252,10 @@ impl<'own, 'library> Pokemon<'own, 'library> { self.boosted_stats.hp() } pub fn weight(&self) -> f32 { - self.weight + self.weight.load(Ordering::Relaxed) } pub fn height(&self) -> f32 { - self.height + self.height.load(Ordering::Relaxed) } pub fn nickname(&self) -> &Option { &self.nickname @@ -261,7 +266,7 @@ 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) -> &RwLock<[Option>>; MAX_MOVES]> { &self.moves } pub fn status(&self) -> &ScriptContainer { @@ -284,17 +289,18 @@ impl<'own, 'library> Pokemon<'own, 'library> { } pub fn get_battle(&self) -> Option<&Battle<'own, 'library>> { - if let Some(data) = &self.battle_data { - Some(data.battle().unwrap()) + let r = self.battle_data.read(); + if let Some(data) = &r.deref() { + unsafe { data.battle.as_ref() } } else { None } } pub fn get_battle_side_index(&self) -> Option { - self.battle_data.as_ref().map(|data| data.battle_side_index) + self.battle_data.read().as_ref().map(|data| data.battle_side_index()) } pub fn get_battle_index(&self) -> Option { - self.battle_data.as_ref().map(|data| data.index) + self.battle_data.read().as_ref().map(|data| data.index()) } pub fn is_ability_overriden(&self) -> bool { self.is_ability_overridden @@ -316,13 +322,9 @@ impl<'own, 'library> Pokemon<'own, 'library> { &self.ability_script } - pub fn seen_opponents(&self) -> Option<&Vec>>>> { - if let Some(data) = &self.battle_data { - Some(&data.seen_opponents) - } else { - None - } - } + // pub fn seen_opponents(&self) -> &RwLock>> { + // &self.battle_data.read + // } pub fn allowed_experience_gain(&self) -> bool { self.allowed_experience } @@ -331,12 +333,16 @@ impl<'own, 'library> Pokemon<'own, 'library> { self.nature } - pub fn recalculate_flat_stats(&mut self) { - self.flat_stats = self.library.stat_calculator().calculate_flat_stats(self); + pub fn recalculate_flat_stats(&self) { + self.library + .stat_calculator() + .calculate_flat_stats(self, &self.flat_stats); self.recalculate_boosted_stats(); } - pub fn recalculate_boosted_stats(&mut self) { - self.boosted_stats = self.library.stat_calculator().calculate_boosted_stats(self); + pub fn recalculate_boosted_stats(&self) { + self.library + .stat_calculator() + .calculate_boosted_stats(self, &self.boosted_stats); } pub fn change_species(&mut self, species: &'own Species, form: &'own Form) { @@ -346,10 +352,10 @@ impl<'own, 'library> Pokemon<'own, 'library> { // If the pokemon is genderless, but it's new species is not, we want to set its gender if self.gender != Gender::Genderless && species.gender_rate() < 0.0 { // If we're in battle, use the battle random for predictability - if self.battle_data.is_some() { - let battle_data = self.battle_data.as_mut().unwrap(); - self.gender = - species.get_random_gender(&mut battle_data.battle().unwrap().random().get_rng().lock().unwrap()); + let r = self.battle_data.read(); + if let Some(data) = r.deref() { + let mut random = data.battle().unwrap().random().get_rng().lock().unwrap(); + self.gender = species.get_random_gender(random.deref_mut()); } else { // If we're not in battle, just use a new random. self.gender = species.get_random_gender(&mut Random::default()); @@ -359,7 +365,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { else if species.gender_rate() < 0.0 && self.gender != Gender::Genderless { self.gender = Gender::Genderless; } - if let Some(battle_data) = &self.battle_data { + let r = self.battle_data.read(); + if let Some(battle_data) = &r.deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::SpeciesChange { pokemon: self, @@ -380,8 +387,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { for t in form.types() { self.types.push(*t); } - self.weight = form.weight(); - self.height = form.height(); + self.weight.store(form.weight(), Ordering::SeqCst); + self.height.store(form.height(), Ordering::SeqCst); let ability_script = self .library @@ -409,7 +416,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { } // TODO: consider form specific attacks? - if let Some(battle_data) = &self.battle_data { + let r = self.battle_data.read(); + if let Some(battle_data) = r.deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::FormChange { pokemon: self, form }) } @@ -424,58 +432,59 @@ impl<'own, 'library> Pokemon<'own, 'library> { self.current_health() == 0 } - pub fn set_battle_data(&mut self, battle: *mut Battle<'own, 'library>, battle_side_index: u8) { - if let Some(battle_data) = &mut self.battle_data { + pub fn set_battle_data(&self, battle: *mut Battle<'own, 'library>, battle_side_index: u8) { + let mut w = self.battle_data.write(); + if let Some(battle_data) = w.deref_mut() { battle_data.battle = battle; - battle_data.battle_side_index = battle_side_index; + battle_data.battle_side_index.store(battle_side_index, Ordering::SeqCst); } else { - self.battle_data = Some(PokemonBattleData { + w.replace(PokemonBattleData { battle, - battle_side_index, - index: 0, - on_battle_field: false, + battle_side_index: AtomicU8::new(battle_side_index), + index: AtomicU8::new(0), + on_battle_field: AtomicBool::new(false), seen_opponents: Default::default(), - }) + }); } } - pub fn set_on_battlefield(&mut self, value: bool) { - if let Some(data) = &mut self.battle_data { - data.on_battle_field = value; + pub fn set_on_battlefield(&self, value: bool) { + let r = self.battle_data.read(); + if let Some(data) = &mut r.deref() { + data.on_battle_field.store(value, Ordering::SeqCst); if !value { self.volatile.write().clear(); - self.weight = self.form.weight(); - self.height = self.form.height(); + self.weight.store(self.form.weight(), Ordering::SeqCst); + self.height.store(self.form.height(), Ordering::SeqCst); } } } - pub fn set_battle_index(&mut self, index: u8) { - if let Some(data) = &mut self.battle_data { - data.index = index; + pub fn set_battle_index(&self, index: u8) { + let r = self.battle_data.read(); + if let Some(data) = r.deref() { + data.index.store(index, Ordering::SeqCst) } } pub fn is_on_battlefield(&self) -> bool { - if let Some(data) = &self.battle_data { - data.on_battle_field - } else { - false - } + self.battle_data.read().is_some_and(|a| a.on_battle_field()) } - pub fn mark_opponent_as_seen(&mut self, pokemon: Weak>>) { - if let Some(battle_data) = &mut self.battle_data { - for seen_opponent in &battle_data.seen_opponents { + pub fn mark_opponent_as_seen(&self, pokemon: Weak>) { + let r = self.battle_data.read(); + if let Some(battle_data) = &r.deref() { + let mut opponents = battle_data.seen_opponents().write(); + for seen_opponent in opponents.deref() { if seen_opponent.ptr_eq(&pokemon) { return; } } - battle_data.seen_opponents.push(pokemon); + opponents.push(pokemon); } } - pub fn damage(&mut self, mut damage: u32, source: DamageSource) { + pub fn damage(&self, mut damage: u32, source: DamageSource) { if damage > self.current_health() { damage = self.current_health(); } @@ -483,7 +492,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { return; } let new_health = self.current_health() - damage; - if let Some(battle_data) = &self.battle_data { + if let Some(battle_data) = &self.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { battle.event_hook().trigger(Event::Damage { pokemon: self, @@ -492,65 +501,53 @@ impl<'own, 'library> Pokemon<'own, 'library> { new_health, }); // TODO: register history - script_hook!(on_damage, self, self, source, self.current_health(), new_health); } } + if self.battle_data.read().is_some_and(|a| a.on_battle_field()) { + script_hook!(on_damage, self, self, source, self.current_health(), new_health); + } + self.current_health.store(new_health, Ordering::SeqCst); if self.is_fainted() && damage > 0 { self.on_faint(source); } } - pub fn on_faint(&mut self, source: DamageSource) { - if self.battle_data.is_some() && self.battle_data.as_ref().unwrap().battle().is_some() { - self.battle_data - .as_ref() - .unwrap() - .battle() - .unwrap() - .event_hook() - .trigger(Event::Faint { pokemon: self }); - script_hook!(on_faint, self, self, source); - script_hook!(on_remove, self,); + pub fn on_faint(&self, source: DamageSource) { + let r = self.battle_data.read(); + if let Some(battle_data) = r.deref() { + if let Some(battle) = battle_data.battle() { + battle.event_hook().trigger(Event::Faint { pokemon: self }); + script_hook!(on_faint, self, self, source); + script_hook!(on_remove, self,); - let side_index = self.battle_data.as_ref().unwrap().battle_side_index; - let index = self.battle_data.as_ref().unwrap().index; - if !self - .battle_data - .as_ref() - .unwrap() - .battle() - .unwrap() - .can_slot_be_filled(side_index, index) - { - self.battle_data.as_mut().unwrap().battle_mut().unwrap().sides_mut()[side_index as usize] - .mark_slot_as_unfillable(index); + if !battle.can_slot_be_filled(battle_data.battle_side_index(), battle_data.index()) { + battle.sides()[battle_data.battle_side_index() as usize] + .mark_slot_as_unfillable(battle_data.index()); + } + + battle.validate_battle_state(); } - self.battle_data - .as_mut() - .unwrap() - .battle_mut() - .unwrap() - .validate_battle_state(); } } - pub fn learn_move(&mut self, move_name: &StringKey, learn_method: MoveLearnMethod) { - let move_pos = self.learned_moves().iter().position(|a| a.is_none()); + pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) { + let mut learned_moves = self.learned_moves().write(); + let move_pos = learned_moves.iter().position(|a| a.is_none()); if move_pos.is_none() { 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(LearnedMove::new(move_data, learn_method))); + learned_moves[move_pos.unwrap()] = Some(Arc::new(LearnedMove::new(move_data, learn_method))); } } impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> { fn get_script_count(&self) -> usize { let mut c = 3; - if let Some(battle_data) = &self.battle_data { + if let Some(battle_data) = &self.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { - c += battle.sides()[battle_data.battle_side_index as usize].get_script_count(); + c += battle.sides()[battle_data.battle_side_index() as usize].get_script_count(); } } c @@ -569,9 +566,9 @@ impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> { fn collect_scripts(&self, scripts: &mut Vec) { self.get_own_scripts(scripts); - if let Some(battle_data) = &self.battle_data { + if let Some(battle_data) = &self.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { - battle.sides()[battle_data.battle_side_index as usize].collect_scripts(scripts); + battle.sides()[battle_data.battle_side_index() as usize].collect_scripts(scripts); } } } diff --git a/src/dynamic_data/models/pokemon_builder.rs b/src/dynamic_data/models/pokemon_builder.rs index 9e93a99..1fea05e 100644 --- a/src/dynamic_data/models/pokemon_builder.rs +++ b/src/dynamic_data/models/pokemon_builder.rs @@ -29,7 +29,7 @@ impl<'own> PokemonBuilder<'own> { pub fn build(self) -> Pokemon<'own, 'own> { let species = self.library.static_data().species().get(&self.species).unwrap(); let form = species.get_default_form(); - let mut p = Pokemon::new( + let p = Pokemon::new( self.library, species, form, diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs index 49f4e8d..408d380 100644 --- a/src/dynamic_data/models/pokemon_party.rs +++ b/src/dynamic_data/models/pokemon_party.rs @@ -1,10 +1,9 @@ use crate::dynamic_data::models::pokemon::Pokemon; -use parking_lot::RwLock; use std::sync::Arc; #[derive(Debug)] pub struct PokemonParty<'pokemon, 'library> { - pokemon: Vec>>>>, + pokemon: Vec>>>, } impl<'own, 'library> PokemonParty<'own, 'library> { @@ -16,11 +15,11 @@ impl<'own, 'library> PokemonParty<'own, 'library> { Self { pokemon } } - pub fn new_from_vec(pokemon: Vec>>>>) -> Self { + pub fn new_from_vec(pokemon: Vec>>>) -> Self { Self { pokemon } } - pub fn at(&self, index: usize) -> &Option>>> { + pub fn at(&self, index: usize) -> &Option>> { let opt = self.pokemon.get(index); if let Some(v) = opt { v @@ -36,8 +35,8 @@ impl<'own, 'library> PokemonParty<'own, 'library> { pub fn swap_into( &mut self, index: usize, - pokemon: Option>>>, - ) -> Option>>> { + pokemon: Option>>, + ) -> Option>> { if index >= self.pokemon.len() { return pokemon; } @@ -48,7 +47,7 @@ impl<'own, 'library> PokemonParty<'own, 'library> { pub fn has_usable_pokemon(&self) -> bool { for pokemon in self.pokemon.iter().flatten() { - if pokemon.read().is_usable() { + if pokemon.is_usable() { return true; } } @@ -59,7 +58,7 @@ impl<'own, 'library> PokemonParty<'own, 'library> { self.pokemon.len() } - pub fn pokemon(&self) -> &Vec>>>> { + pub fn pokemon(&self) -> &Vec>>> { &self.pokemon } diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 654c27a..0a17670 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -33,86 +33,51 @@ pub trait Script { fn fail_move(&mut self, _move: &ExecutingMove, _fail: &mut bool) {} fn stop_before_move(&mut self, _move: &ExecutingMove, _stop: &mut bool) {} fn on_before_move(&mut self, _move: &ExecutingMove) {} - fn fail_incoming_move(&mut self, _move: &ExecutingMove, _target: &Arc>, _fail: &mut bool) {} - fn is_invulnerable(&mut self, _move: &ExecutingMove, _target: &Arc>, _invulnerable: &mut bool) {} - fn on_move_miss(&mut self, _move: &ExecutingMove, _target: &Arc>) {} - fn change_move_type( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _move_type: &mut u8, - ) { - } + fn fail_incoming_move(&mut self, _move: &ExecutingMove, _target: &Arc, _fail: &mut bool) {} + fn is_invulnerable(&mut self, _move: &ExecutingMove, _target: &Arc, _invulnerable: &mut bool) {} + fn on_move_miss(&mut self, _move: &ExecutingMove, _target: &Arc) {} + fn change_move_type(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _move_type: &mut u8) {} fn change_effectiveness( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _effectiveness: &mut f32, ) { } - fn block_critical( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _block_critical: &mut bool, - ) { - } + fn block_critical(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _block_critical: &mut bool) {} fn block_incoming_critical( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _block_critical: &mut bool, ) { } - fn change_critical_stage( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _stage: &mut u8, - ) { - } + fn change_critical_stage(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _stage: &mut u8) {} fn change_critical_modifier( &mut self, _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _modifier: &mut f32, - ) { - } - fn change_stab_modifier( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _modifier: &mut f32, ) { } + fn change_stab_modifier(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) {} - fn change_base_power( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _base_power: &mut u8, - ) { - } + fn change_base_power(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _base_power: &mut u8) {} fn change_damage_stats_user( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, - _stats_user: &mut Arc>, + _stats_user: &mut Arc, ) { } fn bypass_defensive_stat_boost( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _bypass: &mut bool, ) { @@ -120,7 +85,7 @@ pub trait Script { fn bypass_offensive_stat_boost( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _bypass: &mut bool, ) { @@ -128,7 +93,7 @@ pub trait Script { fn change_offensive_stat_value( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _amount: &mut u32, ) { @@ -136,7 +101,7 @@ pub trait Script { fn change_defensive_stat_value( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _amount: &mut u32, ) { @@ -145,30 +110,17 @@ pub trait Script { fn change_damage_stat_modifier( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _modifier: &mut f32, ) { } - fn change_damage_modifier( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _modifier: &mut f32, - ) { + fn change_damage_modifier(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _modifier: &mut f32) { } - fn change_damage(&mut self, _move: &ExecutingMove, _target: &Arc>, _hit: u8, _damage: &mut u32) {} - fn change_incoming_damage( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _damage: &mut u32, - ) { - } - fn on_incoming_hit(&mut self, _move: &ExecutingMove, _target: &Arc>, _hit: u8) {} - fn on_opponent_faints(&mut self, _move: &ExecutingMove, _target: &Arc>, _hit: u8) {} + fn change_damage(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) {} + fn change_incoming_damage(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _damage: &mut u32) {} + fn on_incoming_hit(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} + fn on_opponent_faints(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} fn prevent_stat_boost_change( &mut self, _target: &Pokemon, @@ -189,29 +141,22 @@ pub trait Script { fn prevent_secondary_effect( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _prevent: &mut bool, ) { } - fn change_effect_chance( - &mut self, - _move: &ExecutingMove, - _target: &Arc>, - _hit: u8, - _chance: &mut f32, - ) { - } + fn change_effect_chance(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _chance: &mut f32) {} fn change_incoming_effect_chance( &mut self, _move: &ExecutingMove, - _target: &Arc>, + _target: &Arc, _hit: u8, _chance: &mut f32, ) { } - fn on_secondary_effect(&mut self, _move: &ExecutingMove, _target: &Arc>, _hit: u8) {} - fn on_after_hits(&mut self, _move: &ExecutingMove, _target: &Arc>) {} + fn on_secondary_effect(&mut self, _move: &ExecutingMove, _target: &Arc, _hit: u8) {} + fn on_after_hits(&mut self, _move: &ExecutingMove, _target: &Arc) {} fn prevent_self_switch(&mut self, _choice: &TurnChoice, _prevent: &mut bool) {} fn prevent_opponent_switch(&mut self, _choice: &TurnChoice, _prevent: &mut bool) {} fn on_fail(&mut self, _target: &Pokemon) {} diff --git a/src/lib.rs b/src/lib.rs index c2f9b8e..95dd6ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ #![feature(let_chains)] #![feature(once_cell)] #![feature(const_option)] +#![feature(is_some_with)] extern crate core; diff --git a/tests/common/data_getter.rs b/tests/common/data_getter.rs index c6d45d4..82809bc 100644 --- a/tests/common/data_getter.rs +++ b/tests/common/data_getter.rs @@ -14,7 +14,6 @@ impl TestDataGetter { .get_pokemon(index[0], index[1]) .as_ref() .unwrap() - .read() .current_health() .to_string(), } diff --git a/tests/common/test_case.rs b/tests/common/test_case.rs index f7a15ac..9d88cf1 100644 --- a/tests/common/test_case.rs +++ b/tests/common/test_case.rs @@ -1,5 +1,4 @@ use super::test_step::TestStep; -use parking_lot::RwLock; use pkmn_lib::defines::LevelInt; use pkmn_lib::dynamic_data::libraries::dynamic_library::DynamicLibrary; use pkmn_lib::dynamic_data::models::battle::Battle; @@ -47,7 +46,7 @@ impl TestCase { let pokemon = party .pokemon .iter() - .map(|a| Some(Arc::new(RwLock::new(a.to_pokemon(library))))) + .map(|a| Some(Arc::new(a.to_pokemon(library)))) .collect(); let indices = party.indices.iter().map(|a| (a[0], a[1])).collect(); parties.push((Arc::new(PokemonParty::new_from_vec(pokemon)), indices)); diff --git a/tests/common/test_step.rs b/tests/common/test_step.rs index 04e5568..2e5d4d2 100644 --- a/tests/common/test_step.rs +++ b/tests/common/test_step.rs @@ -42,24 +42,22 @@ impl TestStep { use_move, target, } => { - let p = battle.sides()[for_pokemon[0] as usize].pokemon()[for_pokemon[1] as usize] + let pokemon = battle.sides()[for_pokemon[0] as usize].pokemon()[for_pokemon[1] as usize] .as_ref() .unwrap() .clone(); let mut used_move = None; - let pokemon_guard = p.read(); - for learned_move in pokemon_guard.learned_moves().iter().flatten() { + for learned_move in pokemon.learned_moves().read().iter().flatten() { if learned_move.move_data().name() == &StringKey::new(use_move) { used_move = Some(learned_move.clone()); break; } } assert!(used_move.is_some()); - drop(pokemon_guard); assert!(battle .try_set_choice(TurnChoice::Move(MoveChoice::new( - p, + pokemon, used_move.unwrap(), target[0], target[1],