From 8f6ecdd4add16f4fabf11ade48446d5e7bd1f3ec Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Fri, 1 Jul 2022 17:07:22 +0200 Subject: [PATCH] More documentation. --- src/dynamic_data/choices.rs | 4 +- src/dynamic_data/flow/target_resolver.rs | 10 + src/dynamic_data/flow/turn_runner.rs | 20 +- src/dynamic_data/libraries/misc_library.rs | 2 +- src/dynamic_data/models/battle.rs | 8 +- src/dynamic_data/models/battle_side.rs | 4 +- src/dynamic_data/models/executing_move.rs | 8 +- src/dynamic_data/models/learned_move.rs | 47 +++- src/dynamic_data/models/pokemon.rs | 209 +++++++++++++----- src/dynamic_data/models/pokemon_builder.rs | 21 +- src/dynamic_data/models/pokemon_party.rs | 14 +- .../script_handling/item_script.rs | 1 + src/dynamic_data/script_handling/mod.rs | 74 +++++-- src/dynamic_data/script_handling/script.rs | 11 +- .../script_handling/script_set.rs | 26 ++- ...e_scripts.rs => volatile_scripts_owner.rs} | 12 +- src/static_data/growth_rates.rs | 6 + src/static_data/items.rs | 28 +++ src/static_data/libraries/ability_library.rs | 33 +-- src/static_data/libraries/data_library.rs | 37 ++-- .../libraries/growth_rate_library.rs | 7 + src/static_data/libraries/item_library.rs | 21 +- src/static_data/libraries/library_settings.rs | 4 + src/static_data/libraries/mod.rs | 2 +- src/static_data/libraries/move_library.rs | 24 +- src/static_data/libraries/species_library.rs | 22 +- src/static_data/libraries/static_data.rs | 26 ++- src/static_data/libraries/type_library.rs | 95 +++++--- src/static_data/moves/move_data.rs | 61 ++++- src/static_data/moves/secondary_effect.rs | 17 +- src/static_data/natures.rs | 22 +- src/static_data/species_data/form.rs | 43 +++- src/utils/random.rs | 45 +++- src/utils/string_key.rs | 17 +- tests/common/library_loader.rs | 2 +- 35 files changed, 721 insertions(+), 262 deletions(-) rename src/dynamic_data/script_handling/{volatile_scripts.rs => volatile_scripts_owner.rs} (68%) diff --git a/src/dynamic_data/choices.rs b/src/dynamic_data/choices.rs index 8d0c5f0..5304896 100644 --- a/src/dynamic_data/choices.rs +++ b/src/dynamic_data/choices.rs @@ -3,10 +3,10 @@ use std::sync::Arc; use parking_lot::RwLock; -use crate::dynamic_data::LearnedMove; use crate::dynamic_data::Pokemon; use crate::dynamic_data::ScriptContainer; -use crate::dynamic_data::{ScriptSource, ScriptSourceData, ScriptWrapper}; +use crate::dynamic_data::{LearnedMove, ScriptWrapper}; +use crate::dynamic_data::{ScriptSource, ScriptSourceData}; /// The data on a turn choice that should be contained in every turn choice, regardless of type. #[derive(Debug)] diff --git a/src/dynamic_data/flow/target_resolver.rs b/src/dynamic_data/flow/target_resolver.rs index 4fd3ca5..5c0f53b 100644 --- a/src/dynamic_data/flow/target_resolver.rs +++ b/src/dynamic_data/flow/target_resolver.rs @@ -50,13 +50,22 @@ fn get_all_adjacent_opponent<'b, 'library>( battle.get_pokemon(side, index).as_ref().cloned(), battle.get_pokemon(side, left as u8).as_ref().cloned(), battle.get_pokemon(side, right).as_ref().cloned(), + battle + .get_pokemon(get_opposite_side(side), left as u8) + .as_ref() + .cloned(), battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(), + battle.get_pokemon(get_opposite_side(side), right).as_ref().cloned(), ] } else { vec![ battle.get_pokemon(side, index).as_ref().cloned(), battle.get_pokemon(side, left as u8).as_ref().cloned(), battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(), + battle + .get_pokemon(get_opposite_side(side), left as u8) + .as_ref() + .cloned(), ] }; } @@ -65,6 +74,7 @@ fn get_all_adjacent_opponent<'b, 'library>( battle.get_pokemon(side, index).as_ref().cloned(), battle.get_pokemon(side, right).as_ref().cloned(), battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(), + battle.get_pokemon(get_opposite_side(side), right).as_ref().cloned(), ] } diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index c11285c..03bb2e8 100644 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -246,14 +246,18 @@ impl<'own, 'library> Battle<'own, 'library> { hit_data.set_damage(damage); let mut accuracy = executing_move.use_move().accuracy(); - script_hook!( - change_accuracy, - executing_move, - executing_move, - target, - hit_index, - &mut accuracy - ); + // If the accuracy is 255, the move should always hit, and as such we should not allow + // modifying it. + if accuracy != 255 { + script_hook!( + change_accuracy, + executing_move, + executing_move, + target, + hit_index, + &mut accuracy + ); + } if accuracy < 100 && self.random().get_max(100) as u8 >= accuracy { script_hook!(on_move_miss, target, executing_move, target); self.event_hook().trigger(Event::Miss { diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs index 1dcfa7f..811507d 100644 --- a/src/dynamic_data/libraries/misc_library.rs +++ b/src/dynamic_data/libraries/misc_library.rs @@ -40,7 +40,7 @@ impl<'library> Gen7MiscLibrary<'library> { pub fn new() -> Self { let struggle_data = Box::new(MoveData::new( &StringKey::new("struggle"), - 0, + 0.into(), MoveCategory::Physical, 50, 255, diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 5afd0ed..59a283e 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -7,7 +7,6 @@ use parking_lot::RwLock; use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::event_hooks::{Event, EventHook}; -use crate::dynamic_data::is_valid_target; use crate::dynamic_data::models::battle_party::BattleParty; use crate::dynamic_data::models::battle_random::BattleRandom; use crate::dynamic_data::models::battle_result::BattleResult; @@ -17,8 +16,9 @@ use crate::dynamic_data::ChoiceQueue; use crate::dynamic_data::DynamicLibrary; use crate::dynamic_data::Script; use crate::dynamic_data::ScriptSet; -use crate::dynamic_data::VolatileScripts; -use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData, ScriptWrapper}; +use crate::dynamic_data::VolatileScriptsOwner; +use crate::dynamic_data::{is_valid_target, ScriptWrapper}; +use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData}; use crate::{script_hook, PkmnResult, StringKey}; /// A pokemon battle, with any amount of sides and pokemon per side. @@ -322,7 +322,7 @@ impl<'own, 'library> Battle<'own, 'library> { } } -impl<'own, 'library> VolatileScripts<'own> for Battle<'own, 'library> { +impl<'own, 'library> VolatileScriptsOwner<'own> for Battle<'own, 'library> { fn volatile_scripts(&self) -> &Arc { &self.volatile_scripts } diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index a2413e9..7010d43 100644 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -13,7 +13,7 @@ use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::dynamic_data::Script; use crate::dynamic_data::ScriptSet; -use crate::dynamic_data::VolatileScripts; +use crate::dynamic_data::VolatileScriptsOwner; use crate::{script_hook, PkmnResult, StringKey}; /// A side on a battle. @@ -296,7 +296,7 @@ impl<'own, 'library> BattleSide<'own, 'library> { } } -impl<'own, 'library> VolatileScripts<'own> for BattleSide<'own, 'library> { +impl<'own, 'library> VolatileScriptsOwner<'own> for BattleSide<'own, 'library> { fn volatile_scripts(&self) -> &Arc { &self.volatile_scripts } diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs index 84115cd..3fceca7 100644 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -10,7 +10,7 @@ use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::dynamic_data::ScriptContainer; use crate::dynamic_data::TargetList; -use crate::static_data::MoveData; +use crate::static_data::{MoveData, TypeIdentifier}; use crate::{PkmnResult, PokemonError}; /// A hit data is the data for a single hit, on a single target. @@ -25,7 +25,7 @@ pub struct HitData { /// The actual damage of the hit. damage: AtomicU32, /// The type id of the type used for the hit. - move_type: AtomicU8, + move_type: Atomic, /// Whether or not the hit has failed. has_failed: AtomicBool, } @@ -48,7 +48,7 @@ impl HitData { self.damage.load(Ordering::Relaxed) } /// The type id of the type used for the hit. - pub fn move_type(&self) -> u8 { + pub fn move_type(&self) -> TypeIdentifier { self.move_type.load(Ordering::Relaxed) } /// Whether or not the hit has failed. @@ -73,7 +73,7 @@ impl HitData { self.damage.store(value, Ordering::SeqCst); } /// Sets the move type id of the hit. - pub fn set_move_type(&self, value: u8) { + pub fn set_move_type(&self, value: TypeIdentifier) { self.move_type.store(value, Ordering::SeqCst); } /// Marks the hit as failed. diff --git a/src/dynamic_data/models/learned_move.rs b/src/dynamic_data/models/learned_move.rs index 790a5cd..a86ca12 100644 --- a/src/dynamic_data/models/learned_move.rs +++ b/src/dynamic_data/models/learned_move.rs @@ -2,21 +2,32 @@ use std::sync::atomic::{AtomicU8, Ordering}; use crate::static_data::MoveData; +/// A learned move is the data attached to a Pokemon for a move it has learned. It has information +/// such as the remaining amount of users, how it has been learned, etc. #[derive(Debug)] pub struct LearnedMove<'library> { + /// The immutable move information of the move. move_data: &'library MoveData, + /// The maximal power points for this move. max_pp: u8, + /// The amount of remaining power points. If this is 0, we can not use the move anymore. remaining_pp: AtomicU8, + /// The way the move was learned. learn_method: MoveLearnMethod, } -#[derive(Copy, Clone, Debug)] +/// The different ways a move can be learned. +#[derive(Copy, Clone, Debug, Default)] pub enum MoveLearnMethod { + /// We do not know the learn method. + #[default] Unknown = 0, + /// The move was learned through level up. Level = 1, } impl<'a> LearnedMove<'a> { + /// Instantiate a new learned move. pub fn new(move_data: &'a MoveData, learn_method: MoveLearnMethod) -> Self { Self { move_data, @@ -26,36 +37,50 @@ impl<'a> LearnedMove<'a> { } } + /// The immutable move information of the move. pub fn move_data(&self) -> &MoveData { self.move_data } - + /// The maximal power points for this move. pub fn max_pp(&self) -> u8 { self.max_pp } + /// The amount of remaining power points. If this is 0, we can not use the move anymore. pub fn remaining_pp(&self) -> u8 { self.remaining_pp.load(Ordering::Relaxed) } + /// The way the move was learned. pub fn learn_method(&self) -> MoveLearnMethod { self.learn_method } + /// Try and reduce the PP by a certain amount. If the amount is higher than the current uses, + /// return false. Otherwise, reduce the PP, and return true. pub fn try_use(&self, amount: u8) -> bool { - if amount > self.remaining_pp() { - return false; - } - self.remaining_pp.fetch_sub(amount, Ordering::SeqCst); - true + let res = self.remaining_pp.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + if amount > x { + None + } else { + Some(x - amount) + } + }); + res.is_ok() } + /// Set the remaining PP to the max amount of PP. pub fn restore_all_uses(&self) { self.remaining_pp.store(self.max_pp, Ordering::SeqCst); } + /// Restore the remaining PP by a certain amount. Will prevent it from going above max PP. pub fn restore_uses(&self, mut uses: u8) { - if self.remaining_pp() + uses > self.max_pp { - uses = self.remaining_pp() - uses; - } - self.remaining_pp.fetch_add(uses, Ordering::SeqCst); + self.remaining_pp + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + if x + uses > self.max_pp { + uses = self.max_pp - x; + } + Some(x) + }) + .unwrap(); } } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index 598197d..00e598a 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -11,8 +11,7 @@ use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::damage_source::DamageSource; use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod}; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; -use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScripts}; -use crate::static_data::AbilityIndex; +use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScriptsOwner}; use crate::static_data::DataLibrary; use crate::static_data::Form; use crate::static_data::Gender; @@ -20,95 +19,107 @@ use crate::static_data::Item; use crate::static_data::Nature; use crate::static_data::Species; use crate::static_data::{Ability, Statistic}; +use crate::static_data::{AbilityIndex, TypeIdentifier}; use crate::static_data::{ClampedStatisticSet, StatisticSet}; use crate::utils::Random; use crate::{script_hook, PkmnResult, StringKey}; -#[derive(Debug)] -pub struct PokemonBattleData<'pokemon, 'library> { - battle: *mut Battle<'pokemon, 'library>, - battle_side_index: AtomicU8, - index: AtomicU8, - on_battle_field: AtomicBool, - seen_opponents: RwLock>>>, -} - -impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> { - pub fn battle_mut(&mut self) -> Option<&mut Battle<'pokemon, 'library>> { - unsafe { self.battle.as_mut() } - } - pub fn battle(&self) -> Option<&Battle<'pokemon, 'library>> { - unsafe { self.battle.as_ref() } - } - - pub fn battle_side_index(&self) -> u8 { - self.battle_side_index.load(Ordering::Relaxed) - } - pub fn index(&self) -> u8 { - self.index.load(Ordering::Relaxed) - } - pub fn on_battle_field(&self) -> bool { - self.on_battle_field.load(Ordering::Relaxed) - } - pub fn seen_opponents(&self) -> &RwLock>>> { - &self.seen_opponents - } -} - +/// An individual Pokemon as we know and love them. #[derive(Debug)] pub struct Pokemon<'own, 'library> where 'own: 'library, { + /// The library data of the Pokemon. library: &'own DynamicLibrary, + /// The species of the Pokemon. species: &'own Species, + /// The form of the Pokemon. form: &'own Form, + /// An optional display species of the Pokemon. If this is set, the client should display this + /// species. An example of usage for this is the Illusion ability. display_species: Option<&'own Species>, + /// An optional display form of the Pokemon. If this is set, the client should display this + // species. An example of usage for this is the Illusion ability. display_form: Option<&'own Form>, + /// The current level of the Pokemon. level: LevelInt, + /// The amount of experience of the Pokemon. experience: AtomicU32, + /// A unique random number for this Pokemon. unique_identifier: u32, + + /// The gender of the Pokemon. gender: Gender, + /// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are + /// currently not used, and can be used for other implementations. coloring: u8, + /// The held item of the Pokemon. held_item: RwLock>, + /// The remaining health points of the Pokemon. current_health: AtomicU32, + /// The weight of the Pokemon in kilograms. weight: Atomic, + /// The height of the Pokemon in meters. height: Atomic, - stat_boost: ClampedStatisticSet, + /// The stats of the Pokemon when disregarding any stat boosts. flat_stats: StatisticSet, + /// The statistics boosts of the Pokemon. Will prevent the value from going above 6, and below + /// -6. + stat_boost: ClampedStatisticSet, + /// The stats of the Pokemon including the stat boosts boosted_stats: StatisticSet, + /// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. individual_values: ClampedStatisticSet, + /// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. effort_values: ClampedStatisticSet, + /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. nature: &'own Nature, + /// An optional nickname of the Pokemon. nickname: Option, + /// An index of the ability to find the actual ability on the form. ability_index: AbilityIndex, - is_ability_overridden: bool, + /// An ability can be overriden to an arbitrary ability. This is for example used for the Mummy + /// ability. override_ability: Option, + /// If in battle, we have additional data. battle_data: RwLock>>, + /// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots + /// are defined by None. moves: RwLock<[Option>>; MAX_MOVES]>, + /// Whether or not the Pokemon is allowed to gain experience. allowed_experience: bool, - types: Vec, + /// The current types of the Pokemon. + types: Vec, + /// Whether or not this Pokemon is an egg. is_egg: bool, + /// Whether or not this Pokemon was caught this battle. is_caught: bool, + /// The script for the held item. held_item_trigger_script: ScriptContainer, + /// The script for the ability. ability_script: ScriptContainer, + /// The script for the status. status_script: ScriptContainer, + /// The volatile status scripts of the Pokemon. volatile: Arc, + /// Data required for the Pokemon to be a script source. script_source_data: RwLock, } impl<'own, 'library> Pokemon<'own, 'library> { + /// Instantiates a new Pokemon. pub fn new( library: &'own DynamicLibrary, species: &'own Species, @@ -155,7 +166,6 @@ impl<'own, 'library> Pokemon<'own, 'library> { nature, nickname: None, ability_index: ability, - is_ability_overridden: false, override_ability: None, battle_data: RwLock::new(None), moves: RwLock::new([None, None, None, None]), @@ -176,15 +186,19 @@ impl<'own, 'library> Pokemon<'own, 'library> { pokemon } + /// The library data of the Pokemon. pub fn library(&self) -> &'own DynamicLibrary { self.library } + /// The species of the Pokemon. pub fn species(&self) -> &'own Species { self.species } + /// The form of the Pokemon. pub fn form(&self) -> &'own Form { self.form } + /// The species that should be displayed to the user. This handles stuff like the Illusion ability. pub fn display_species(&self) -> &'own Species { if let Some(v) = self.display_species { v @@ -192,6 +206,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { self.species } } + /// The form that should be displayed to the user. This handles stuff like the Illusion ability. pub fn display_form(&self) -> &'own Form { if let Some(v) = self.display_form { v @@ -199,25 +214,32 @@ impl<'own, 'library> Pokemon<'own, 'library> { self.form } } - + /// The current level of the Pokemon. pub fn level(&self) -> LevelInt { self.level } + /// The amount of experience of the Pokemon. pub fn experience(&self) -> u32 { self.experience.load(Ordering::Relaxed) } + /// A unique random number for this Pokemon. pub fn unique_identifier(&self) -> u32 { self.unique_identifier } + /// The gender of the Pokemon. pub fn gender(&self) -> Gender { self.gender } + /// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are + /// currently not used, and can be used for other implementations. pub fn coloring(&self) -> u8 { self.coloring } + /// Checks whether the Pokemon is holding an item, pub fn held_item(&self) -> &RwLock> { &self.held_item } + /// Checks whether the Pokemon is holding a specific item. pub fn has_held_item(&self, name: &StringKey) -> bool { // Only true if we have an item, and the item name is the same as the requested item. if let Some(v) = self.held_item.read().deref() { @@ -225,12 +247,15 @@ impl<'own, 'library> Pokemon<'own, 'library> { } false } + /// Changes the held item of the Pokemon/ pub fn set_held_item(&self, item: &'own Item) -> Option<&'own Item> { self.held_item.write().replace(item) } + /// Removes the held item from the Pokemon. pub fn remove_held_item(&self) -> Option<&'own Item> { self.held_item.write().take() } + /// Makes the Pokemon uses its held item. pub fn consume_held_item(&self) -> bool { if self.held_item.read().is_none() { return false; @@ -244,42 +269,53 @@ impl<'own, 'library> Pokemon<'own, 'library> { todo!(); } + /// The remaining health points of the Pokemon. pub fn current_health(&self) -> u32 { self.current_health.load(Ordering::Relaxed) } + /// The max health points of the Pokemon. pub fn max_health(&self) -> u32 { self.boosted_stats.hp() } + /// The weight of the Pokemon in kilograms. pub fn weight(&self) -> f32 { self.weight.load(Ordering::Relaxed) } + /// The height of the Pokemon in meters. pub fn height(&self) -> f32 { self.height.load(Ordering::Relaxed) } + /// An optional nickname of the Pokemon. pub fn nickname(&self) -> &Option { &self.nickname } + /// An index of the ability to find the actual ability on the form. pub fn real_ability(&self) -> &AbilityIndex { &self.ability_index } - pub fn types(&self) -> &Vec { + /// The current types of the Pokemon. + pub fn types(&self) -> &Vec { &self.types } + /// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots + /// are defined by None. pub fn learned_moves(&self) -> &RwLock<[Option>>; MAX_MOVES]> { &self.moves } - pub fn status(&self) -> &ScriptContainer { - &self.status_script - } + + /// The stats of the Pokemon when disregarding any stat boosts. pub fn flat_stats(&self) -> &StatisticSet { &self.flat_stats } + /// The stats of the Pokemon including the stat boosts pub fn boosted_stats(&self) -> &StatisticSet { &self.boosted_stats } + /// Get the stat boosts for a specific stat. pub fn stat_boost(&self, stat: Statistic) -> i8 { self.stat_boost.get_stat(stat) } + /// Change a boosted stat by a certain amount. pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> bool { let mut prevent = false; script_hook!( @@ -328,13 +364,16 @@ impl<'own, 'library> Pokemon<'own, 'library> { return changed; } + /// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. pub fn individual_values(&self) -> &ClampedStatisticSet { &self.individual_values } + /// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. pub fn effort_values(&self) -> &ClampedStatisticSet { &self.effort_values } + /// Gets the battle the battle is currently in. pub fn get_battle(&self) -> Option<&Battle<'own, 'library>> { let r = self.battle_data.read(); if let Some(data) = &r.deref() { @@ -343,20 +382,24 @@ impl<'own, 'library> Pokemon<'own, 'library> { None } } + /// Get the index of the side of the battle the Pokemon is in. Only returns a value if the Pokemon + /// is on the battlefield. pub fn get_battle_side_index(&self) -> Option { self.battle_data.read().as_ref().map(|data| data.battle_side_index()) } + /// Get the index of the slot on the side of the battle the Pokemon is in. Only returns a value + /// if the Pokemon is on the battlefield. pub fn get_battle_index(&self) -> Option { self.battle_data.read().as_ref().map(|data| data.index()) } + /// Returns whether something overrides the ability. pub fn is_ability_overriden(&self) -> bool { - self.is_ability_overridden + self.override_ability.is_some() } + /// Returns the currently active ability. pub fn active_ability(&self) -> &Ability { - if self.is_ability_overridden { - if let Some(v) = &self.override_ability { - return v; - } + if let Some(v) = &self.override_ability { + return v; } self.library .static_data() @@ -365,33 +408,41 @@ impl<'own, 'library> Pokemon<'own, 'library> { .unwrap() } + /// The script for the status. + pub fn status(&self) -> &ScriptContainer { + &self.status_script + } + + /// Returns the script for the currently active ability. pub fn ability_script(&self) -> &ScriptContainer { &self.ability_script } - // pub fn seen_opponents(&self) -> &RwLock>> { - // &self.battle_data.read - // } + /// Whether or not the Pokemon is allowed to gain experience. pub fn allowed_experience_gain(&self) -> bool { self.allowed_experience } + /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. pub fn nature(&self) -> &'own Nature { self.nature } + /// Calculates the flat stats on the Pokemon. pub fn recalculate_flat_stats(&self) { self.library .stat_calculator() .calculate_flat_stats(self, &self.flat_stats); self.recalculate_boosted_stats(); } + /// Calculates the boosted stats on the Pokemon. pub fn recalculate_boosted_stats(&self) { self.library .stat_calculator() .calculate_boosted_stats(self, &self.boosted_stats); } + /// Change the species of the Pokemon. pub fn change_species(&mut self, species: &'own Species, form: &'own Form) { self.species = species; self.form = form; @@ -424,6 +475,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Change the form of the Pokemon. pub fn change_form(&mut self, form: &'own Form) { if std::ptr::eq(self.form, form) { return; @@ -474,14 +526,17 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Whether or not the Pokemon is useable in a battle. pub fn is_usable(&self) -> bool { !self.is_caught && !self.is_egg && !self.is_fainted() } + /// Returns whether the Pokemon is fainted. pub fn is_fainted(&self) -> bool { self.current_health() == 0 } + /// Sets the current battle the Pokemon is in. 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() { @@ -498,6 +553,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Sets whether or not the Pokemon is on the battlefield. pub fn set_on_battlefield(&self, value: bool) { let r = self.battle_data.read(); if let Some(data) = &mut r.deref() { @@ -510,6 +566,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Sets the index of the slot of the side the Pokemon is on. pub fn set_battle_index(&self, index: u8) { let r = self.battle_data.read(); if let Some(data) = r.deref() { @@ -517,10 +574,12 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Whether or not the Pokemon is on the battlefield. pub fn is_on_battlefield(&self) -> bool { self.battle_data.read().is_some_and(|a| a.on_battle_field()) } + /// Marks an opponent as seen, for use in experience gain. pub fn mark_opponent_as_seen(&self, pokemon: Weak>) { let r = self.battle_data.read(); if let Some(battle_data) = &r.deref() { @@ -534,6 +593,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Damages the Pokemon by a certain amount of damage, from a specific damage source. pub fn damage(&self, mut damage: u32, source: DamageSource) { if damage > self.current_health() { damage = self.current_health(); @@ -563,7 +623,8 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } - pub fn on_faint(&self, source: DamageSource) { + /// Triggers when the Pokemon faints. + 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() { @@ -581,6 +642,7 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } + /// Learn a move. 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()); @@ -592,6 +654,49 @@ impl<'own, 'library> Pokemon<'own, 'library> { } } +/// The data of the Pokemon related to being in a battle. +#[derive(Debug)] +pub struct PokemonBattleData<'pokemon, 'library> { + /// The battle data of the Pokemon + battle: *mut Battle<'pokemon, 'library>, + /// The index of the side of the Pokemon + battle_side_index: AtomicU8, + /// The index of the slot on the side of the Pokemon. + index: AtomicU8, + /// Whether or not the Pokemon is on the battlefield. + on_battle_field: AtomicBool, + /// A list of opponents the Pokemon has seen this battle. + seen_opponents: RwLock>>>, +} + +impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> { + /// The battle data of the Pokemon + pub fn battle_mut(&mut self) -> Option<&mut Battle<'pokemon, 'library>> { + unsafe { self.battle.as_mut() } + } + /// The battle data of the Pokemon + pub fn battle(&self) -> Option<&Battle<'pokemon, 'library>> { + unsafe { self.battle.as_ref() } + } + + /// The index of the side of the Pokemon + pub fn battle_side_index(&self) -> u8 { + self.battle_side_index.load(Ordering::Relaxed) + } + /// The index of the slot on the side of the Pokemon. + pub fn index(&self) -> u8 { + self.index.load(Ordering::Relaxed) + } + /// Whether or not the Pokemon is on the battlefield. + pub fn on_battle_field(&self) -> bool { + self.on_battle_field.load(Ordering::Relaxed) + } + /// A list of opponents the Pokemon has seen this battle. + pub fn seen_opponents(&self) -> &RwLock>>> { + &self.seen_opponents + } +} + impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> { fn get_script_count(&self) -> usize { let mut c = 3; @@ -624,7 +729,7 @@ impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> { } } -impl<'own, 'library> VolatileScripts<'own> for Pokemon<'own, 'library> { +impl<'own, 'library> VolatileScriptsOwner<'own> for Pokemon<'own, 'library> { fn volatile_scripts(&self) -> &Arc { &self.volatile } diff --git a/src/dynamic_data/models/pokemon_builder.rs b/src/dynamic_data/models/pokemon_builder.rs index 4cf399b..161e7e6 100644 --- a/src/dynamic_data/models/pokemon_builder.rs +++ b/src/dynamic_data/models/pokemon_builder.rs @@ -3,30 +3,47 @@ use crate::dynamic_data::models::learned_move::MoveLearnMethod; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::DynamicLibrary; use crate::static_data::{AbilityIndex, DataLibrary, Gender}; -use crate::StringKey; +use crate::{Random, StringKey}; +/// This allows for the easy chain building of a Pokemon. pub struct PokemonBuilder<'own> { + /// The library of the Pokemon. library: &'own DynamicLibrary, + /// The name of the species of the Pokemon. species: StringKey, + /// The level of the Pokemon. level: LevelInt, + /// The moves the Pokemon will know. learned_moves: Vec, + /// A random seed used for any randomization done. + random_seed: Option, } impl<'own> PokemonBuilder<'own> { + /// Creates a new PokemonBuilder with a library, species, and level. pub fn new(library: &'own DynamicLibrary, species: StringKey, level: LevelInt) -> Self { Self { library, species, level, learned_moves: vec![], + random_seed: None, } } + /// Makes the Pokemon learn a move. pub fn learn_move(mut self, learned_move: StringKey) -> Self { self.learned_moves.push(learned_move); self } + /// Finally turn the builder into an actual Pokemon. pub fn build(self) -> Pokemon<'own, 'own> { + let mut random = if let Some(seed) = self.random_seed { + Random::new(seed) + } else { + Random::default() + }; + let species = self.library.static_data().species().get(&self.species).unwrap(); let form = species.get_default_form(); let p = Pokemon::new( @@ -38,7 +55,7 @@ impl<'own> PokemonBuilder<'own> { index: 0, }, self.level, - 0, + random.get_unsigned(), Gender::Male, 0, &"hardy".into(), diff --git a/src/dynamic_data/models/pokemon_party.rs b/src/dynamic_data/models/pokemon_party.rs index 408d380..d190580 100644 --- a/src/dynamic_data/models/pokemon_party.rs +++ b/src/dynamic_data/models/pokemon_party.rs @@ -1,12 +1,16 @@ -use crate::dynamic_data::models::pokemon::Pokemon; use std::sync::Arc; +use crate::dynamic_data::models::pokemon::Pokemon; + +/// A list of Pokemon belonging to a trainer. #[derive(Debug)] pub struct PokemonParty<'pokemon, 'library> { + /// The underlying list of Pokemon. pokemon: Vec>>>, } impl<'own, 'library> PokemonParty<'own, 'library> { + /// Instantiates a party with a set size. pub fn new(size: usize) -> Self { let mut pokemon = Vec::with_capacity(size); for _i in 0..size { @@ -15,10 +19,12 @@ impl<'own, 'library> PokemonParty<'own, 'library> { Self { pokemon } } + /// Instantiates a party with a list. pub fn new_from_vec(pokemon: Vec>>>) -> Self { Self { pokemon } } + /// Gets a Pokemon at an index in the party. pub fn at(&self, index: usize) -> &Option>> { let opt = self.pokemon.get(index); if let Some(v) = opt { @@ -28,10 +34,12 @@ impl<'own, 'library> PokemonParty<'own, 'library> { } } + /// Swaps two Pokemon in the party around. pub fn switch(&mut self, a: usize, b: usize) { self.pokemon.swap(a, b); } + /// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon. pub fn swap_into( &mut self, index: usize, @@ -45,6 +53,7 @@ impl<'own, 'library> PokemonParty<'own, 'library> { old } + /// Whether or not the party still has Pokemon that can be used in battle. pub fn has_usable_pokemon(&self) -> bool { for pokemon in self.pokemon.iter().flatten() { if pokemon.is_usable() { @@ -54,14 +63,17 @@ impl<'own, 'library> PokemonParty<'own, 'library> { false } + /// Get the length of the underlying list of Pokemon. pub fn length(&self) -> usize { self.pokemon.len() } + /// Gets the underlying list of Pokemon. pub fn pokemon(&self) -> &Vec>>> { &self.pokemon } + /// Makes sure there are no empty spots in the party anymore, leaving the length the same. pub fn pack_party(&mut self) { let mut first_empty = None; let mut i = 0; diff --git a/src/dynamic_data/script_handling/item_script.rs b/src/dynamic_data/script_handling/item_script.rs index 50fc844..ffd0309 100644 --- a/src/dynamic_data/script_handling/item_script.rs +++ b/src/dynamic_data/script_handling/item_script.rs @@ -1 +1,2 @@ +/// The script functions that are relevant to item use. pub trait ItemScript {} diff --git a/src/dynamic_data/script_handling/mod.rs b/src/dynamic_data/script_handling/mod.rs index 5f1687c..812460f 100644 --- a/src/dynamic_data/script_handling/mod.rs +++ b/src/dynamic_data/script_handling/mod.rs @@ -9,13 +9,15 @@ pub use script::*; #[doc(inline)] pub use script_set::*; #[doc(inline)] -pub use volatile_scripts::*; +pub use volatile_scripts_owner::*; mod item_script; mod script; mod script_set; -mod volatile_scripts; +mod volatile_scripts_owner; +/// This macro runs a script function on a given ScriptSource, and all its parents. It will ensure +/// to only run the script function if it is not suppressed, and can take any amount of parameters. #[macro_export] macro_rules! script_hook { ($hook_name: ident, $source: ident, $($parameters: expr),*) => { @@ -33,6 +35,8 @@ macro_rules! script_hook { }; } +/// This macro runs a script function on all scripts in a Vec of scripts. It will ensure it only +/// runs the script function if it is not suppressed, and can take any amount of parameters. #[macro_export] macro_rules! run_scripts { ($hook_name: ident, $source: ident, $($parameters: expr),*) => { @@ -68,14 +72,20 @@ macro_rules! run_scripts { }; } +/// The script source data is the basic data required for any script source. #[derive(Default, Debug)] pub struct ScriptSourceData { + /// Whether or not the data has been initialized yet. is_initialized: bool, + /// A list that references all possible scripts on this source, and it's parents. scripts: Vec, } +/// A script source is a struct on which we can trigger scripts to be run from. pub trait ScriptSource<'a> { - fn get_script_iterator(&self) -> ScriptAggregator { + /// Gets an iterator over all the scripts that are relevant to this script source. If the data + /// has not been initialised, it will do so here. + fn get_script_iterator(&self) -> ScriptIterator { let lock = self.get_script_source_data(); if !lock.read().is_initialized { let mut data = lock.write(); @@ -83,17 +93,28 @@ pub trait ScriptSource<'a> { self.collect_scripts(&mut data.scripts); data.is_initialized = true; } - ScriptAggregator::new(&lock.read().scripts as *const Vec) + ScriptIterator::new(&lock.read().scripts as *const Vec) } + + /// The number of scripts that are expected to be relevant for this source. This generally is + /// the number of its own scripts + the number of scripts for any parents. fn get_script_count(&self) -> usize; + /// Returns the underlying data required for us to be a script source. fn get_script_source_data(&self) -> &RwLock; + /// This should add all scripts belonging to this source to the scripts Vec, disregarding its + /// potential parents. fn get_own_scripts(&self, scripts: &mut Vec); + /// This should add all scripts that are relevant to the source the the scripts Vec, including + /// everything that belongs to its parents. fn collect_scripts(&self, scripts: &mut Vec); } +/// Enum to store both ScriptSets and sets in a single value. #[derive(Debug)] pub enum ScriptWrapper { + /// A reference to a single script. Script(Weak>>>), + /// A reference to a ScriptSet. Set(Weak), } @@ -109,29 +130,31 @@ impl From<&Arc> for ScriptWrapper { } } -pub struct ScriptAggregator { +/// This struct allows for the iteration over scripts. +pub struct ScriptIterator { + /// A pointer to the vector of ScriptWrappers. This can be a pointer, as we know it remains valid + /// while we're using it. scripts: *const Vec, - size: i32, + /// The current index in the scripts. index: i32, + /// If we're currently inside a set, the current index inside the set. set_index: i32, } -impl ScriptAggregator { +impl ScriptIterator { + /// Instantiates an iterator. pub fn new(scripts: *const Vec) -> Self { - unsafe { - let len = scripts.as_ref().unwrap().len(); - Self { - scripts, - size: len as i32, - index: -1, - set_index: -1, - } + Self { + scripts, + index: -1, + set_index: -1, } } + /// Move to the next valid value in the scripts. fn increment_to_next_value(&mut self) -> bool { if self.index != -1 { - let wrapper = unsafe { &self.scripts.as_ref().unwrap()[self.index as usize] }; + let wrapper = unsafe { &(*self.scripts)[self.index as usize] }; if let ScriptWrapper::Set(set) = wrapper { if let Some(set) = set.upgrade() { self.set_index += 1; @@ -144,7 +167,8 @@ impl ScriptAggregator { } } self.index += 1; - for index in self.index..self.size { + let len = (unsafe { &*self.scripts }).len() as i32; + for index in self.index..len { self.index = index; let wrapper = unsafe { &self.scripts.as_ref().unwrap()[self.index as usize] }; if let ScriptWrapper::Set(s) = wrapper { @@ -166,6 +190,7 @@ impl ScriptAggregator { false } + /// Gets the next valid script. If none is found, returns None. pub fn get_next(&mut self) -> Option { if !self.increment_to_next_value() { return None; @@ -183,6 +208,7 @@ impl ScriptAggregator { } } + /// Resets the iterator to the start. pub fn reset(&mut self) { self.index = -1; self.set_index = -1; @@ -260,7 +286,7 @@ mod tests { fn script_aggregator_property_iterates_single_script() { let script = ScriptContainer::new(Arc::new(TestScript::new())); let scripts = vec![ScriptWrapper::from(&script)]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); while let Some(v) = aggregator.get_next() { v.get().unwrap().read().as_ref().unwrap().on_initialize(&[]); } @@ -272,7 +298,7 @@ mod tests { fn script_aggregator_property_iterates_single_script_with_resets() { let script = ScriptContainer::new(Arc::new(TestScript::new())); let scripts = vec![ScriptWrapper::from(&script)]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); for i in 1..11 { aggregator.reset(); while let Some(v) = aggregator.get_next() { @@ -293,7 +319,7 @@ mod tests { ScriptWrapper::from(&script2), ScriptWrapper::from(&script3), ]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); while let Some(v) = aggregator.get_next() { v.get().unwrap().read().as_ref().unwrap().on_initialize(&[]); } @@ -315,7 +341,7 @@ mod tests { ScriptWrapper::from(&script2), ScriptWrapper::from(&script3), ]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); for i in 1..11 { aggregator.reset(); while let Some(v) = aggregator.get_next() { @@ -338,7 +364,7 @@ mod tests { set.add(Arc::new(TestScript::new_with_name("test_c"))); let scripts = vec![ScriptWrapper::from(&set)]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); for i in 1..11 { aggregator.reset(); while let Some(v) = aggregator.get_next() { @@ -367,7 +393,7 @@ mod tests { set.add(Arc::new(TestScript::new_with_name("test_c"))); let scripts = vec![ScriptWrapper::from(&set)]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); assert_eq!( aggregator .get_next() @@ -407,7 +433,7 @@ mod tests { set.add(Arc::new(TestScript::new_with_name("test_c"))); let scripts = vec![ScriptWrapper::from(&set)]; - let mut aggregator = ScriptAggregator::new(&scripts as *const Vec); + let mut aggregator = ScriptIterator::new(&scripts as *const Vec); assert_eq!( aggregator .get_next() diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index ce8ecb8..2f83e01 100644 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -12,7 +12,7 @@ use crate::dynamic_data::Battle; use crate::dynamic_data::DamageSource; use crate::dynamic_data::ExecutingMove; use crate::dynamic_data::Pokemon; -use crate::static_data::EffectParameter; +use crate::static_data::{EffectParameter, TypeIdentifier}; use crate::static_data::{Item, Statistic}; use crate::StringKey; @@ -105,7 +105,14 @@ pub trait Script: Send + Sync { /// move, which include the scripts that are attached to the owner of the script. fn on_move_miss(&self, _move: &ExecutingMove, _target: &Arc) {} /// This function allows the script to change the actual type that is used for the move on a target. - fn change_move_type(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _move_type: &mut u8) {} + fn change_move_type( + &self, + _move: &ExecutingMove, + _target: &Arc, + _hit: u8, + _move_type: &mut TypeIdentifier, + ) { + } /// This function allows the script to change how effective a move is on a target. fn change_effectiveness(&self, _move: &ExecutingMove, _target: &Arc, _hit: u8, _effectiveness: &mut f32) {} /// This function allows a script to block an outgoing move from being critical. diff --git a/src/dynamic_data/script_handling/script_set.rs b/src/dynamic_data/script_handling/script_set.rs index 9deea2f..e32112e 100644 --- a/src/dynamic_data/script_handling/script_set.rs +++ b/src/dynamic_data/script_handling/script_set.rs @@ -1,16 +1,23 @@ -use crate::dynamic_data::script_handling::script::{Script, ScriptContainer}; -use crate::{PkmnResult, StringKey}; -use indexmap::IndexMap; -use parking_lot::RwLock; use std::ops::Deref; use std::sync::Arc; +use indexmap::IndexMap; +use parking_lot::RwLock; + +use crate::dynamic_data::script_handling::script::{Script, ScriptContainer}; +use crate::{PkmnResult, StringKey}; + +/// A collection of unique scripts. #[derive(Debug, Default)] pub struct ScriptSet { + /// The scripts collection. This is an indexmap so we can iterate over them and always get the + /// scripts in the same order., while still allowing fast lookup. scripts: RwLock>, } impl ScriptSet { + /// Adds a script to the set. If the script with that name already exists in this set, this + /// makes that script stack instead. The return value here is that script. pub fn add(&self, script: Arc) -> ScriptContainer { if let Some(lock) = self.scripts.read().get(script.name()) { if let Some(existing) = lock.get() { @@ -27,6 +34,8 @@ impl ScriptSet { self.scripts.read().last().unwrap().1.clone() } + /// Adds a script with a name to the set. If the script with that name already exists in this + /// set, this makes that script stack instead. The return value here is that script. pub fn stack_or_add<'b, F>(&self, key: &StringKey, instantiation: &'b F) -> PkmnResult> where F: Fn() -> PkmnResult>>, @@ -51,10 +60,12 @@ impl ScriptSet { } } + /// Gets a script from the set using its unique name. pub fn get(&self, key: &StringKey) -> Option { self.scripts.read().get(key).cloned() } + /// Removes a script from the set using its unique name. pub fn remove(&self, key: &StringKey) { let value = self.scripts.write().shift_remove(key); if let Some(script) = value { @@ -66,6 +77,7 @@ impl ScriptSet { } } + /// Clears all scripts from the set. pub fn clear(&self) { for script in self.scripts.read().deref() { if let Some(script) = script.1.get() { @@ -77,18 +89,24 @@ impl ScriptSet { self.scripts.write().clear(); } + /// Checks if the set has a script with the given name. pub fn has(&self, key: &StringKey) -> bool { self.scripts.read().contains_key(key) } + /// Gets a script from the set at a specific index. pub fn at(&self, index: usize) -> ScriptContainer { self.scripts.read()[index].clone() } + /// Gets the number of scripts in the set. pub fn count(&self) -> usize { self.scripts.read().len() } + /// Get a vector of the scripts in this set. This copies the current scripts into a Vec, and + /// returns that. This allows modifying the scripts in the set, while still being able to iterate + /// over them. pub(crate) fn get_owning_iterator(&self) -> Vec { let s = self.scripts.read(); let mut v = Vec::with_capacity(s.deref().len()); diff --git a/src/dynamic_data/script_handling/volatile_scripts.rs b/src/dynamic_data/script_handling/volatile_scripts_owner.rs similarity index 68% rename from src/dynamic_data/script_handling/volatile_scripts.rs rename to src/dynamic_data/script_handling/volatile_scripts_owner.rs index 2de6d2e..f37cf86 100644 --- a/src/dynamic_data/script_handling/volatile_scripts.rs +++ b/src/dynamic_data/script_handling/volatile_scripts_owner.rs @@ -1,25 +1,33 @@ +use std::sync::Arc; + use crate::dynamic_data::script_handling::script::{Script, ScriptContainer}; use crate::dynamic_data::script_handling::script_set::ScriptSet; use crate::{PkmnResult, StringKey}; -use std::sync::Arc; -pub trait VolatileScripts<'a> { +/// This trait adds a bunch of helper functions to deal with volatile scripts on a struct. +pub trait VolatileScriptsOwner<'a> { + /// Return the [`ScriptSet`] that are our volatile scripts. fn volatile_scripts(&self) -> &Arc; + /// Loads a volatile script by name. fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>>; + /// Check if a volatile script with given name exists. fn has_volatile_script(&self, key: &StringKey) -> bool { self.volatile_scripts().has(key) } + /// Gets a volatile script by name. fn get_volatile_script(&self, key: &StringKey) -> Option { self.volatile_scripts().get(key) } + /// Adds a volatile script by name. fn add_volatile_script(&mut self, key: &StringKey) -> PkmnResult> { self.volatile_scripts() .stack_or_add(key, &|| self.load_volatile_script(key)) } + /// Removes a volatile script by name. fn remove_volatile_script(&mut self, key: &StringKey) { self.volatile_scripts().remove(key) } diff --git a/src/static_data/growth_rates.rs b/src/static_data/growth_rates.rs index bb00821..6b78586 100644 --- a/src/static_data/growth_rates.rs +++ b/src/static_data/growth_rates.rs @@ -1,15 +1,21 @@ use crate::defines::LevelInt; +/// A growth rate defines how much experience is required per level. pub trait GrowthRate { + /// Calculate the level something with this growth rate would have at a certain experience. fn calculate_level(&self, experience: u32) -> LevelInt; + /// Calculate the experience something with this growth rate would have at a certain level. fn calculate_experience(&self, level: LevelInt) -> u32; } +/// An implementation of the growth rate that uses a lookup table for experience. pub struct LookupGrowthRate { + /// The lookup Vec. experience: Vec, } impl LookupGrowthRate { + /// Instantiates a new lookup growth rate. pub fn new(experience: Vec) -> LookupGrowthRate { LookupGrowthRate { experience } } diff --git a/src/static_data/items.rs b/src/static_data/items.rs index 8d40053..c661400 100644 --- a/src/static_data/items.rs +++ b/src/static_data/items.rs @@ -4,41 +4,63 @@ use serde::{Deserialize, Serialize}; use crate::StringKey; +/// An item category defines which bag slot items are stored in. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(u8)] pub enum ItemCategory { + /// This is where most items should go. MiscItem, + /// Pokeballs are used for capturing Pokemons. Pokeball, + /// Medicine is used for healing HP, PP, and status effects Medicine, + /// Berry is used for all berries. Berry, + /// TMHM is used for Technical and Hidden Machines. TMHM, + /// Form Changer is used for items that change forms, such as mega stones. FormChanger, + /// Key Items are single stored items, generally used for story progression. KeyItem, + /// Mail is used for mail items. Mail, } +/// A battle item category defines how the item is categorized when in battle. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[repr(u8)] pub enum BattleItemCategory { + /// This item can't be used in battle. None, + /// This item is used for healing Pokemon. Healing, + /// This item is used for healing Pokemon from a status. StatusHealing, + /// This item is used for capturing Pokemon. Pokeball, + /// This item does not belong in above categories, but is still a battle item. MiscBattleItem, } +/// An item is an object which the player can pick up, keep in their Bag, and use in some manner #[derive(Debug)] pub struct Item { + /// The name of the item. name: StringKey, + /// Which bag slot items are stored in. category: ItemCategory, + /// How the item is categorized when in battle. battle_category: BattleItemCategory, + /// The buying value of the item. price: i32, + /// A set of arbitrary flags that can be set on the item. flags: HashSet, } impl Item { + /// Instantiates an item. pub fn new( name: &StringKey, category: ItemCategory, @@ -55,22 +77,28 @@ impl Item { } } + /// The name of the item. pub fn name(&self) -> &StringKey { &self.name } + /// Which bag slot items are stored in. pub fn category(&self) -> ItemCategory { self.category } + /// How the item is categorized when in battle. pub fn battle_category(&self) -> BattleItemCategory { self.battle_category } + /// The buying value of the item. pub fn price(&self) -> i32 { self.price } + /// A set of arbitrary flags that can be set on the item. pub fn flags(&self) -> &HashSet { &self.flags } + /// Checks whether the item has a specific flag. pub fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } diff --git a/src/static_data/libraries/ability_library.rs b/src/static_data/libraries/ability_library.rs index a478d12..b803b87 100644 --- a/src/static_data/libraries/ability_library.rs +++ b/src/static_data/libraries/ability_library.rs @@ -1,35 +1,31 @@ -use hashbrown::HashMap; +use indexmap::IndexMap; use crate::static_data::Ability; use crate::static_data::DataLibrary; use crate::StringKey; +/// A storage for all abilities that can be used in this data library. #[derive(Debug)] pub struct AbilityLibrary { - map: HashMap>, - list: Vec, + /// The underlying map for the library. + map: IndexMap>, } impl AbilityLibrary { + /// Instantiates a new ability library. pub fn new(capacity: usize) -> AbilityLibrary { AbilityLibrary { - map: HashMap::with_capacity(capacity), - list: Vec::with_capacity(capacity), + map: IndexMap::with_capacity(capacity), } } } impl DataLibrary<'_, Box> for AbilityLibrary { - fn map(&self) -> &HashMap> { + fn map(&self) -> &IndexMap> { &self.map } - - fn list_values(&self) -> &Vec { - &self.list - } - - fn get_modify(&mut self) -> (&mut HashMap>, &mut Vec) { - (&mut self.map, &mut self.list) + fn get_modify(&mut self) -> &mut IndexMap> { + &mut self.map } } @@ -62,17 +58,8 @@ pub mod tests { #[test] fn get_ability_library_direct_map_access() { let lib = build(); - let map = lib.map(); - let ability = map.get(&"test_ability".into()); + let ability = lib.get(&"test_ability".into()); assert!(ability.is_some()); assert_eq!(ability.unwrap().name(), &"test_ability".into()); } - - #[test] - fn get_ability_library_direct_list_access() { - let lib = build(); - let list = lib.list_values(); - assert_eq!(list.len(), 1); - assert!(list.contains(&StringKey::new("test_ability"))); - } } diff --git a/src/static_data/libraries/data_library.rs b/src/static_data/libraries/data_library.rs index c7f3277..909a560 100644 --- a/src/static_data/libraries/data_library.rs +++ b/src/static_data/libraries/data_library.rs @@ -1,43 +1,48 @@ +use indexmap::IndexMap; + use crate::Random; use crate::StringKey; -use hashbrown::HashMap; +/// A data library is a collection of methods to set up a default library, where values are stored +/// by both key, while keeping their insertion order. pub trait DataLibrary<'a, T: 'a> { - fn map(&self) -> &HashMap; - fn list_values(&self) -> &Vec; - fn get_modify(&mut self) -> (&mut HashMap, &mut Vec); + /// Returns the underlying map. + fn map(&self) -> &IndexMap; + /// Returns the underlying map in mutable manner. + fn get_modify(&mut self) -> &mut IndexMap; + /// Adds a new value to the library. fn add(&mut self, key: &StringKey, value: T) { - let modifies = self.get_modify(); - modifies.0.insert(key.clone(), value); - modifies.1.push(key.clone()); + self.get_modify().insert(key.clone(), value); } + /// Removes a value from the library. fn remove(&mut self, key: &StringKey) { - let modifies = self.get_modify(); - let index = modifies.1.iter().position(|r| r == key).unwrap(); - modifies.0.remove(key); - modifies.1.remove(index); + self.get_modify().remove(key); } + /// Gets a value from the library. fn get(&'a self, key: &StringKey) -> Option<&'a T> { - self.map().get(key) + self.map().get::(key) } + /// Gets a mutable value from the library. fn get_mut(&mut self, key: &StringKey) -> Option<&mut T> { - self.get_modify().0.get_mut(key) + self.get_modify().get_mut(key) } + /// Gets the amount of values in the library. fn len(&self) -> usize { self.map().len() } + /// Returns whether the library has no values. fn is_empty(&self) -> bool { self.map().is_empty() } + /// Gets a random value from the library. fn random_value(&self, rand: &mut Random) -> &T { - let i = rand.get_between(0, self.list_values().len() as i32); - let key = &self.list_values()[i as usize]; - return &self.map()[key]; + let i = rand.get_between(0, self.len() as i32); + return &self.map().get_index(i as usize).unwrap().1; } } diff --git a/src/static_data/libraries/growth_rate_library.rs b/src/static_data/libraries/growth_rate_library.rs index 78892f5..1630381 100644 --- a/src/static_data/libraries/growth_rate_library.rs +++ b/src/static_data/libraries/growth_rate_library.rs @@ -7,23 +7,30 @@ use crate::defines::LevelInt; use crate::static_data::GrowthRate; use crate::StringKey; +/// A library to store all growth rates. pub struct GrowthRateLibrary { + /// The underlying data structure. growth_rates: HashMap>, } impl GrowthRateLibrary { + /// Instantiates a new growth rate library with a capacity. pub fn new(capacity: usize) -> GrowthRateLibrary { GrowthRateLibrary { growth_rates: HashMap::with_capacity(capacity), } } + /// Calculates the level for a given growth key name and a certain experience. pub fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt { self.growth_rates[growth_rate].calculate_level(experience) } + /// Calculates the experience for a given growth key name and a certain level. pub fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> u32 { self.growth_rates[growth_rate].calculate_experience(level) } + + /// Adds a new growth rate with a name and value. pub fn add_growth_rate(&mut self, key: &StringKey, value: Box) { self.growth_rates.insert(key.clone(), value); } diff --git a/src/static_data/libraries/item_library.rs b/src/static_data/libraries/item_library.rs index 1ba525b..adbe946 100644 --- a/src/static_data/libraries/item_library.rs +++ b/src/static_data/libraries/item_library.rs @@ -1,35 +1,32 @@ -use hashbrown::HashMap; +use indexmap::IndexMap; use crate::static_data::DataLibrary; use crate::static_data::Item; use crate::StringKey; +/// A library to store all items. #[derive(Debug)] pub struct ItemLibrary { - map: HashMap>, - list: Vec, + /// The underlying data structure. + map: IndexMap>, } impl ItemLibrary { + /// Instantiates a new Item Library. pub fn new(capacity: usize) -> ItemLibrary { ItemLibrary { - map: HashMap::with_capacity(capacity), - list: Vec::with_capacity(capacity), + map: IndexMap::with_capacity(capacity), } } } impl DataLibrary<'_, Box> for ItemLibrary { - fn map(&self) -> &HashMap> { + fn map(&self) -> &IndexMap> { &self.map } - fn list_values(&self) -> &Vec { - &self.list - } - - fn get_modify(&mut self) -> (&mut HashMap>, &mut Vec) { - (&mut self.map, &mut self.list) + fn get_modify(&mut self) -> &mut IndexMap> { + &mut self.map } } diff --git a/src/static_data/libraries/library_settings.rs b/src/static_data/libraries/library_settings.rs index fd449be..9565bb7 100644 --- a/src/static_data/libraries/library_settings.rs +++ b/src/static_data/libraries/library_settings.rs @@ -1,15 +1,19 @@ use crate::defines::LevelInt; +/// This library holds several misc settings for the library. #[derive(Debug)] pub struct LibrarySettings { + /// The highest level a Pokemon can be. maximum_level: LevelInt, } impl LibrarySettings { + /// Creates a new settings library. pub fn new(maximum_level: LevelInt) -> Self { Self { maximum_level } } + /// The highest level a Pokemon can be. pub fn maximum_level(&self) -> LevelInt { self.maximum_level } diff --git a/src/static_data/libraries/mod.rs b/src/static_data/libraries/mod.rs index 8af13e4..3947dfc 100644 --- a/src/static_data/libraries/mod.rs +++ b/src/static_data/libraries/mod.rs @@ -15,7 +15,7 @@ pub use species_library::SpeciesLibrary; #[doc(inline)] pub use static_data::StaticData; #[doc(inline)] -pub use type_library::TypeLibrary; +pub use type_library::*; mod ability_library; mod data_library; diff --git a/src/static_data/libraries/move_library.rs b/src/static_data/libraries/move_library.rs index 31ce402..480759f 100644 --- a/src/static_data/libraries/move_library.rs +++ b/src/static_data/libraries/move_library.rs @@ -1,35 +1,31 @@ -use hashbrown::HashMap; +use indexmap::IndexMap; use crate::static_data::DataLibrary; use crate::static_data::MoveData; use crate::StringKey; +/// A library to store all data for moves. #[derive(Debug)] pub struct MoveLibrary { - map: HashMap, - list: Vec, + /// The underlying map. + map: IndexMap, } impl MoveLibrary { + /// Instantiates a new Move Library. pub fn new(capacity: usize) -> MoveLibrary { MoveLibrary { - map: HashMap::with_capacity(capacity), - list: Vec::with_capacity(capacity), + map: IndexMap::with_capacity(capacity), } } } impl DataLibrary<'_, MoveData> for MoveLibrary { - fn map(&self) -> &HashMap { + fn map(&self) -> &IndexMap { &self.map } - - fn list_values(&self) -> &Vec { - &self.list - } - - fn get_modify(&mut self) -> (&mut HashMap, &mut Vec) { - (&mut self.map, &mut self.list) + fn get_modify(&mut self) -> &mut IndexMap { + &mut self.map } } @@ -45,7 +41,7 @@ pub mod tests { fn build_move() -> MoveData { MoveData::new( &"foo".into(), - 0, + 0.into(), MoveCategory::Physical, 100, 100, diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs index 5877af3..514918c 100644 --- a/src/static_data/libraries/species_library.rs +++ b/src/static_data/libraries/species_library.rs @@ -1,35 +1,31 @@ -use hashbrown::HashMap; +use indexmap::IndexMap; use crate::static_data::DataLibrary; use crate::static_data::Species; use crate::StringKey; +/// A library to store all data for Pokemon species. #[derive(Debug)] pub struct SpeciesLibrary { - map: HashMap>, - list: Vec, + /// The underlying map. + map: IndexMap>, } impl SpeciesLibrary { + /// Instantiates a new Species Library. pub fn new(capacity: usize) -> SpeciesLibrary { SpeciesLibrary { - map: HashMap::with_capacity(capacity), - list: Vec::with_capacity(capacity), + map: IndexMap::with_capacity(capacity), } } } impl<'a> DataLibrary<'a, Box> for SpeciesLibrary { - fn map(&self) -> &HashMap> { + fn map(&self) -> &IndexMap> { &self.map } - - fn list_values(&self) -> &Vec { - &self.list - } - - fn get_modify(&mut self) -> (&mut HashMap>, &mut Vec) { - (&mut self.map, &mut self.list) + fn get_modify(&mut self) -> &mut IndexMap> { + &mut self.map } } diff --git a/src/static_data/libraries/static_data.rs b/src/static_data/libraries/static_data.rs index 6f033b4..02ea402 100644 --- a/src/static_data/libraries/static_data.rs +++ b/src/static_data/libraries/static_data.rs @@ -7,19 +7,29 @@ use crate::static_data::NatureLibrary; use crate::static_data::SpeciesLibrary; use crate::static_data::TypeLibrary; +/// The storage for all different libraries. #[derive(Debug)] pub struct StaticData { + /// Several misc settings for the library. settings: LibrarySettings, + /// All data for Pokemon species. species: SpeciesLibrary, + /// All data for the moves. moves: MoveLibrary, + /// All data for the items. items: ItemLibrary, + /// All data for growth rates. growth_rates: GrowthRateLibrary, + /// All data related to types and type effectiveness. types: TypeLibrary, + /// All data related to natures. natures: NatureLibrary, + /// All data related to abilities. abilities: AbilityLibrary, } impl StaticData { + /// Instantiates a new data collection. pub fn new(settings: LibrarySettings) -> Self { Self { settings, @@ -32,50 +42,64 @@ impl StaticData { abilities: AbilityLibrary::new(0), } } - + /// Several misc settings for the library. pub fn settings(&self) -> &LibrarySettings { &self.settings } + /// All data for Pokemon species. pub fn species(&self) -> &SpeciesLibrary { &self.species } + /// All data for Pokemon species. pub fn species_mut(&mut self) -> &mut SpeciesLibrary { &mut self.species } + /// All data for the moves. pub fn moves(&self) -> &MoveLibrary { &self.moves } + /// All data for the moves. pub fn moves_mut(&mut self) -> &mut MoveLibrary { &mut self.moves } + /// All data for the items. pub fn items(&self) -> &ItemLibrary { &self.items } + /// All data for the items. pub fn items_mut(&mut self) -> &mut ItemLibrary { &mut self.items } + /// All data for growth rates. pub fn growth_rates(&self) -> &GrowthRateLibrary { &self.growth_rates } + /// All data for growth rates. pub fn growth_rates_mut(&mut self) -> &mut GrowthRateLibrary { &mut self.growth_rates } + /// All data related to types and type effectiveness. pub fn types(&self) -> &TypeLibrary { &self.types } + /// All data related to types and type effectiveness. pub fn types_mut(&mut self) -> &mut TypeLibrary { &mut self.types } + /// All data related to natures. pub fn natures(&self) -> &NatureLibrary { &self.natures } + /// All data related to natures. pub fn natures_mut(&mut self) -> &mut NatureLibrary { &mut self.natures } + /// All data related to abilities. pub fn abilities(&self) -> &AbilityLibrary { &self.abilities } + /// All data related to abilities. pub fn abilities_mut(&mut self) -> &mut AbilityLibrary { &mut self.abilities } diff --git a/src/static_data/libraries/type_library.rs b/src/static_data/libraries/type_library.rs index 999c260..224aea6 100644 --- a/src/static_data/libraries/type_library.rs +++ b/src/static_data/libraries/type_library.rs @@ -1,13 +1,32 @@ -use crate::StringKey; use hashbrown::HashMap; +use crate::StringKey; + +/// A unique key that can be used to store a reference to a type. Opaque reference to a byte +/// internally. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash)] +pub struct TypeIdentifier { + /// The unique internal value. + val: u8, +} + +impl From for TypeIdentifier { + fn from(val: u8) -> Self { + Self { val } + } +} + +/// All data related to types and effectiveness. #[derive(Debug)] pub struct TypeLibrary { - types: HashMap, + /// A list of types + types: HashMap, + /// The effectiveness of the different types against each other. effectiveness: Vec>, } impl TypeLibrary { + /// Instantiates a new type library with a specific capacity. pub fn new(capacity: usize) -> TypeLibrary { TypeLibrary { types: HashMap::with_capacity(capacity), @@ -15,15 +34,20 @@ impl TypeLibrary { } } - pub fn get_type_id(&self, key: &StringKey) -> u8 { + /// Gets the type identifier for a type with a name. + pub fn get_type_id(&self, key: &StringKey) -> TypeIdentifier { self.types[key] } - pub fn get_single_effectiveness(&self, attacking: u8, defending: u8) -> f32 { - self.effectiveness[attacking as usize][defending as usize] + /// Gets the effectiveness for a single attacking type against a single defending type. + pub fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32 { + self.effectiveness[attacking.val as usize][defending.val as usize] } - pub fn get_effectiveness(&self, attacking: u8, defending: &[u8]) -> f32 { + /// Gets the effectiveness for a single attacking type against an amount of defending types. + /// This is equivalent to running [`get_single_effectiveness`] on each defending type, and + /// multiplying the results with each other. + pub fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> f32 { let mut e = 1.0; for def in defending { e *= self.get_single_effectiveness(attacking, *def); @@ -31,37 +55,42 @@ impl TypeLibrary { e } - pub fn register_type(&mut self, name: &StringKey) -> u8 { - let id = self.types.len() as u8; + /// Registers a new type in the library. + pub fn register_type(&mut self, name: &StringKey) -> TypeIdentifier { + let id = TypeIdentifier { + val: self.types.len() as u8, + }; self.types.insert(name.clone(), id); - self.effectiveness.resize((id + 1) as usize, vec![]); + self.effectiveness.resize((id.val + 1) as usize, vec![]); for effectiveness in &mut self.effectiveness { - effectiveness.resize((id + 1) as usize, 1.0) + effectiveness.resize((id.val + 1) as usize, 1.0) } id } - pub fn set_effectiveness(&mut self, attacking: u8, defending: u8, effectiveness: f32) { - self.effectiveness[attacking as usize][defending as usize] = effectiveness; + /// Sets the effectiveness for an attacking type against a defending type. + pub fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32) { + self.effectiveness[attacking.val as usize][defending.val as usize] = effectiveness; } } #[cfg(test)] pub mod tests { - use crate::static_data::libraries::type_library::TypeLibrary; use assert_approx_eq::assert_approx_eq; + use crate::static_data::libraries::type_library::TypeLibrary; + pub fn build() -> TypeLibrary { let mut lib = TypeLibrary::new(2); // Borrow as mut so we can insert let w = &mut lib; - w.register_type(&"foo".into()); - w.register_type(&"bar".into()); + let t0 = w.register_type(&"foo".into()); + let t1 = w.register_type(&"bar".into()); // Drops borrow as mut - w.set_effectiveness(0, 1, 0.5); - w.set_effectiveness(1, 0, 2.0); + w.set_effectiveness(t0, t1, 0.5); + w.set_effectiveness(t1, t0, 2.0); lib } @@ -72,14 +101,14 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type(&"foo".into()); - w.register_type(&"bar".into()); + let t0 = w.register_type(&"foo".into()); + let t1 = w.register_type(&"bar".into()); // Drops borrow as mut // Borrow as read so we can read let r = &lib; - assert_eq!(r.get_type_id(&"foo".into()), 0); - assert_eq!(r.get_type_id(&"bar".into()), 1); + assert_eq!(r.get_type_id(&"foo".into()), t0); + assert_eq!(r.get_type_id(&"bar".into()), t1); } #[test] @@ -88,16 +117,16 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type(&"foo".into()); - w.register_type(&"bar".into()); - w.set_effectiveness(0, 1, 0.5); - w.set_effectiveness(1, 0, 2.0); + let t0 = w.register_type(&"foo".into()); + let t1 = w.register_type(&"bar".into()); + w.set_effectiveness(t0, t1, 0.5); + w.set_effectiveness(t1, t0, 2.0); // Drops borrow as mut // Borrow as read so we can read let r = &lib; - assert_approx_eq!(r.get_single_effectiveness(0, 1), 0.5); - assert_approx_eq!(r.get_single_effectiveness(1, 0), 2.0); + assert_approx_eq!(r.get_single_effectiveness(t0, t1), 0.5); + assert_approx_eq!(r.get_single_effectiveness(t1, t0), 2.0); } #[test] @@ -106,15 +135,15 @@ pub mod tests { // Borrow as mut so we can insert let w = &mut lib; - w.register_type(&"foo".into()); - w.register_type(&"bar".into()); - w.set_effectiveness(0, 1, 0.5); - w.set_effectiveness(1, 0, 2.0); + let t0 = w.register_type(&"foo".into()); + let t1 = w.register_type(&"bar".into()); + w.set_effectiveness(t0, t1, 0.5); + w.set_effectiveness(t1, t0, 2.0); // Drops borrow as mut // Borrow as read so we can read let r = &lib; - assert_approx_eq!(r.get_effectiveness(0, &[1_u8, 1_u8]), 0.25); - assert_approx_eq!(r.get_effectiveness(1, &[0_u8, 0_u8]), 4.0); + assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]), 0.25); + assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]), 4.0); } } diff --git a/src/static_data/moves/move_data.rs b/src/static_data/moves/move_data.rs index a3e63a9..bb2f2fa 100644 --- a/src/static_data/moves/move_data.rs +++ b/src/static_data/moves/move_data.rs @@ -1,58 +1,93 @@ -use crate::static_data::SecondaryEffect; -use crate::StringKey; use hashbrown::HashSet; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use crate::static_data::{SecondaryEffect, TypeIdentifier}; +use crate::StringKey; + +/// The move category defines what global kind of move this move is. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum MoveCategory { + /// A physical move uses the physical attack stats and physical defense stats to calculate damage. Physical = 0, + /// A special move uses the special attack stats and special defense stats to calculate damage. Special = 1, + /// A status move does not do damage, and only runs a secondary effect. Status = 2, } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +/// The move target defines what kind of targets the move can touch. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MoveTarget { + /// Adjacent allows a move to target any Pokemon that is either directly to the left or right of + /// the user, opposed to the user, or left or right of the slot that is opposing the user. + #[default] Adjacent = 0, + /// AdjacentAlly allows a move to target any Pokemon that is directly to the left or right of + /// the user. AdjacentAlly, + /// AdjacentAllySelf allows a move to target any Pokemon that is either directly to the left or + /// right of the user, or the user itself. AdjacentAllySelf, + /// AdjacentOpponent allows a move to target any Pokemon that is either the opponent, or directly + /// to the left or right of it. AdjacentOpponent, + /// All makes the move target everything on the field. All, + /// AllAdjacent makes the move target everything adjacent on the field. AllAdjacent, + /// AllAdjacentOpponent makes the move target everything adjacent to the opponent, and the opponent. AllAdjacentOpponent, + /// AllAlly targets all Pokemon on the same side as the user. AllAlly, + /// AllOpponent targets all Pokemon on an opposing side from the user. AllOpponent, + /// Any allows a move to target a single Pokemon, in any position. Any, + /// RandomOpponent allows a move to target a single Pokemon, in a random position. RandomOpponent, + /// SelfUse makes the move target the user itself. #[cfg_attr(feature = "serde", serde(rename = "Self"))] SelfUse, } +/// A move is the skill Pokémon primarily use in battle. This is the data related to that. #[derive(PartialEq, Debug)] pub struct MoveData { + /// The name of the move. name: StringKey, - move_type: u8, + /// The attacking type of the move. + move_type: TypeIdentifier, + /// The category of the move. category: MoveCategory, + /// The base power, not considering any modifiers, the move has. base_power: u8, + /// The accuracy of the move in percentage. Should be 255 for moves that always hit. accuracy: u8, + /// The number of times the move can be used. This can be modified on actually learned moves using + /// PP-Ups base_usages: u8, + /// How the move handles targets. target: MoveTarget, + /// The priority of the move. A higher priority means the move should go before other moves. priority: i8, + /// The optional secondary effect the move has. secondary_effect: Option, + /// Arbitrary flags that can be applied to the move. flags: HashSet, } impl MoveData { + /// Instantiates a new move. pub fn new( name: &StringKey, - move_type: u8, + move_type: TypeIdentifier, category: MoveCategory, base_power: u8, accuracy: u8, @@ -75,37 +110,47 @@ impl MoveData { flags, } } - + /// The name of the move. pub fn name(&self) -> &StringKey { &self.name } - pub fn move_type(&self) -> u8 { + /// The attacking type of the move. + pub fn move_type(&self) -> TypeIdentifier { self.move_type } + /// The category of the move. pub fn category(&self) -> MoveCategory { self.category } + /// The base power, not considering any modifiers, the move has. pub fn base_power(&self) -> u8 { self.base_power } + /// The accuracy of the move in percentage. Should be 255 for moves that always hit. pub fn accuracy(&self) -> u8 { self.accuracy } + /// The number of times the move can be used. This can be modified on actually learned moves using + /// PP-Ups pub fn base_usages(&self) -> u8 { self.base_usages } + /// How the move handles targets. pub fn target(&self) -> MoveTarget { self.target } + /// The priority of the move. A higher priority means the move should go before other moves. pub fn priority(&self) -> i8 { self.priority } + /// The optional secondary effect the move has. pub fn secondary_effect(&self) -> &Option { &self.secondary_effect } + /// Arbitrary flags that can be applied to the move. pub fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } diff --git a/src/static_data/moves/secondary_effect.rs b/src/static_data/moves/secondary_effect.rs index 90588c9..36e41ed 100644 --- a/src/static_data/moves/secondary_effect.rs +++ b/src/static_data/moves/secondary_effect.rs @@ -1,21 +1,32 @@ use crate::StringKey; +/// A parameter for an effect. This is basically a simple way to dynamically store multiple different +/// primitives on data. #[derive(PartialEq, Debug)] pub enum EffectParameter { + /// A boolean value. Bool(bool), + /// An integer value. Stored as a 64 bit int to deal with potentially large numbers. Int(i64), + /// A float value. Stored as a 32 bit float. Float(f32), + /// A string value. String(String), } +/// A secondary effect is an effect on a move that happens after it hits. #[derive(PartialEq, Debug)] pub struct SecondaryEffect { + /// The chance in percentages that the effect triggers. -1 to make it always trigger. chance: f32, + /// The name of the effect. effect_name: StringKey, + /// A list of parameters for the effect. parameters: Vec, } impl SecondaryEffect { + /// Instantiates a new Secondary Effect. pub fn new(chance: f32, effect_name: StringKey, parameters: Vec) -> SecondaryEffect { SecondaryEffect { chance, @@ -24,12 +35,15 @@ impl SecondaryEffect { } } + /// The chance in percentages that the effect triggers. -1 to make it always trigger. pub fn chance(&self) -> f32 { self.chance } + /// The name of the effect. pub fn effect_name(&self) -> &StringKey { &self.effect_name } + /// A list of parameters for the effect. pub fn parameters(&self) -> &Vec { &self.parameters } @@ -37,9 +51,10 @@ impl SecondaryEffect { #[cfg(test)] mod tests { - use crate::static_data::moves::secondary_effect::SecondaryEffect; use assert_approx_eq::assert_approx_eq; + use crate::static_data::moves::secondary_effect::SecondaryEffect; + #[test] fn create_secondary_effect() { let empty = SecondaryEffect::new(0.0, "".into(), vec![]); diff --git a/src/static_data/natures.rs b/src/static_data/natures.rs index 4fe8885..5d5acc7 100644 --- a/src/static_data/natures.rs +++ b/src/static_data/natures.rs @@ -1,16 +1,24 @@ -use crate::static_data::Statistic; -use crate::StringKey; use hashbrown::HashMap; +use crate::static_data::Statistic; +use crate::StringKey; + +/// A nature is an attribute on a Pokemon that modifies the effective base stats on a Pokemon. They +/// can have an increased statistic and a decreased statistic, or be neutral. #[derive(Debug)] pub struct Nature { + /// The stat that should receive the increased modifier. increase_stat: Statistic, + /// The stat that should receive the decreased modifier. decrease_stat: Statistic, + /// The amount by which the increased stat is multiplied. increase_modifier: f32, + /// The amount by which the decreased stat is multiplied. decrease_modifier: f32, } impl Nature { + /// Instantiates a new statistic. pub fn new( increase_stat: Statistic, decrease_stat: Statistic, @@ -25,14 +33,18 @@ impl Nature { } } + /// The stat that should receive the increased modifier. pub fn increased_stat(&self) -> Statistic { self.increase_stat } + /// The stat that should receive the decreased modifier. pub fn decreased_stat(&self) -> Statistic { self.decrease_stat } + /// Calculates the modifier for a given stat. If it's the increased stat, returns the increased + /// modifier, if it's the decreased stat, returns the decreased modifier. Otherwise returns 1.0 pub fn get_stat_modifier(&self, stat: Statistic) -> f32 { if stat == self.increase_stat { self.increase_modifier @@ -44,26 +56,32 @@ impl Nature { } } +/// A library of all natures that can be used, stored by their names. #[derive(Debug)] pub struct NatureLibrary { + /// The underlying data structure. map: HashMap, } impl NatureLibrary { + /// Creates a new nature library with a given capacity. pub fn new(capacity: usize) -> Self { NatureLibrary { map: HashMap::with_capacity(capacity), } } + /// Adds a new nature with name to the library. pub fn load_nature(&mut self, name: StringKey, nature: Nature) { self.map.insert(name, nature); } + /// Gets a nature by name. pub fn get_nature(&self, key: &StringKey) -> Option<&Nature> { self.map.get(key) } + /// Finds a nature name by nature. pub fn get_nature_name(&self, nature: &Nature) -> StringKey { for kv in &self.map { // As natures can't be copied, and should always be the same reference as the value diff --git a/src/static_data/species_data/form.rs b/src/static_data/species_data/form.rs index 6453f7c..44667f1 100644 --- a/src/static_data/species_data/form.rs +++ b/src/static_data/species_data/form.rs @@ -1,32 +1,46 @@ -use crate::static_data::AbilityIndex; +use hashbrown::HashSet; + use crate::static_data::LearnableMoves; use crate::static_data::Statistic; use crate::static_data::{Ability, StaticStatisticSet}; +use crate::static_data::{AbilityIndex, TypeIdentifier}; use crate::Random; use crate::StringKey; -use hashbrown::HashSet; +/// A form is a variant of a specific species. A species always has at least one form, but can have +/// many more. #[derive(Debug)] pub struct Form { + /// The name of the form. name: StringKey, + /// The height of the form in meters. height: f32, + /// The weight of the form in kilograms. weight: f32, + /// The base amount of experience that is gained when beating a Pokemon with this form. base_experience: u32, - types: Vec, + /// The normal types a Pokemon with this form has. + types: Vec, + /// The inherent values of a form of species that are used for the stats of a Pokemon. base_stats: StaticStatisticSet, + /// The possible abilities a Pokemon with this form can have. abilities: Vec, + /// The possible hidden abilities a Pokemon with this form can have. hidden_abilities: Vec, + /// The moves a Pokemon with this form can learn. moves: LearnableMoves, + /// Arbitrary flags can be set on a form for scripting use. flags: HashSet, } impl Form { + /// Instantiates a new form. pub fn new( name: &StringKey, height: f32, weight: f32, base_experience: u32, - types: Vec, + types: Vec, base_stats: StaticStatisticSet, abilities: Vec, hidden_abilities: Vec, @@ -47,45 +61,58 @@ impl Form { } } + /// The name of the form. pub fn name(&self) -> &StringKey { &self.name } + /// The height of the form in meters. pub fn height(&self) -> f32 { self.height } + /// The weight of the form in kilograms. pub fn weight(&self) -> f32 { self.weight } + /// The base amount of experience that is gained when beating a Pokemon with this form. pub fn base_experience(&self) -> u32 { self.base_experience } - pub fn types(&self) -> &Vec { + /// The normal types a Pokemon with this form has. + pub fn types(&self) -> &Vec { &self.types } + /// The inherent values of a form of species that are used for the stats of a Pokemon. pub fn base_stats(&self) -> &StaticStatisticSet { &self.base_stats } + /// The possible abilities a Pokemon with this form can have. pub fn abilities(&self) -> &Vec { &self.abilities } + /// The possible hidden abilities a Pokemon with this form can have. pub fn hidden_abilities(&self) -> &Vec { &self.hidden_abilities } + /// The moves a Pokemon with this form can learn. pub fn moves(&self) -> &LearnableMoves { &self.moves } + /// Arbitrary flags can be set on a form for scripting use. pub fn flags(&self) -> &HashSet { &self.flags } - pub fn get_type(&self, index: usize) -> u8 { + /// Get a type of the move at a certain index. + pub fn get_type(&self, index: usize) -> TypeIdentifier { self.types[index] } + /// Gets a single base stat value. pub fn get_base_stat(&self, stat: Statistic) -> u16 { self.base_stats.get_stat(stat) } + /// Find the index of an ability that can be on this form. pub fn find_ability_index(&self, ability: &Ability) -> Option { for (index, a) in self.abilities.iter().enumerate() { if a == ability.name() { @@ -106,6 +133,7 @@ impl Form { None } + /// Gets an ability from the form. pub fn get_ability(&self, index: AbilityIndex) -> &StringKey { if index.hidden { &self.hidden_abilities[index.index as usize] @@ -114,13 +142,16 @@ impl Form { } } + /// Gets a random ability from the form. pub fn get_random_ability(&self, rand: &mut Random) -> &StringKey { &self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize] } + /// Gets a random hidden ability from the form. pub fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey { &self.hidden_abilities[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize] } + /// Check if the form has a specific flag set. pub fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) } diff --git a/src/utils/random.rs b/src/utils/random.rs index 1bc0976..3b7cb0f 100644 --- a/src/utils/random.rs +++ b/src/utils/random.rs @@ -2,25 +2,27 @@ use rand::distributions::{Distribution, Uniform}; use rand::{Rng, SeedableRng}; use rand_pcg::Pcg32; +/// A random number generator. #[derive(Clone)] pub struct Random { + /// The seed of the random number generator. seed: u128, + /// A float distribution. distribution: Uniform, + /// The underlying RNG. PCG for fast, hard to predict random number generation. random_gen: Pcg32, } impl Default for Random { + /// The default for the RNG uses the nanoseconds since epoch as seed. fn default() -> Self { let seed = chrono::Utc::now().timestamp_nanos() as u128; - Random { - seed, - distribution: Uniform::from(0.0..1.0), - random_gen: Pcg32::from_seed(seed.to_be_bytes()), - } + Random::new(seed) } } impl Random { + /// Creates a new RNG with a specific seed. pub fn new(seed: u128) -> Self { Random { seed, @@ -29,41 +31,60 @@ impl Random { } } + /// The seed used for the RNG. pub fn get_seed(&self) -> u128 { self.seed } + /// Get a random 32 bit integer between minimal and maximal 32 bit integer pub fn get(&mut self) -> i32 { self.random_gen.gen() } + /// Get a random 32 bit signed integer between 0 and max. If max equals 0, always returns 0. pub fn get_max(&mut self, max: i32) -> i32 { - assert!(max > 0); + if max <= 0 { + return 0; + } Uniform::from(0..max).sample(&mut self.random_gen) } + /// Get a random 32 bit signed integer between min and max. If max is equal or less than min, + /// always returns min. pub fn get_between(&mut self, min: i32, max: i32) -> i32 { - assert!(max > min); + if max <= min { + return min; + } Uniform::from(min..max).sample(&mut self.random_gen) } + /// Get a random 32 bit integer unsigned between 0 and maximal 32 bit unsigned int. pub fn get_unsigned(&mut self) -> u32 { self.random_gen.gen() } + /// Get a random 32 bit signed integer between 0 and max. If max equals 0, always returns 0. pub fn get_max_unsigned(&mut self, max: u32) -> u32 { - assert!(max > 0); + if max == 0 { + return 0; + } Uniform::from(0..max).sample(&mut self.random_gen) } + /// Get a random 32 bit unsigned integer between min and max. If max is equal or less than min, + /// always returns min. pub fn get_between_unsigned(&mut self, min: u32, max: u32) -> u32 { - assert!(max > min); + if max <= min { + return min; + } Uniform::from(min..max).sample(&mut self.random_gen) } + /// Gets a random 32 bit float between 0.0 and 1.0 pub fn get_float(&mut self) -> f32 { self.get_double() as f32 } + /// Gets a random 64 bit float between 0.0 and 1.0 pub fn get_double(&mut self) -> f64 { self.distribution.sample(&mut self.random_gen) } @@ -71,11 +92,13 @@ impl Random { #[cfg(test)] mod tests { - use crate::utils::random::Random; - extern crate test; use std::hint::black_box; use test::Bencher; + use crate::utils::random::Random; + + extern crate test; + #[test] #[cfg_attr(miri, ignore)] fn create_random() { diff --git a/src/utils/string_key.rs b/src/utils/string_key.rs index 1f9f2ee..0f67756 100644 --- a/src/utils/string_key.rs +++ b/src/utils/string_key.rs @@ -1,23 +1,30 @@ -use hashbrown::HashMap; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::lazy::SyncLazy; use std::sync::{Arc, Mutex, Weak}; +use hashbrown::HashMap; + /// StringKey is an immutable string that is used for indexing of hashmaps or equality a lot. /// By reference counting the string instead of copying, and caching the hash, we can get some /// free speed out of it. Note that StringKeys also compare case insensitive, so that for example /// `charmander` == `Charmander`. #[derive(Clone, Debug)] pub struct StringKey { + /// The underlying reference counted string. str: Arc, + /// The unique hash of the string. hash: u32, } +/// A cache of all allocated strings. This allows us to re-use strings that are often used without +/// allocation. static STRING_CACHE: SyncLazy>>> = SyncLazy::new(|| Mutex::new(HashMap::new())); +/// An empty StringKey static EMPTY: SyncLazy = SyncLazy::new(|| StringKey::new("")); impl StringKey { + /// Calculates the hash of a string key in a const manner. pub const fn get_hash_const(s: &[u8; N]) -> u32 { let mut crc: u32 = 0xffffffff; @@ -29,6 +36,7 @@ impl StringKey { crc ^ 0xffffffff } + /// Gets the hash of a string. pub fn get_hash(s: &str) -> u32 { let mut crc: u32 = 0xffffffff; for byte in s.bytes() { @@ -37,6 +45,8 @@ impl StringKey { crc ^ 0xffffffff } + /// Creates a new StringKey. If we can find a value for this StringKey in the cache, we re-use + /// that value. pub fn new(s: &str) -> Self { let hash = StringKey::get_hash(s); let mut cache = STRING_CACHE.lock().unwrap(); @@ -54,14 +64,17 @@ impl StringKey { v } + /// Gets the empty StringKey. pub fn empty() -> Self { EMPTY.clone() } + /// Gets the underlying string for the StringKey. pub fn str(&self) -> &str { &self.str } + /// Gets the hash of the string value. pub fn hash(&self) -> u32 { self.hash } @@ -93,6 +106,7 @@ impl Display for StringKey { } } +/// Converts a character to lowercased in a const safe way. const fn to_lower(c: u8) -> u8 { if c >= b'A' && c <= b'Z' { return c + (b'a' - b'A'); @@ -100,6 +114,7 @@ const fn to_lower(c: u8) -> u8 { c } +/// A lookup table for use in CRC32 hash. const CRC_TABLE: &[u32] = &[ 0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs index 46aefbe..9f06ffb 100644 --- a/tests/common/library_loader.rs +++ b/tests/common/library_loader.rs @@ -59,7 +59,7 @@ pub fn load_types(path: &String, type_library: &mut TypeLibrary) { for (i, v) in record.iter().skip(1).enumerate() { let effectiveness = v.parse::().unwrap(); - type_library.set_effectiveness(offensive_type_id, i as u8, effectiveness); + type_library.set_effectiveness(offensive_type_id, (i as u8).into(), effectiveness); } } }