From ce33ec0649a3ee83d1c891a9c0e7c47b82f836bd Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 6 Jun 2022 13:54:59 +0200 Subject: [PATCH] Further massive amounts of work --- Cargo.toml | 4 +- src/dynamic_data/event_hooks/event_hook.rs | 5 + .../libraries/battle_stat_calculator.rs | 81 ++++++++++- src/dynamic_data/libraries/dynamic_library.rs | 27 +++- src/dynamic_data/libraries/mod.rs | 1 + src/dynamic_data/libraries/script_resolver.rs | 12 ++ src/dynamic_data/models/battle.rs | 11 +- src/dynamic_data/models/battle_party.rs | 28 +++- src/dynamic_data/models/battle_random.rs | 10 ++ src/dynamic_data/models/battle_side.rs | 119 +++++++++++++++- src/dynamic_data/models/mod.rs | 1 + src/dynamic_data/models/pokemon.rs | 86 +++++++++--- src/dynamic_data/models/pokemon_party.rs | 80 +++++++++++ src/dynamic_data/script_handling/mod.rs | 49 ++++--- src/dynamic_data/script_handling/script.rs | 2 + .../script_handling/script_set.rs | 67 ++++++++- .../script_handling/volatile_scripts.rs | 30 ++++ src/lib.rs | 16 ++- .../growth_rates/lookup_growth_rate.rs | 6 +- src/static_data/libraries/data_library.rs | 2 +- .../libraries/growth_rate_library.rs | 15 +- src/static_data/libraries/item_library.rs | 2 +- src/static_data/libraries/move_library.rs | 2 +- src/static_data/libraries/species_library.rs | 6 +- src/static_data/libraries/static_data.rs | 4 + src/static_data/libraries/type_library.rs | 2 +- src/static_data/mod.rs | 1 + src/static_data/natures.rs | 131 ++++++++++++++++++ src/static_data/species_data/form.rs | 2 +- .../species_data/learnable_moves.rs | 10 +- src/static_data/species_data/species.rs | 8 +- src/static_data/statistic_set.rs | 106 ++++++++++++++ src/static_data/statistics.rs | 2 +- 33 files changed, 848 insertions(+), 80 deletions(-) create mode 100644 src/dynamic_data/libraries/script_resolver.rs create mode 100644 src/dynamic_data/models/pokemon_party.rs create mode 100644 src/dynamic_data/script_handling/volatile_scripts.rs create mode 100644 src/static_data/natures.rs diff --git a/Cargo.toml b/Cargo.toml index cfb852b..294d6e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,4 +49,6 @@ rand_pcg = "0.3.1" maplit = "1.0.2" failure = "0.1.8" failure_derive = "0.1.8" -lazy_static = "1.4.0" \ No newline at end of file +lazy_static = "1.4.0" +hashbrown = "0.12.1" +indexmap = "1.8.2" diff --git a/src/dynamic_data/event_hooks/event_hook.rs b/src/dynamic_data/event_hooks/event_hook.rs index d3d28be..03ef36e 100644 --- a/src/dynamic_data/event_hooks/event_hook.rs +++ b/src/dynamic_data/event_hooks/event_hook.rs @@ -33,4 +33,9 @@ pub enum Event<'a> { index: u8, pokemon: Option>>>, }, + Swap { + side_index: u8, + index_a: u8, + index_b: u8, + }, } diff --git a/src/dynamic_data/libraries/battle_stat_calculator.rs b/src/dynamic_data/libraries/battle_stat_calculator.rs index 4d218aa..7bfe929 100644 --- a/src/dynamic_data/libraries/battle_stat_calculator.rs +++ b/src/dynamic_data/libraries/battle_stat_calculator.rs @@ -1,3 +1,80 @@ -pub trait BattleStatCalculator { - //fn is_critical(attack: &ExecutingMove, target: &Pokemon, hit: u8); +use crate::dynamic_data::models::pokemon::Pokemon; +use crate::static_data::statistic_set::StatisticSet; +use crate::static_data::statistics::Statistic; + +#[derive(Debug)] +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), + self.calculate_other_stat(pokemon, Statistic::Defense), + self.calculate_other_stat(pokemon, Statistic::SpecialAttack), + self.calculate_other_stat(pokemon, Statistic::SpecialDefense), + self.calculate_other_stat(pokemon, Statistic::Speed), + ) + } + + pub fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 { + if stat == Statistic::HP { + self.calculate_health_stat(pokemon) + } else { + self.calculate_other_stat(pokemon, stat) + } + } + + 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), + self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack), + self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense), + self.calculate_boosted_stat(pokemon, Statistic::Speed), + ) + } + + pub fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 { + (self.calculate_flat_stat(pokemon, stat) as f32 + * self.get_stat_boost_modifier(pokemon, stat)) as u32 + } + + fn calculate_health_stat(&self, pokemon: &Pokemon) -> u32 { + let base = pokemon.form().get_base_stat(Statistic::HP) as u32; + let iv = *pokemon.individual_values().hp() as u32; + let ev = *pokemon.effort_values().hp() as u32; + let level = *pokemon.level() as u32; + (((2 * base + iv + (ev / 4)) * level) / 100) + level + 10 + } + + fn calculate_other_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 { + let base = pokemon.form().get_base_stat(stat) as u32; + let iv = pokemon.individual_values().get_stat(stat) as u32; + let ev = pokemon.effort_values().get_stat(stat) as u32; + let level = *pokemon.level() as u32; + let unmodified = (((2 * base + iv + (ev / 4)) * level) / 100) + 5; + return (unmodified as f32 * pokemon.nature().get_stat_modifier(stat)) as u32; + } + + fn get_stat_boost_modifier(&self, pokemon: &Pokemon, stat: Statistic) -> f32 { + let boost = pokemon.stat_boost().get_stat(stat); + match boost { + -6 => 2.0 / 8.0, + -5 => 2.0 / 7.0, + -4 => 2.0 / 6.0, + -3 => 2.0 / 5.0, + -2 => 2.0 / 4.0, + -1 => 2.0 / 3.0, + 0 => 1.0, + 1 => 3.0 / 2.0, + 2 => 4.0 / 2.0, + 3 => 5.0 / 2.0, + 4 => 6.0 / 2.0, + 5 => 7.0 / 2.0, + 6 => 8.0 / 2.0, + _ => panic!("Stat boost was out of expected range of -6 to 6"), + } + } } diff --git a/src/dynamic_data/libraries/dynamic_library.rs b/src/dynamic_data/libraries/dynamic_library.rs index 3c8385d..71a1fb8 100644 --- a/src/dynamic_data/libraries/dynamic_library.rs +++ b/src/dynamic_data/libraries/dynamic_library.rs @@ -1,19 +1,42 @@ +use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator; +use crate::dynamic_data::libraries::script_resolver::ScriptCategory; +use crate::dynamic_data::script_handling::script::Script; use crate::static_data::libraries::static_data::StaticData; -use derive_getters::Getters; +use crate::PkmnResult; -#[derive(Getters, Debug)] +#[derive(Debug)] pub struct DynamicLibrary<'a> { static_data: StaticData<'a>, + stat_calculator: BattleStatCalculator, +} + +impl<'a> DynamicLibrary<'a> { + pub fn static_data(&self) -> &StaticData<'a> { + &self.static_data + } + pub fn stat_calculator(&self) -> &BattleStatCalculator { + &self.stat_calculator + } + + pub fn load_script( + &self, + _category: ScriptCategory, + _key: &str, + ) -> PkmnResult> { + todo!() + } } #[cfg(test)] pub mod test { + use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator; use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary; use crate::static_data::libraries::static_data; pub fn build<'a>() -> DynamicLibrary<'a> { DynamicLibrary { static_data: static_data::test::build(), + stat_calculator: BattleStatCalculator {}, } } } diff --git a/src/dynamic_data/libraries/mod.rs b/src/dynamic_data/libraries/mod.rs index aa0bc6c..91f0601 100644 --- a/src/dynamic_data/libraries/mod.rs +++ b/src/dynamic_data/libraries/mod.rs @@ -1,2 +1,3 @@ pub mod battle_stat_calculator; pub mod dynamic_library; +pub mod script_resolver; diff --git a/src/dynamic_data/libraries/script_resolver.rs b/src/dynamic_data/libraries/script_resolver.rs new file mode 100644 index 0000000..51b3480 --- /dev/null +++ b/src/dynamic_data/libraries/script_resolver.rs @@ -0,0 +1,12 @@ +pub trait ScriptResolver {} + +#[derive(Debug, Clone)] +pub enum ScriptCategory { + Move, + Ability, + Status, + Pokemon, + Battle, + Side, + ItemBattleTrigger, +} diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 61f7053..19c66f4 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -12,8 +12,9 @@ use std::sync::{Arc, RwLock}; #[derive(Getters, Debug)] pub struct Battle<'a> { + #[getter(skip)] library: &'a DynamicLibrary<'a>, - parties: Vec, + parties: Vec>, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, @@ -33,7 +34,7 @@ pub struct Battle<'a> { impl<'a> Battle<'a> { pub fn new( library: &'a DynamicLibrary<'a>, - parties: Vec, + parties: Vec>, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, @@ -59,7 +60,7 @@ impl<'a> Battle<'a> { event_hook: Default::default(), history_holder: Box::new(HistoryHolder {}), current_turn: 0, - volatile: ScriptSet {}, + volatile: ScriptSet::default(), last_turn_time: 0, })); @@ -77,4 +78,8 @@ impl<'a> Battle<'a> { pub fn can_slot_be_filled(&self) -> bool { todo!() } + + pub fn library(&self) -> &'a DynamicLibrary<'a> { + self.library + } } diff --git a/src/dynamic_data/models/battle_party.rs b/src/dynamic_data/models/battle_party.rs index 8dbef20..b9d94d5 100644 --- a/src/dynamic_data/models/battle_party.rs +++ b/src/dynamic_data/models/battle_party.rs @@ -1,2 +1,28 @@ +use crate::dynamic_data::models::pokemon_party::PokemonParty; + #[derive(Debug)] -pub struct BattleParty {} +pub struct BattleParty<'a> { + party: &'a PokemonParty<'a>, + responsible_indices: Vec<(u8, u8)>, +} + +impl<'a> BattleParty<'a> { + pub fn is_responsible_for_index(&self, side: u8, index: u8) -> bool { + for responsible_index in &self.responsible_indices { + if responsible_index.0 == side && responsible_index.1 == index { + return true; + } + } + false + } + + pub fn has_pokemon_not_in_field(&self) -> bool { + for pokemon in self.party.pokemon().iter().flatten() { + let pokemon = pokemon.read().unwrap(); + if pokemon.is_usable() && !pokemon.is_on_battlefield() { + return true; + } + } + false + } +} diff --git a/src/dynamic_data/models/battle_random.rs b/src/dynamic_data/models/battle_random.rs index 6932d23..1d34950 100644 --- a/src/dynamic_data/models/battle_random.rs +++ b/src/dynamic_data/models/battle_random.rs @@ -17,6 +17,16 @@ impl BattleRandom { pub fn get_rng(&self) -> &Mutex { &self.random } + + pub fn get(&self) -> i32 { + return self.get_rng().lock().unwrap().get(); + } + pub fn get_max(&self, max: i32) -> i32 { + return self.get_rng().lock().unwrap().get_max(max); + } + pub fn get_between(&self, min: i32, max: i32) -> i32 { + return self.get_rng().lock().unwrap().get_between(min, max); + } } impl Debug for BattleRandom { diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index 06f8bc2..f7a9808 100644 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -1,9 +1,13 @@ use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::event_hooks::event_hook::Event; use crate::dynamic_data::models::battle::Battle; +use crate::dynamic_data::models::battle_party::BattleParty; use crate::dynamic_data::models::pokemon::Pokemon; +use crate::dynamic_data::script_handling::script::Script; +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; -use crate::script_hook; +use crate::{script_hook, PkmnResult}; use derive_getters::Getters; use std::sync::{Arc, RwLock, Weak}; @@ -16,7 +20,8 @@ pub struct BattleSide<'a> { fillable_slots: Vec, choices_set: u8, battle: Weak>>, - has_fled: bool, + has_fled_battle: bool, + volatile_scripts: Arc>, } impl<'a> BattleSide<'a> { @@ -39,7 +44,8 @@ impl<'a> BattleSide<'a> { fillable_slots, choices_set: 0, battle, - has_fled: false, + has_fled_battle: false, + volatile_scripts: Default::default(), } } @@ -136,6 +142,113 @@ impl<'a> BattleSide<'a> { } false } + + pub fn mark_slot_as_unfillable(&mut self, pokemon: Arc>) { + for (i, slot) in self.pokemon.iter().enumerate() { + if let Some(p) = slot { + if p.read().unwrap().unique_identifier() == pokemon.unique_identifier() { + self.fillable_slots[i] = false; + return; + } + } + } + } + + 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().unwrap().unique_identifier() == pokemon.unique_identifier() { + return self.fillable_slots[i]; + } + } + } + false + } + + pub fn is_defeated(&self) -> bool { + for fillable_slot in &self.fillable_slots { + if *fillable_slot { + return false; + } + } + true + } + + pub fn has_fled(&self) -> bool { + self.has_fled_battle + } + + pub fn mark_as_fled(&mut self) { + self.has_fled_battle = true; + } + + pub fn get_random_creature_index(&self) -> u8 { + // TODO: Consider adding parameter to only get index for available creatures. + self.battle + .upgrade() + .unwrap() + .read() + .unwrap() + .random() + .get_max(self.pokemon_per_side as i32) as u8 + } + + pub fn swap_positions(&mut self, a: u8, b: u8) -> bool { + // If out of range, don't allow swapping. + if a >= self.pokemon_per_side || b >= self.pokemon_per_side { + return false; + } + // If the two indices are the same, don't allow swapping. + if a == b { + return false; + } + + let battle = self.battle.upgrade().unwrap(); + let battle = battle.read().unwrap(); + // Fetch parties for the two indices. + let mut party_a = None; + let mut party_b = None; + for party in battle.parties() { + if party.is_responsible_for_index(self.index, a) { + party_a = Some(party); + } + if party.is_responsible_for_index(self.index, b) { + party_b = Some(party); + } + } + // If either of the parties does not exist, fail. + if party_a.is_none() || party_b.is_none() { + return false; + } + // Don't allow swapping if different parties are responsible for the indices. + if party_a.unwrap() as *const BattleParty != party_b.unwrap() as *const BattleParty { + return false; + } + + self.pokemon.swap(a as usize, b as usize); + battle.event_hook().trigger(Event::Swap { + side_index: self.index, + index_a: a, + index_b: b, + }); + true + } +} + +impl<'a> VolatileScripts<'a> for BattleSide<'a> { + fn volatile_scripts(&self) -> &Arc> { + &self.volatile_scripts + } + + fn load_volatile_script(&self, key: &str) -> PkmnResult> { + self.battle + .upgrade() + .unwrap() + .read() + .unwrap() + .library() + .load_script(crate::ScriptCategory::Side, key) + } } impl<'a> ScriptSource for BattleSide<'a> { diff --git a/src/dynamic_data/models/mod.rs b/src/dynamic_data/models/mod.rs index 197af53..4de1ad5 100644 --- a/src/dynamic_data/models/mod.rs +++ b/src/dynamic_data/models/mod.rs @@ -5,3 +5,4 @@ pub mod battle_result; pub mod battle_side; pub mod learned_move; pub mod pokemon; +pub mod pokemon_party; diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index ba04a1a..30cc7ab 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -4,18 +4,21 @@ use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::learned_move::LearnedMove; use crate::dynamic_data::script_handling::script::Script; 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; use crate::static_data::items::item::Item; +use crate::static_data::natures::Nature; use crate::static_data::species_data::ability_index::AbilityIndex; use crate::static_data::species_data::form::Form; use crate::static_data::species_data::gender::Gender; use crate::static_data::species_data::species::Species; -use crate::static_data::statistic_set::StatisticSet; +use crate::static_data::statistic_set::{ClampedStatisticSet, StatisticSet}; use crate::static_data::statistics::Statistic; use crate::utils::random::Random; +use crate::{PkmnResult, ScriptCategory}; use derive_getters::Getters; use std::collections::HashSet; -use std::sync::{RwLock, Weak}; +use std::sync::{Arc, RwLock, Weak}; #[derive(Debug)] pub struct PokemonBattleData<'a> { @@ -45,6 +48,7 @@ impl<'a> PokemonBattleData<'a> { pub struct Pokemon<'a> { library: &'a DynamicLibrary<'a>, species: &'a Species<'a>, + #[getter(skip)] form: &'a Form<'a>, display_species: Option<&'a Species<'a>>, @@ -56,14 +60,18 @@ pub struct Pokemon<'a> { gender: Gender, coloring: u8, held_item: Option<&'a Item>, - health: u32, + current_health: u32, weight: f32, height: f32, - stat_boost: StatisticSet, - flat_stats: StatisticSet, - boosted_stats: StatisticSet, + stat_boost: ClampedStatisticSet, + flat_stats: StatisticSet, + boosted_stats: StatisticSet, + individual_values: ClampedStatisticSet, + effort_values: ClampedStatisticSet, + #[getter(skip)] + nature: &'a Nature, nickname: Option, @@ -80,7 +88,7 @@ pub struct Pokemon<'a> { ability_script: Option>, status_script: Option>, - volatile: ScriptSet, + volatile: Arc>, } impl<'a> Pokemon<'a> { @@ -93,6 +101,7 @@ impl<'a> Pokemon<'a> { unique_identifier: u32, gender: Gender, coloring: u8, + nature: String, ) -> Pokemon<'a> { // Calculate experience from the level for the specified growth rate. let experience = library @@ -102,7 +111,12 @@ impl<'a> Pokemon<'a> { let health = form.get_base_stat(Statistic::HP) as u32; let weight = *form.weight(); let height = *form.height(); - Pokemon { + let nature = library + .static_data() + .natures() + .get_nature(&nature) + .expect("Unknown nature name was given."); + let mut pokemon = Pokemon { library, species, form, @@ -114,12 +128,15 @@ impl<'a> Pokemon<'a> { gender, coloring, held_item: None, - health, + current_health: health, weight, height, stat_boost: Default::default(), - flat_stats: *form.base_stats(), - boosted_stats: *form.base_stats(), + flat_stats: Default::default(), + boosted_stats: Default::default(), + individual_values: Default::default(), + effort_values: Default::default(), + nature, nickname: None, ability_index: ability, is_ability_overridden: false, @@ -130,8 +147,26 @@ impl<'a> Pokemon<'a> { types: form.types().to_vec(), ability_script: None, status_script: None, - volatile: ScriptSet {}, - } + volatile: Default::default(), + }; + pokemon.recalculate_flat_stats(); + + pokemon + } + + pub fn form(&self) -> &'a Form<'a> { + self.form + } + pub fn nature(&self) -> &'a Nature { + self.nature + } + + pub fn recalculate_flat_stats(&mut self) { + self.flat_stats = self.library.stat_calculator().calculate_flat_stats(self); + self.recalculate_boosted_stats(); + } + pub fn recalculate_boosted_stats(&mut self) { + self.boosted_stats = self.library.stat_calculator().calculate_boosted_stats(self); } pub fn change_species(&mut self, species: &'a Species, form: &'a Form) { @@ -188,7 +223,7 @@ impl<'a> Pokemon<'a> { if let Some(data) = &mut self.battle_data { data.on_battle_field = value; if !value { - self.reset_active_scripts(); + self.volatile.write().unwrap().clear(); self.weight = *self.form.weight(); self.height = *self.form.height(); } @@ -201,15 +236,19 @@ impl<'a> Pokemon<'a> { } } + pub fn is_on_battlefield(&self) -> bool { + if let Some(data) = &self.battle_data { + data.on_battle_field + } else { + false + } + } + pub fn mark_opponent_as_seen(&mut self, unique_identifier: u32) { if let Some(battle_data) = &mut self.battle_data { battle_data.seen_opponents.insert(unique_identifier); } } - - pub fn reset_active_scripts(&mut self) { - todo!() - } } impl<'a> ScriptSource for Pokemon<'a> { @@ -218,6 +257,16 @@ impl<'a> ScriptSource for Pokemon<'a> { } } +impl<'a> VolatileScripts<'a> for Pokemon<'a> { + fn volatile_scripts(&self) -> &Arc> { + &self.volatile + } + + fn load_volatile_script(&self, key: &str) -> PkmnResult> { + self.library.load_script(ScriptCategory::Pokemon, key) + } +} + #[cfg(test)] pub mod test { use crate::dynamic_data::libraries::dynamic_library; @@ -244,6 +293,7 @@ pub mod test { 0, Gender::Male, 0, + "test_nature".to_string(), ); assert_eq!(pokemon.species.name(), "foo"); assert_eq!(pokemon.form.name(), "default"); diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs new file mode 100644 index 0000000..95211da --- /dev/null +++ b/src/dynamic_data/models/pokemon_party.rs @@ -0,0 +1,80 @@ +use crate::dynamic_data::models::pokemon::Pokemon; +use std::sync::{Arc, RwLock}; + +#[derive(Debug)] +pub struct PokemonParty<'a> { + pokemon: Vec>>>>, +} + +impl<'a> PokemonParty<'a> { + pub fn new(size: usize) -> Self { + let mut pokemon = Vec::with_capacity(size); + for _i in 0..size { + pokemon.push(None); + } + Self { pokemon } + } + + pub fn at(&self, index: usize) -> &Option>>> { + let opt = self.pokemon.get(index); + if let Some(v) = opt { + v + } else { + &None + } + } + + pub fn switch(&mut self, a: usize, b: usize) { + self.pokemon.swap(a, b); + } + + pub fn swap_into( + &mut self, + index: usize, + pokemon: Option>>>, + ) -> Option>>> { + if index >= self.pokemon.len() { + return pokemon; + } + let old = self.pokemon[index].as_ref().cloned(); + self.pokemon[index] = pokemon; + old + } + + pub fn has_usable_pokemon(&self) -> bool { + for pokemon in self.pokemon.iter().flatten() { + if pokemon.read().unwrap().is_usable() { + return true; + } + } + false + } + + pub fn length(&self) -> usize { + self.pokemon.len() + } + + pub fn pokemon(&self) -> &Vec>>>> { + &self.pokemon + } + + pub fn pack_party(&mut self) { + let mut first_empty = None; + let mut i = 0; + loop { + if self.pokemon[i].is_none() { + if first_empty.is_none() { + first_empty = Some(i) + } + } else if first_empty.is_some() { + self.pokemon.swap(first_empty.unwrap(), i); + i = first_empty.unwrap(); + first_empty = None; + } + i += 1; + if i >= self.pokemon.len() { + break; + } + } + } +} diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs index b875d6b..eefc1f8 100644 --- a/src/dynamic_data/script_handling/mod.rs +++ b/src/dynamic_data/script_handling/mod.rs @@ -1,8 +1,10 @@ use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; +use std::sync::Weak; pub mod script; pub mod script_set; +pub mod volatile_scripts; #[macro_export] macro_rules! script_hook { @@ -24,20 +26,20 @@ pub trait ScriptSource { fn get_script_count(&self); } -pub enum ScriptWrapper<'a> { - Script(&'a Box), - Set(&'a ScriptSet), +pub enum ScriptWrapper { + Script(Weak>), + Set(Weak), } -pub struct ScriptAggregator<'a> { - scripts: Vec>>, +pub struct ScriptAggregator { + scripts: Vec>, size: i32, index: i32, set_index: i32, } -impl<'a> ScriptAggregator<'a> { - pub fn new(scripts: Vec>>) -> Self { +impl ScriptAggregator { + pub fn new(scripts: Vec>) -> Self { let len = scripts.len(); Self { scripts, @@ -51,11 +53,13 @@ impl<'a> ScriptAggregator<'a> { if self.index != -1 { if let Some(wrapper) = &self.scripts[self.index as usize] { if let ScriptWrapper::Set(set) = wrapper { - self.set_index += 1; - if self.set_index as usize >= set.count() { - self.set_index = -1; - } else { - return true; + if let Some(set) = set.upgrade() { + self.set_index += 1; + if self.set_index as usize >= set.count() { + self.set_index = -1; + } else { + return true; + } } } } @@ -64,10 +68,16 @@ impl<'a> ScriptAggregator<'a> { for index in self.index..self.size { self.index = index; if let Some(wrapper) = &self.scripts[index as usize] { - if let ScriptWrapper::Set(..) = wrapper { - self.set_index = 0; + if let ScriptWrapper::Set(s) = wrapper { + if let Some(..) = s.upgrade() { + self.set_index = 0; + return true; + } + } else if let ScriptWrapper::Script(script) = wrapper { + if let Some(..) = script.upgrade() { + return true; + } } - return true; } } @@ -79,8 +89,13 @@ impl<'a> ScriptAggregator<'a> { return None; } return match self.scripts[self.index as usize].as_ref().unwrap() { - ScriptWrapper::Script(script) => Some(script), - ScriptWrapper::Set(set) => Some(set.at(self.set_index as usize)), + // We can make this unsafe as we know there is a strong reference. This is validated in + // increment_to_next_value + ScriptWrapper::Script(script) => unsafe { Some(&*script.as_ptr()) }, + ScriptWrapper::Set(set) => unsafe { + let r = (&*set.as_ptr()).at(self.set_index as usize); + Some(r.as_ref()) + }, }; } } diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index 4f1a1bc..26f3b05 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -2,6 +2,8 @@ use crate::dynamic_data::models::pokemon::Pokemon; use std::fmt::{Debug, Formatter}; pub trait Script { + fn name(&self) -> &str; + fn is_suppressed(&self) -> bool { self.get_suppressed_count() > 0 } diff --git a/src/dynamic_data/script_handling/script_set.rs b/src/dynamic_data/script_handling/script_set.rs index 6c8b5ee..e09a4bb 100644 --- a/src/dynamic_data/script_handling/script_set.rs +++ b/src/dynamic_data/script_handling/script_set.rs @@ -1,14 +1,69 @@ use crate::dynamic_data::script_handling::script::Script; +use crate::PkmnResult; +use indexmap::IndexMap; +use std::sync::Arc; -#[derive(Debug)] -pub struct ScriptSet {} +#[derive(Debug, Default)] +pub struct ScriptSet { + scripts: IndexMap>>, +} impl ScriptSet { - pub fn count(&self) -> usize { - todo!() + pub fn add(&mut self, script: Box) -> Arc> { + if let Some(existing) = self.scripts.get(script.name()) { + existing.stack(); + return existing.clone(); + } + let arc = Arc::new(script); + self.scripts.insert(arc.name().to_string(), arc.clone()); + self.scripts.last().unwrap().1.clone() } - pub fn at(&self, _index: usize) -> &Box { - todo!() + pub fn stack_or_add<'b, F>( + &mut self, + key: &str, + instantiation: &'b F, + ) -> PkmnResult>> + where + F: Fn() -> PkmnResult>, + { + if let Some(existing) = self.scripts.get(key) { + existing.stack(); + return Ok(existing.clone()); + } + let script = instantiation()?; + let arc = Arc::new(script); + self.scripts.insert(arc.name().to_string(), arc.clone()); + Ok(self.scripts.last().unwrap().1.clone()) + } + + pub fn get(&self, key: &str) -> Option<&Arc>> { + self.scripts.get(key) + } + + pub fn remove(&mut self, key: &str) { + let value = self.scripts.shift_remove(key); + if let Some(script) = value { + script.on_remove(); + } + } + + pub fn clear(&mut self) { + for script in &self.scripts { + script.1.on_remove(); + } + self.scripts.clear(); + } + + pub fn has(&self, key: &str) -> bool { + self.scripts.contains_key(key) + } + + pub fn at(&self, index: usize) -> &Arc> { + &self.scripts[index] + } + + pub fn count(&self) -> usize { + self.scripts.len() } } diff --git a/src/dynamic_data/script_handling/volatile_scripts.rs b/src/dynamic_data/script_handling/volatile_scripts.rs new file mode 100644 index 0000000..fe57fbd --- /dev/null +++ b/src/dynamic_data/script_handling/volatile_scripts.rs @@ -0,0 +1,30 @@ +use crate::dynamic_data::script_handling::script::Script; +use crate::dynamic_data::script_handling::script_set::ScriptSet; +use crate::PkmnResult; +use std::sync::{Arc, RwLock}; + +pub trait VolatileScripts<'a> { + fn volatile_scripts(&self) -> &Arc>; + fn load_volatile_script(&self, key: &str) -> PkmnResult>; + + fn has_volatile_script(&self, key: &str) -> bool { + self.volatile_scripts().read().unwrap().has(key) + } + + fn get_volatile_script(&self, key: &str) -> Option>> { + let scripts = self.volatile_scripts().read().unwrap(); + let s = scripts.get(key); + s.cloned() + } + + fn add_volatile_script(&mut self, key: &str) -> PkmnResult>> { + self.volatile_scripts() + .write() + .unwrap() + .stack_or_add(key, &|| self.load_volatile_script(key)) + } + + fn remove_volatile_script(&mut self, key: &str) { + self.volatile_scripts().write().unwrap().remove(key) + } +} diff --git a/src/lib.rs b/src/lib.rs index 9b8ec54..dae56da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,16 +4,26 @@ #![feature(bench_black_box)] #![feature(let_chains)] -#[macro_use] -extern crate maplit; - #[cfg(feature = "c_interface")] #[macro_use] extern crate lazy_static; +use crate::dynamic_data::libraries::script_resolver::ScriptCategory; + mod c_interface; pub mod defines; pub mod dynamic_data; pub mod static_data; pub mod utils; + +#[derive(Debug, Clone)] +pub enum PokemonError { + ScriptNotFound { + category: ScriptCategory, + name: String, + }, + MiscError, +} + +pub type PkmnResult = std::result::Result; diff --git a/src/static_data/growth_rates/lookup_growth_rate.rs b/src/static_data/growth_rates/lookup_growth_rate.rs index fee1d4e..bcfdb3f 100644 --- a/src/static_data/growth_rates/lookup_growth_rate.rs +++ b/src/static_data/growth_rates/lookup_growth_rate.rs @@ -22,6 +22,10 @@ impl GrowthRate for LookupGrowthRate { } fn calculate_experience(&self, level: LevelInt) -> u32 { - self.experience[(level - 1) as usize] + if level >= self.experience.len() as LevelInt { + *self.experience.last().unwrap() + } else { + self.experience[(level - 1) as usize] + } } } diff --git a/src/static_data/libraries/data_library.rs b/src/static_data/libraries/data_library.rs index 4d5e9ab..6fe1d3e 100644 --- a/src/static_data/libraries/data_library.rs +++ b/src/static_data/libraries/data_library.rs @@ -1,5 +1,5 @@ use crate::utils::random::Random; -use std::collections::HashMap; +use hashbrown::HashMap; pub trait DataLibrary<'a, T: 'a> { fn map(&self) -> &HashMap; diff --git a/src/static_data/libraries/growth_rate_library.rs b/src/static_data/libraries/growth_rate_library.rs index c24e99e..e025b1c 100644 --- a/src/static_data/libraries/growth_rate_library.rs +++ b/src/static_data/libraries/growth_rate_library.rs @@ -1,6 +1,6 @@ use crate::defines::LevelInt; use crate::static_data::growth_rates::growth_rate::GrowthRate; -use std::collections::HashMap; +use hashbrown::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; @@ -42,7 +42,10 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.add_growth_rate("foo", Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100]))); + w.add_growth_rate( + "test_growthrate", + Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100])), + ); // Drops borrow as mut lib @@ -51,14 +54,14 @@ pub mod tests { #[test] fn add_growth_rate_to_library_and_calculate_level() { let lib = build(); - assert_eq!(lib.calculate_level("foo", 3), 1); - assert_eq!(lib.calculate_level("foo", 50), 3); + assert_eq!(lib.calculate_level("test_growthrate", 3), 1); + assert_eq!(lib.calculate_level("test_growthrate", 50), 3); } #[test] fn add_growth_rate_to_library_and_calculate_experience() { let lib = build(); - assert_eq!(lib.calculate_experience("foo", 1), 0); - assert_eq!(lib.calculate_experience("foo", 3), 10); + assert_eq!(lib.calculate_experience("test_growthrate", 1), 0); + assert_eq!(lib.calculate_experience("test_growthrate", 3), 10); } } diff --git a/src/static_data/libraries/item_library.rs b/src/static_data/libraries/item_library.rs index 22c190f..c1fc5ef 100644 --- a/src/static_data/libraries/item_library.rs +++ b/src/static_data/libraries/item_library.rs @@ -1,6 +1,6 @@ use crate::static_data::items::item::Item; use crate::static_data::libraries::data_library::DataLibrary; -use std::collections::HashMap; +use hashbrown::HashMap; #[derive(Debug)] pub struct ItemLibrary { diff --git a/src/static_data/libraries/move_library.rs b/src/static_data/libraries/move_library.rs index aa48fff..e60d0b4 100644 --- a/src/static_data/libraries/move_library.rs +++ b/src/static_data/libraries/move_library.rs @@ -1,6 +1,6 @@ use crate::static_data::libraries::data_library::DataLibrary; use crate::static_data::moves::move_data::MoveData; -use std::collections::HashMap; +use hashbrown::HashMap; #[derive(Debug)] pub struct MoveLibrary { diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs index 1292ed7..ee0eb55 100644 --- a/src/static_data/libraries/species_library.rs +++ b/src/static_data/libraries/species_library.rs @@ -1,6 +1,6 @@ use crate::static_data::libraries::data_library::DataLibrary; use crate::static_data::species_data::species::Species; -use std::collections::HashMap; +use hashbrown::HashMap; #[derive(Debug)] pub struct SpeciesLibrary<'a> { @@ -39,14 +39,14 @@ pub mod tests { 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 std::collections::HashSet; + use hashbrown::HashSet; fn build_species<'a>() -> Species<'a> { Species::new( 0, "foo", 0.5, - "", + "test_growthrate", 0, Form::new( "default", diff --git a/src/static_data/libraries/static_data.rs b/src/static_data/libraries/static_data.rs index 0628acd..917cc42 100644 --- a/src/static_data/libraries/static_data.rs +++ b/src/static_data/libraries/static_data.rs @@ -4,6 +4,7 @@ use crate::static_data::libraries::library_settings::LibrarySettings; use crate::static_data::libraries::move_library::MoveLibrary; use crate::static_data::libraries::species_library::SpeciesLibrary; use crate::static_data::libraries::type_library::TypeLibrary; +use crate::static_data::natures::NatureLibrary; use derive_getters::Getters; #[derive(Getters, Debug)] @@ -14,6 +15,7 @@ pub struct StaticData<'a> { items: ItemLibrary, growth_rates: GrowthRateLibrary, types: TypeLibrary, + natures: NatureLibrary, } #[cfg(test)] @@ -23,6 +25,7 @@ pub mod test { use crate::static_data::libraries::{ growth_rate_library, item_library, move_library, species_library, type_library, }; + use crate::static_data::natures; pub fn build<'a>() -> StaticData<'a> { StaticData { @@ -32,6 +35,7 @@ pub mod test { items: item_library::tests::build(), growth_rates: growth_rate_library::tests::build(), types: type_library::tests::build(), + natures: natures::tests::build(), } } } diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs index 396adfd..05d37d7 100644 --- a/src/static_data/libraries/type_library.rs +++ b/src/static_data/libraries/type_library.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use hashbrown::HashMap; #[derive(Debug)] pub struct TypeLibrary { diff --git a/src/static_data/mod.rs b/src/static_data/mod.rs index 21f6598..a83c238 100644 --- a/src/static_data/mod.rs +++ b/src/static_data/mod.rs @@ -2,6 +2,7 @@ pub mod growth_rates; pub mod items; pub mod libraries; pub mod moves; +pub mod natures; pub mod species_data; pub mod statistic_set; pub mod statistics; diff --git a/src/static_data/natures.rs b/src/static_data/natures.rs new file mode 100644 index 0000000..af627e3 --- /dev/null +++ b/src/static_data/natures.rs @@ -0,0 +1,131 @@ +use crate::static_data::statistics::Statistic; +use hashbrown::HashMap; + +#[derive(Debug)] +pub struct Nature { + increase_stat: Statistic, + decrease_stat: Statistic, + increase_modifier: f32, + decrease_modifier: f32, +} + +impl Nature { + pub fn new( + increase_stat: Statistic, + decrease_stat: Statistic, + increase_modifier: f32, + decrease_modifier: f32, + ) -> Self { + Self { + increase_stat, + decrease_stat, + increase_modifier, + decrease_modifier, + } + } + + pub fn increased_stat(&self) -> Statistic { + self.increase_stat + } + + pub fn decreased_stat(&self) -> Statistic { + self.decrease_stat + } + + pub fn get_stat_modifier(&self, stat: Statistic) -> f32 { + if stat == self.increase_stat { + self.increase_modifier + } else if stat == self.decrease_stat { + self.decrease_modifier + } else { + 1.0 + } + } +} + +#[derive(Debug)] +pub struct NatureLibrary { + map: HashMap, +} + +impl NatureLibrary { + pub fn new(capacity: usize) -> Self { + NatureLibrary { + map: HashMap::with_capacity(capacity), + } + } + + pub fn load_nature(&mut self, name: &str, nature: Nature) { + self.map.insert(name.to_string(), nature); + } + + pub fn get_nature(&self, key: &str) -> Option<&Nature> { + self.map.get(key) + } + + pub fn get_nature_name(&self, nature: &Nature) -> String { + for kv in &self.map { + // As natures can't be copied, and should always be the same reference as the value + // in the map, we just compare by reference. + if (kv.1 as *const Nature) == (nature as *const Nature) { + return kv.0.to_string(); + } + } + panic!("No name was found for the given nature. This should never happen."); + } +} + +#[cfg(test)] +pub mod tests { + use crate::static_data::natures::{Nature, NatureLibrary}; + use crate::static_data::statistics::Statistic; + + pub fn build() -> NatureLibrary { + let mut lib = NatureLibrary::new(2); + + lib.load_nature( + "test_nature", + Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), + ); + + lib + } + + #[test] + fn create_nature_library_insert_and_retrieve() { + let mut lib = NatureLibrary::new(2); + lib.load_nature( + "foo", + Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), + ); + lib.load_nature( + "bar", + Nature::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9), + ); + let n1 = lib.get_nature("foo").expect("Nature was not found"); + assert_eq!(n1.increase_stat, Statistic::HP); + assert_eq!(n1.decrease_stat, Statistic::Attack); + assert_eq!(n1.increase_modifier, 1.1); + assert_eq!(n1.decrease_modifier, 0.9); + } + + #[test] + fn create_nature_library_insert_and_get_name() { + let mut lib = NatureLibrary::new(2); + lib.load_nature( + "foo", + Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), + ); + lib.load_nature( + "bar", + Nature::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9), + ); + + let n1 = lib.get_nature("foo").expect("Nature was not found"); + let name = lib.get_nature_name(n1); + assert_eq!(name, "foo"); + let n2 = lib.get_nature("bar").expect("Nature was not found"); + let name2 = lib.get_nature_name(n2); + assert_eq!(name2, "bar"); + } +} diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs index bc03b80..18d987b 100644 --- a/src/static_data/species_data/form.rs +++ b/src/static_data/species_data/form.rs @@ -4,7 +4,7 @@ use crate::static_data::statistic_set::StatisticSet; use crate::static_data::statistics::Statistic; use crate::utils::random::Random; use derive_getters::Getters; -use std::collections::HashSet; +use hashbrown::HashSet; #[derive(Getters, Debug)] pub struct Form<'a> { diff --git a/src/static_data/species_data/learnable_moves.rs b/src/static_data/species_data/learnable_moves.rs index dfc7ca1..72dc34b 100644 --- a/src/static_data/species_data/learnable_moves.rs +++ b/src/static_data/species_data/learnable_moves.rs @@ -1,7 +1,7 @@ use crate::defines::LevelInt; use crate::static_data::moves::move_data::MoveData; -use std::collections::hash_map::Entry::{Occupied, Vacant}; -use std::collections::HashMap; +use hashbrown::hash_map::Entry::{Occupied, Vacant}; +use hashbrown::HashMap; #[derive(Default, PartialEq, Debug)] pub struct LearnableMoves<'a> { @@ -45,7 +45,6 @@ mod tests { #[test] fn adds_level_moves() { - let mut moves = LearnableMoves::new(); let move1 = MoveData::new( "foo", 0, @@ -70,6 +69,8 @@ mod tests { SecondaryEffect::empty(), Default::default(), ); + + let mut moves = LearnableMoves::new(); moves.add_level_move(1, &move1); moves.add_level_move(1, &move2); @@ -81,7 +82,6 @@ mod tests { #[test] fn adds_two_same_moves_at_different_level() { - let mut moves = LearnableMoves::new(); let move1 = MoveData::new( "foo", 0, @@ -94,6 +94,8 @@ mod tests { SecondaryEffect::empty(), Default::default(), ); + let mut moves = LearnableMoves::new(); + moves.add_level_move(1, &move1); moves.add_level_move(5, &move1); diff --git a/src/static_data/species_data/species.rs b/src/static_data/species_data/species.rs index 7ad606f..9f56211 100644 --- a/src/static_data/species_data/species.rs +++ b/src/static_data/species_data/species.rs @@ -2,7 +2,7 @@ use self::super::form::Form; use crate::static_data::species_data::gender::Gender; use crate::utils::random::Random; use derive_getters::Getters; -use std::collections::{HashMap, HashSet}; +use hashbrown::{HashMap, HashSet}; #[derive(Debug, Getters)] pub struct Species<'a> { @@ -25,15 +25,15 @@ impl<'a> Species<'a> { default_form: Form<'a>, flags: HashSet, ) -> Species<'a> { + let mut forms = HashMap::with_capacity(1); + forms.insert("default".to_string(), default_form); Species { id, name: name.to_string(), gender_rate, growth_rate: growth_rate.to_string(), capture_rate, - forms: hashmap! { - "default".to_string() => default_form, - }, + forms, flags, } } diff --git a/src/static_data/statistic_set.rs b/src/static_data/statistic_set.rs index a9969df..13123cc 100644 --- a/src/static_data/statistic_set.rs +++ b/src/static_data/statistic_set.rs @@ -19,6 +19,24 @@ impl StatisticSet where T: PrimInt, { + pub fn new( + hp: T, + attack: T, + defense: T, + special_attack: T, + special_defense: T, + speed: T, + ) -> Self { + Self { + hp, + attack, + defense, + special_attack, + special_defense, + speed, + } + } + pub const fn get_stat(&self, stat: Statistic) -> T { match stat { Statistic::HP => self.hp, @@ -63,3 +81,91 @@ where } } } + +#[derive(Default, Eq, PartialEq, Copy, Clone, Debug, Getters)] +pub struct ClampedStatisticSet +where + T: PrimInt, +{ + hp: T, + attack: T, + defense: T, + special_attack: T, + special_defense: T, + speed: T, +} + +impl ClampedStatisticSet +where + T: PrimInt, +{ + pub const fn get_stat(&self, stat: Statistic) -> T { + 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, + } + } + + 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, + } + } + + 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(); + } + if *original_value == new_value { + return false; + } + *original_value = new_value; + true + } + + pub fn increase_stat(&mut self, stat: Statistic, value: T) -> 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), + } + } + + pub fn decrease_stat(&mut self, stat: Statistic, value: T) -> 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), + } + } +} diff --git a/src/static_data/statistics.rs b/src/static_data/statistics.rs index 86153ab..92d8f34 100644 --- a/src/static_data/statistics.rs +++ b/src/static_data/statistics.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum Statistic { HP, Attack,