Initial commit, implements most of the static_data side.
This commit is contained in:
commit
2a08fb2645
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
.idea/
|
|
@ -0,0 +1,46 @@
|
|||
[package]
|
||||
name = "pkmn_lib"
|
||||
version = "0.1.0"
|
||||
authors = ["Deukhoofd <Deukhoofd@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "pkmn_lib"
|
||||
crate_type = ["cdylib"]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
debug = true
|
||||
debug-assertions = true
|
||||
overflow-checks = true
|
||||
lto = "thin"
|
||||
panic = 'unwind'
|
||||
incremental = true
|
||||
codegen-units = 256
|
||||
rpath = false
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
debug = 1
|
||||
debug-assertions = false
|
||||
overflow-checks = true
|
||||
lto = "fat"
|
||||
panic = 'unwind'
|
||||
incremental = false
|
||||
codegen-units = 16
|
||||
rpath = false
|
||||
|
||||
[dependencies]
|
||||
# Used for PrimInt, so we can check if a generic is an integer
|
||||
num-traits = "0.2"
|
||||
# Used for automatically generating getters
|
||||
derive-getters = "0.2.0"
|
||||
# Allow us to assert whether floats are approximately a value
|
||||
assert_approx_eq = "1.1.0"
|
||||
# Used for time based code (i.e. randomness)
|
||||
chrono = "0.4.19"
|
||||
# Used for RNG
|
||||
rand = "0.8.3"
|
||||
rand_pcg = "0.3.0"
|
||||
# Used for the hashmap!{} macro, creating a simple initializer pattern for hashmaps.
|
||||
maplit = "1.0.2"
|
|
@ -0,0 +1 @@
|
|||
pub type LevelInt = u8;
|
|
@ -0,0 +1,10 @@
|
|||
// The too many arguments is annoying, especially for when we create constructors, disable.
|
||||
#![allow(clippy::too_many_arguments, clippy::needless_range_loop)]
|
||||
#![feature(nll)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
|
||||
pub mod defines;
|
||||
pub mod static_data;
|
||||
pub mod utils;
|
|
@ -0,0 +1,6 @@
|
|||
use crate::defines::LevelInt;
|
||||
|
||||
pub trait GrowthRate {
|
||||
fn calculate_level(&self, experience: u32) -> LevelInt;
|
||||
fn calculate_experience(&self, level: LevelInt) -> u32;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use crate::defines::LevelInt;
|
||||
use crate::static_data::growth_rates::growth_rate::GrowthRate;
|
||||
|
||||
pub struct LookupGrowthRate {
|
||||
experience: Vec<u32>,
|
||||
}
|
||||
|
||||
impl LookupGrowthRate {
|
||||
pub fn new(experience: Vec<u32>) -> LookupGrowthRate {
|
||||
LookupGrowthRate { experience }
|
||||
}
|
||||
}
|
||||
|
||||
impl GrowthRate for LookupGrowthRate {
|
||||
fn calculate_level(&self, experience: u32) -> LevelInt {
|
||||
for (i, v) in self.experience.iter().enumerate() {
|
||||
if *v > experience {
|
||||
return i as LevelInt;
|
||||
}
|
||||
}
|
||||
self.experience.len() as LevelInt
|
||||
}
|
||||
|
||||
fn calculate_experience(&self, level: LevelInt) -> u32 {
|
||||
self.experience[(level - 1) as usize]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod growth_rate;
|
||||
pub mod lookup_growth_rate;
|
|
@ -0,0 +1,34 @@
|
|||
use super::item_category::{BattleItemCategory, ItemCategory};
|
||||
use derive_getters::Getters;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Getters, Debug)]
|
||||
pub struct Item {
|
||||
name: String,
|
||||
category: ItemCategory,
|
||||
battle_category: BattleItemCategory,
|
||||
price: i32,
|
||||
flags: HashSet<String>,
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
category: ItemCategory,
|
||||
battle_category: BattleItemCategory,
|
||||
price: i32,
|
||||
flags: HashSet<String>,
|
||||
) -> Item {
|
||||
Item {
|
||||
name: name.to_string(),
|
||||
category,
|
||||
battle_category,
|
||||
price,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_flag(&self, key: &str) -> bool {
|
||||
self.flags.contains(key)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#[derive(Debug)]
|
||||
pub enum ItemCategory {
|
||||
MiscItem,
|
||||
Pokeball,
|
||||
Medicine,
|
||||
Berry,
|
||||
TMHM,
|
||||
FormeChanger,
|
||||
KeyItem,
|
||||
Mail,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BattleItemCategory {
|
||||
Healing,
|
||||
StatusHealing,
|
||||
Pokeball,
|
||||
MiscBattleItem,
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod item;
|
||||
pub mod item_category;
|
|
@ -0,0 +1,42 @@
|
|||
use crate::utils::random::Random;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait DataLibrary<'a, T: 'a> {
|
||||
fn map(&self) -> &HashMap<String, T>;
|
||||
fn list_values(&self) -> &Vec<String>;
|
||||
fn get_modify(&mut self) -> (&mut HashMap<String, T>, &mut Vec<String>);
|
||||
|
||||
fn add(&mut self, key: &str, value: T) {
|
||||
let modifies = self.get_modify();
|
||||
modifies.0.insert(key.to_string(), value);
|
||||
modifies.1.push(key.to_string());
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &str) {
|
||||
let modifies = self.get_modify();
|
||||
let index = modifies.1.iter().position(|r| *r == key).unwrap();
|
||||
modifies.0.remove(key);
|
||||
modifies.1.remove(index);
|
||||
}
|
||||
|
||||
fn get(&self, key: &str) -> Option<&T> {
|
||||
self.map().get(key)
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, key: &str) -> Option<&mut T> {
|
||||
self.get_modify().0.get_mut(key)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.map().len()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.map().is_empty()
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
use crate::defines::LevelInt;
|
||||
use crate::static_data::growth_rates::growth_rate::GrowthRate;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
pub struct GrowthRateLibrary {
|
||||
growth_rates: HashMap<String, Box<dyn GrowthRate>>,
|
||||
}
|
||||
|
||||
impl GrowthRateLibrary {
|
||||
pub fn new(capacity: usize) -> GrowthRateLibrary {
|
||||
GrowthRateLibrary {
|
||||
growth_rates: HashMap::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_level(&self, growth_rate: &str, experience: u32) -> LevelInt {
|
||||
self.growth_rates[growth_rate].calculate_level(experience)
|
||||
}
|
||||
pub fn calculate_experience(&self, growth_rate: &str, level: LevelInt) -> u32 {
|
||||
self.growth_rates[growth_rate].calculate_experience(level)
|
||||
}
|
||||
pub fn add_growth_rate(&mut self, key: &str, value: Box<dyn GrowthRate>) {
|
||||
self.growth_rates.insert(key.to_string(), value);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for GrowthRateLibrary {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("GrowthRateLibrary").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::static_data::growth_rates::lookup_growth_rate::LookupGrowthRate;
|
||||
use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary;
|
||||
|
||||
#[test]
|
||||
fn add_growth_rate_to_library_and_calculate_level() {
|
||||
let mut lib = GrowthRateLibrary::new(1);
|
||||
|
||||
// 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])));
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
assert_eq!(r.calculate_level("foo", 3), 1);
|
||||
assert_eq!(r.calculate_level("foo", 50), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_growth_rate_to_library_and_calculate_experience() {
|
||||
let mut lib = GrowthRateLibrary::new(1);
|
||||
|
||||
// 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])));
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
assert_eq!(r.calculate_experience("foo", 1), 0);
|
||||
assert_eq!(r.calculate_experience("foo", 3), 10);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
use crate::static_data::items::item::Item;
|
||||
use crate::static_data::libraries::data_library::DataLibrary;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItemLibrary {
|
||||
map: HashMap<String, Item>,
|
||||
list: Vec<String>,
|
||||
}
|
||||
|
||||
impl ItemLibrary {
|
||||
pub fn new(capacity: usize) -> ItemLibrary {
|
||||
ItemLibrary {
|
||||
map: HashMap::with_capacity(capacity),
|
||||
list: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataLibrary<'_, Item> for ItemLibrary {
|
||||
fn map(&self) -> &HashMap<String, Item> {
|
||||
&self.map
|
||||
}
|
||||
|
||||
fn list_values(&self) -> &Vec<String> {
|
||||
&self.list
|
||||
}
|
||||
|
||||
fn get_modify(&mut self) -> (&mut HashMap<String, Item>, &mut Vec<String>) {
|
||||
(&mut self.map, &mut self.list)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
use crate::defines::LevelInt;
|
||||
use derive_getters::Getters;
|
||||
|
||||
#[derive(Getters, Debug)]
|
||||
pub struct LibrarySettings {
|
||||
maximum_level: LevelInt,
|
||||
maximum_move_count: u8,
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
pub mod data_library;
|
||||
pub mod growth_rate_library;
|
||||
pub mod item_library;
|
||||
pub mod library_settings;
|
||||
pub mod move_library;
|
||||
pub mod species_library;
|
||||
pub mod static_data;
|
||||
pub mod type_library;
|
|
@ -0,0 +1,32 @@
|
|||
use crate::static_data::libraries::data_library::DataLibrary;
|
||||
use crate::static_data::moves::move_data::MoveData;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MoveLibrary {
|
||||
map: HashMap<String, MoveData>,
|
||||
list: Vec<String>,
|
||||
}
|
||||
|
||||
impl MoveLibrary {
|
||||
pub fn new(capacity: usize) -> MoveLibrary {
|
||||
MoveLibrary {
|
||||
map: HashMap::with_capacity(capacity),
|
||||
list: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataLibrary<'_, MoveData> for MoveLibrary {
|
||||
fn map(&self) -> &HashMap<String, MoveData> {
|
||||
&self.map
|
||||
}
|
||||
|
||||
fn list_values(&self) -> &Vec<String> {
|
||||
&self.list
|
||||
}
|
||||
|
||||
fn get_modify(&mut self) -> (&mut HashMap<String, MoveData>, &mut Vec<String>) {
|
||||
(&mut self.map, &mut self.list)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
use crate::static_data::libraries::data_library::DataLibrary;
|
||||
use crate::static_data::species_data::species::Species;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SpeciesLibrary<'a> {
|
||||
map: HashMap<String, Species<'a>>,
|
||||
list: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'a> SpeciesLibrary<'a> {
|
||||
pub fn new(capacity: usize) -> SpeciesLibrary<'a> {
|
||||
SpeciesLibrary {
|
||||
map: HashMap::with_capacity(capacity),
|
||||
list: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DataLibrary<'a, Species<'a>> for SpeciesLibrary<'a> {
|
||||
fn map(&self) -> &HashMap<String, Species<'a>> {
|
||||
&self.map
|
||||
}
|
||||
|
||||
fn list_values(&self) -> &Vec<String> {
|
||||
&self.list
|
||||
}
|
||||
|
||||
fn get_modify(&mut self) -> (&mut HashMap<String, Species<'a>>, &mut Vec<String>) {
|
||||
(&mut self.map, &mut self.list)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::static_data::libraries::data_library::DataLibrary;
|
||||
use crate::static_data::libraries::species_library::SpeciesLibrary;
|
||||
use crate::static_data::species_data::form::Form;
|
||||
use crate::static_data::species_data::learnable_moves::LearnableMoves;
|
||||
use crate::static_data::species_data::species::Species;
|
||||
use crate::static_data::statistic_set::StatisticSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn build_species<'a>() -> Species<'a> {
|
||||
Species::new(
|
||||
0,
|
||||
"foo",
|
||||
0.5,
|
||||
"",
|
||||
0,
|
||||
Form::new(
|
||||
"default",
|
||||
0.0,
|
||||
0.0,
|
||||
0,
|
||||
Vec::new(),
|
||||
StatisticSet::default(),
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
LearnableMoves::new(),
|
||||
HashSet::new(),
|
||||
),
|
||||
HashSet::new(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_species_to_library_and_fetch() {
|
||||
let mut lib = SpeciesLibrary::new(1);
|
||||
let species = build_species();
|
||||
|
||||
// Borrow as mut so we can insert
|
||||
let w = &mut lib;
|
||||
w.add("foo", species);
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
let mon = r.get("foo");
|
||||
assert!(mon.is_some());
|
||||
assert_eq!(*mon.unwrap().id(), 0_u16);
|
||||
assert_eq!(mon.unwrap().name(), "foo");
|
||||
assert_eq!(r.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_species_to_library_then_remove() {
|
||||
let mut lib = SpeciesLibrary::new(1);
|
||||
let species = build_species();
|
||||
|
||||
// Borrow as mut so we can insert
|
||||
let w = &mut lib;
|
||||
w.add("foo", species);
|
||||
w.remove("foo");
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
let mon = r.get("foo");
|
||||
assert!(mon.is_none());
|
||||
assert_eq!(r.len(), 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary;
|
||||
use crate::static_data::libraries::item_library::ItemLibrary;
|
||||
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 derive_getters::Getters;
|
||||
|
||||
#[derive(Getters, Debug)]
|
||||
struct StaticData<'a> {
|
||||
settings: LibrarySettings,
|
||||
species: SpeciesLibrary<'a>,
|
||||
moves: MoveLibrary,
|
||||
items: ItemLibrary,
|
||||
growth_rates: GrowthRateLibrary,
|
||||
types: TypeLibrary,
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypeLibrary {
|
||||
types: HashMap<String, u8>,
|
||||
effectiveness: Vec<Vec<f32>>,
|
||||
}
|
||||
|
||||
impl TypeLibrary {
|
||||
pub fn new(capacity: usize) -> TypeLibrary {
|
||||
TypeLibrary {
|
||||
types: HashMap::with_capacity(capacity),
|
||||
effectiveness: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type_id(&self, key: &str) -> u8 {
|
||||
self.types[key]
|
||||
}
|
||||
|
||||
pub fn get_single_effectiveness(&self, attacking: u8, defending: u8) -> f32 {
|
||||
self.effectiveness[attacking as usize][defending as usize]
|
||||
}
|
||||
|
||||
pub fn get_effectiveness(&self, attacking: u8, defending: &[u8]) -> f32 {
|
||||
let mut e = 1.0;
|
||||
for def in defending {
|
||||
e *= self.get_single_effectiveness(attacking, *def);
|
||||
}
|
||||
e
|
||||
}
|
||||
|
||||
pub fn register_type(&mut self, name: &str) -> u8 {
|
||||
let id = self.types.len() as u8;
|
||||
self.types.insert(name.to_string(), id);
|
||||
self.effectiveness.resize((id + 1) as usize, vec![]);
|
||||
for effectiveness in &mut self.effectiveness {
|
||||
effectiveness.resize((id + 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;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::static_data::libraries::type_library::TypeLibrary;
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
#[test]
|
||||
fn add_two_types_retrieve_them() {
|
||||
let mut lib = TypeLibrary::new(2);
|
||||
|
||||
// Borrow as mut so we can insert
|
||||
let w = &mut lib;
|
||||
w.register_type("foo");
|
||||
w.register_type("bar");
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
assert_eq!(r.get_type_id("foo"), 0);
|
||||
assert_eq!(r.get_type_id("bar"), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_two_types_set_effectiveness_retrieve() {
|
||||
let mut lib = TypeLibrary::new(2);
|
||||
|
||||
// Borrow as mut so we can insert
|
||||
let w = &mut lib;
|
||||
w.register_type("foo");
|
||||
w.register_type("bar");
|
||||
w.set_effectiveness(0, 1, 0.5);
|
||||
w.set_effectiveness(1, 0, 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_two_types_get_aggregate_effectiveness() {
|
||||
let mut lib = TypeLibrary::new(2);
|
||||
|
||||
// Borrow as mut so we can insert
|
||||
let w = &mut lib;
|
||||
w.register_type("foo");
|
||||
w.register_type("bar");
|
||||
w.set_effectiveness(0, 1, 0.5);
|
||||
w.set_effectiveness(1, 0, 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
pub mod growth_rates;
|
||||
pub mod items;
|
||||
pub mod libraries;
|
||||
pub mod moves;
|
||||
pub mod species_data;
|
||||
pub mod statistic_set;
|
||||
pub mod statistics;
|
|
@ -0,0 +1,2 @@
|
|||
pub mod move_data;
|
||||
pub mod secondary_effect;
|
|
@ -0,0 +1,75 @@
|
|||
use self::super::secondary_effect::SecondaryEffect;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum MoveCategory {
|
||||
Physical,
|
||||
Special,
|
||||
Status,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum MoveTarget {
|
||||
Adjacent,
|
||||
AdjacentAlly,
|
||||
AdjacentAllySelf,
|
||||
AdjacentOpponent,
|
||||
|
||||
All,
|
||||
AllAdjacent,
|
||||
AllAdjacentOpponent,
|
||||
AllAlly,
|
||||
AllOpponent,
|
||||
|
||||
Any,
|
||||
|
||||
RandomOpponent,
|
||||
SelfUse,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct MoveData {
|
||||
name: String,
|
||||
move_type: u8,
|
||||
category: MoveCategory,
|
||||
base_power: u8,
|
||||
accuracy: u8,
|
||||
base_usages: u8,
|
||||
target: MoveTarget,
|
||||
priority: i8,
|
||||
secondary_effect: SecondaryEffect,
|
||||
flags: HashSet<String>,
|
||||
}
|
||||
|
||||
impl MoveData {
|
||||
pub fn new(
|
||||
name: String,
|
||||
move_type: u8,
|
||||
category: MoveCategory,
|
||||
base_power: u8,
|
||||
accuracy: u8,
|
||||
base_usages: u8,
|
||||
target: MoveTarget,
|
||||
priority: i8,
|
||||
secondary_effect: SecondaryEffect,
|
||||
flags: HashSet<String>,
|
||||
) -> MoveData {
|
||||
MoveData {
|
||||
name,
|
||||
move_type,
|
||||
category,
|
||||
base_power,
|
||||
accuracy,
|
||||
base_usages,
|
||||
target,
|
||||
priority,
|
||||
secondary_effect,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_flag(&self, key: &str) -> bool {
|
||||
self.flags.contains(key)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
use derive_getters::Getters;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum EffectParameter {
|
||||
Bool(bool),
|
||||
Int(i64),
|
||||
Float(f32),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Getters)]
|
||||
pub struct SecondaryEffect {
|
||||
chance: f32,
|
||||
effect_name: String,
|
||||
parameters: Vec<EffectParameter>,
|
||||
}
|
||||
|
||||
impl SecondaryEffect {
|
||||
pub fn empty() -> SecondaryEffect {
|
||||
SecondaryEffect {
|
||||
chance: 0.0,
|
||||
effect_name: "".to_string(),
|
||||
parameters: vec![],
|
||||
}
|
||||
}
|
||||
pub fn new(
|
||||
chance: f32,
|
||||
effect_name: String,
|
||||
parameters: Vec<EffectParameter>,
|
||||
) -> SecondaryEffect {
|
||||
SecondaryEffect {
|
||||
chance,
|
||||
effect_name,
|
||||
parameters,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::static_data::moves::secondary_effect::SecondaryEffect;
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
#[test]
|
||||
fn create_secondary_effect() {
|
||||
let empty = SecondaryEffect::empty();
|
||||
assert_approx_eq!(empty.chance, 0.0);
|
||||
assert_eq!(empty.effect_name, "");
|
||||
assert_eq!(empty.parameters.len(), 0);
|
||||
let set = SecondaryEffect::new(50.0, "foo".to_string(), Vec::new());
|
||||
assert_approx_eq!(set.chance, 50.0);
|
||||
assert_eq!(set.effect_name, "foo");
|
||||
assert_eq!(set.parameters.len(), 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AbilityIndex {
|
||||
pub hidden: bool,
|
||||
pub index: u8,
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
use self::super::learnable_moves::LearnableMoves;
|
||||
use crate::static_data::species_data::ability_index::AbilityIndex;
|
||||
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;
|
||||
|
||||
#[derive(Getters, Debug)]
|
||||
pub struct Form<'a> {
|
||||
name: String,
|
||||
height: f32,
|
||||
weight: f32,
|
||||
base_experience: u32,
|
||||
types: Vec<u8>,
|
||||
base_stats: StatisticSet<u16>,
|
||||
abilities: Vec<String>,
|
||||
hidden_abilities: Vec<String>,
|
||||
moves: LearnableMoves<'a>,
|
||||
flags: HashSet<String>,
|
||||
}
|
||||
|
||||
impl<'a> Form<'a> {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
height: f32,
|
||||
weight: f32,
|
||||
base_experience: u32,
|
||||
types: Vec<u8>,
|
||||
base_stats: StatisticSet<u16>,
|
||||
abilities: Vec<String>,
|
||||
hidden_abilities: Vec<String>,
|
||||
moves: LearnableMoves<'a>,
|
||||
flags: HashSet<String>,
|
||||
) -> Form<'a> {
|
||||
Form {
|
||||
name: name.to_string(),
|
||||
height,
|
||||
weight,
|
||||
base_experience,
|
||||
types,
|
||||
base_stats,
|
||||
abilities,
|
||||
hidden_abilities,
|
||||
moves,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type(&self, index: usize) -> u8 {
|
||||
self.types[index]
|
||||
}
|
||||
|
||||
pub fn get_base_stat(&self, stat: Statistic) -> u16 {
|
||||
self.base_stats.get_stat(stat)
|
||||
}
|
||||
|
||||
pub fn find_ability_index(&self, ability: &str) -> Option<AbilityIndex> {
|
||||
for (index, a) in self.abilities.iter().enumerate() {
|
||||
if a == ability {
|
||||
return Some(AbilityIndex {
|
||||
hidden: false,
|
||||
index: index as u8,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (index, a) in self.hidden_abilities.iter().enumerate() {
|
||||
if a == ability {
|
||||
return Some(AbilityIndex {
|
||||
hidden: true,
|
||||
index: index as u8,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_random_ability(&self, rand: &mut Random) -> &String {
|
||||
&self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize]
|
||||
}
|
||||
pub fn get_random_hidden_ability(&self, rand: &mut Random) -> &String {
|
||||
&self.hidden_abilities
|
||||
[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize]
|
||||
}
|
||||
|
||||
pub fn has_flag(&self, key: &str) -> bool {
|
||||
self.flags.contains(key)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature
|
||||
// that allows for a more progressive gender system for those that want it?
|
||||
#[derive(Debug)]
|
||||
pub enum Gender {
|
||||
Male,
|
||||
Female,
|
||||
Genderless,
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
use crate::defines::LevelInt;
|
||||
use crate::static_data::moves::move_data::MoveData;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default, PartialEq, Debug)]
|
||||
pub struct LearnableMoves<'a> {
|
||||
learned_by_level: HashMap<LevelInt, Vec<&'a MoveData>>,
|
||||
distinct_level_moves: Vec<&'a MoveData>,
|
||||
}
|
||||
|
||||
impl<'a> LearnableMoves<'a> {
|
||||
pub fn new() -> LearnableMoves<'a> {
|
||||
LearnableMoves::default()
|
||||
}
|
||||
|
||||
pub fn add_level_move(&mut self, level: LevelInt, m: &'a MoveData) {
|
||||
match self.learned_by_level.entry(level) {
|
||||
Occupied(x) => {
|
||||
x.into_mut().push(m);
|
||||
}
|
||||
Vacant(_) => {
|
||||
self.learned_by_level.insert(level, vec![m]);
|
||||
}
|
||||
}
|
||||
if !self.distinct_level_moves.contains(&m) {
|
||||
self.distinct_level_moves.push(m);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<&'a MoveData>> {
|
||||
self.learned_by_level.get(&level)
|
||||
}
|
||||
|
||||
pub fn get_distinct_level_moves(&self) -> &Vec<&'a MoveData> {
|
||||
&self.distinct_level_moves
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::static_data::moves::move_data::{MoveCategory, MoveData, MoveTarget};
|
||||
use crate::static_data::moves::secondary_effect::SecondaryEffect;
|
||||
use crate::static_data::species_data::learnable_moves::LearnableMoves;
|
||||
|
||||
#[test]
|
||||
fn adds_level_moves() {
|
||||
let mut moves = LearnableMoves::new();
|
||||
let move1 = MoveData::new(
|
||||
"foo".to_string(),
|
||||
0,
|
||||
MoveCategory::Physical,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
MoveTarget::Adjacent,
|
||||
0,
|
||||
SecondaryEffect::empty(),
|
||||
Default::default(),
|
||||
);
|
||||
let move2 = MoveData::new(
|
||||
"bar".to_string(),
|
||||
0,
|
||||
MoveCategory::Physical,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
MoveTarget::Adjacent,
|
||||
0,
|
||||
SecondaryEffect::empty(),
|
||||
Default::default(),
|
||||
);
|
||||
moves.add_level_move(1, &move1);
|
||||
moves.add_level_move(1, &move2);
|
||||
|
||||
let m = moves.get_learned_by_level(1u8).unwrap();
|
||||
assert_eq!(m.len(), 2);
|
||||
assert_eq!(m[0], &move1);
|
||||
assert_eq!(m[1], &move2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_two_same_moves_at_different_level() {
|
||||
let mut moves = LearnableMoves::new();
|
||||
let move1 = MoveData::new(
|
||||
"foo".to_string(),
|
||||
0,
|
||||
MoveCategory::Physical,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
MoveTarget::Adjacent,
|
||||
0,
|
||||
SecondaryEffect::empty(),
|
||||
Default::default(),
|
||||
);
|
||||
moves.add_level_move(1, &move1);
|
||||
moves.add_level_move(5, &move1);
|
||||
|
||||
let m = moves.get_learned_by_level(1u8).unwrap();
|
||||
assert_eq!(m.len(), 1);
|
||||
assert_eq!(m[0], &move1);
|
||||
let m2 = moves.get_learned_by_level(5u8).unwrap();
|
||||
assert_eq!(m2.len(), 1);
|
||||
assert_eq!(m2[0], &move1);
|
||||
let distinct = moves.get_distinct_level_moves();
|
||||
assert_eq!(distinct.len(), 1);
|
||||
assert_eq!(distinct[0], &move1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod ability_index;
|
||||
pub mod form;
|
||||
pub mod gender;
|
||||
pub mod learnable_moves;
|
||||
pub mod species;
|
|
@ -0,0 +1,62 @@
|
|||
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};
|
||||
|
||||
#[derive(Debug, Getters)]
|
||||
pub struct Species<'a> {
|
||||
id: u16,
|
||||
name: String,
|
||||
gender_rate: f32,
|
||||
growth_rate: String,
|
||||
capture_rate: u8,
|
||||
forms: HashMap<String, Form<'a>>,
|
||||
flags: HashSet<String>,
|
||||
}
|
||||
|
||||
impl<'a> Species<'a> {
|
||||
pub fn new(
|
||||
id: u16,
|
||||
name: &str,
|
||||
gender_rate: f32,
|
||||
growth_rate: &str,
|
||||
capture_rate: u8,
|
||||
default_form: Form<'a>,
|
||||
flags: HashSet<String>,
|
||||
) -> Species<'a> {
|
||||
Species {
|
||||
id,
|
||||
name: name.to_string(),
|
||||
gender_rate,
|
||||
growth_rate: growth_rate.to_string(),
|
||||
capture_rate,
|
||||
forms: hashmap! {
|
||||
"default".to_string() => default_form,
|
||||
},
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_form(&mut self, id: String, form: Form<'a>) {
|
||||
self.forms.insert(id, form);
|
||||
}
|
||||
|
||||
pub fn get_form(&self, id: &str) -> Option<&Form> {
|
||||
self.forms.get(id)
|
||||
}
|
||||
|
||||
pub fn get_random_gender(&self, rand: &mut Random) -> Gender {
|
||||
if self.gender_rate < 0.0 {
|
||||
Gender::Genderless
|
||||
} else if rand.get_float() >= self.gender_rate {
|
||||
Gender::Female
|
||||
} else {
|
||||
Gender::Male
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_flag(&self, key: &str) -> bool {
|
||||
self.flags.contains(key)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
use super::statistics::Statistic;
|
||||
use derive_getters::Getters;
|
||||
use num_traits::PrimInt;
|
||||
|
||||
#[derive(Default, Eq, PartialEq, Debug, Getters)]
|
||||
pub struct StatisticSet<T>
|
||||
where
|
||||
T: PrimInt,
|
||||
{
|
||||
hp: T,
|
||||
attack: T,
|
||||
defense: T,
|
||||
special_attack: T,
|
||||
special_defense: T,
|
||||
speed: T,
|
||||
}
|
||||
|
||||
impl<T> StatisticSet<T>
|
||||
where
|
||||
T: PrimInt,
|
||||
{
|
||||
pub 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, value: T) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increase_stat(&mut self, stat: Statistic, value: T) {
|
||||
match stat {
|
||||
Statistic::HP => self.hp = self.hp + value,
|
||||
Statistic::Attack => self.attack = self.attack + value,
|
||||
Statistic::Defense => self.defense = self.defense + value,
|
||||
Statistic::SpecialAttack => self.special_attack = self.special_attack + value,
|
||||
Statistic::SpecialDefense => self.special_defense = self.special_defense + value,
|
||||
Statistic::Speed => self.speed = self.speed + value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrease_stat(&mut self, stat: Statistic, value: T) {
|
||||
match stat {
|
||||
Statistic::HP => self.hp = self.hp - value,
|
||||
Statistic::Attack => self.attack = self.attack - value,
|
||||
Statistic::Defense => self.defense = self.defense - value,
|
||||
Statistic::SpecialAttack => self.special_attack = self.special_attack - value,
|
||||
Statistic::SpecialDefense => self.special_defense = self.special_defense - value,
|
||||
Statistic::Speed => self.speed = self.speed - value,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#[derive(Debug, PartialEq)]
|
||||
pub enum Statistic {
|
||||
HP,
|
||||
Attack,
|
||||
Defense,
|
||||
SpecialAttack,
|
||||
SpecialDefense,
|
||||
Speed,
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod random;
|
|
@ -0,0 +1,179 @@
|
|||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_pcg::Pcg32;
|
||||
|
||||
pub struct Random {
|
||||
seed: u128,
|
||||
distribution: Uniform<f64>,
|
||||
random_gen: Pcg32,
|
||||
}
|
||||
|
||||
impl Default for Random {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Random {
|
||||
pub fn new(seed: u128) -> Self {
|
||||
Random {
|
||||
seed,
|
||||
distribution: Uniform::from(0.0..1.0),
|
||||
random_gen: Pcg32::from_seed(seed.to_be_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seed(&self) -> u128 {
|
||||
self.seed
|
||||
}
|
||||
|
||||
pub fn get(&mut self) -> i32 {
|
||||
self.random_gen.gen()
|
||||
}
|
||||
|
||||
pub fn get_max(&mut self, max: i32) -> i32 {
|
||||
assert!(max > 0);
|
||||
Uniform::from(0..max).sample(&mut self.random_gen)
|
||||
}
|
||||
|
||||
pub fn get_between(&mut self, min: i32, max: i32) -> i32 {
|
||||
assert!(max > min);
|
||||
Uniform::from(min..max).sample(&mut self.random_gen)
|
||||
}
|
||||
|
||||
pub fn get_unsigned(&mut self) -> u32 {
|
||||
self.random_gen.gen()
|
||||
}
|
||||
|
||||
pub fn get_max_unsigned(&mut self, max: u32) -> u32 {
|
||||
assert!(max > 0);
|
||||
Uniform::from(0..max).sample(&mut self.random_gen)
|
||||
}
|
||||
|
||||
pub fn get_between_unsigned(&mut self, min: u32, max: u32) -> u32 {
|
||||
assert!(max > min);
|
||||
Uniform::from(min..max).sample(&mut self.random_gen)
|
||||
}
|
||||
|
||||
pub fn get_float(&mut self) -> f32 {
|
||||
self.get_double() as f32
|
||||
}
|
||||
pub fn get_double(&mut self) -> f64 {
|
||||
self.distribution.sample(&mut self.random_gen)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::random::Random;
|
||||
|
||||
#[test]
|
||||
fn create_random() {
|
||||
let _default = Random::default();
|
||||
let _empty = Random::new(100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_random_with_seed() {
|
||||
let mut v = Random::new(10);
|
||||
assert_eq!(v.get(), 1755576946);
|
||||
assert_eq!(v.get(), 1254514019);
|
||||
assert_eq!(v.get(), 1735834837);
|
||||
assert_eq!(v.get(), 51079449);
|
||||
assert_eq!(v.get(), 506997516);
|
||||
assert_eq!(v.get(), -173527621);
|
||||
assert_eq!(v.get(), 683138464);
|
||||
assert_eq!(v.get(), 580236580);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_random_with_limit_with_seed() {
|
||||
let mut v = Random::new(10);
|
||||
assert_eq!(v.get_max(10), 4);
|
||||
assert_eq!(v.get_max(10), 2);
|
||||
assert_eq!(v.get_max(10), 4);
|
||||
assert_eq!(v.get_max(10), 0);
|
||||
assert_eq!(v.get_max(10), 1);
|
||||
assert_eq!(v.get_max(10), 9);
|
||||
assert_eq!(v.get_max(10), 1);
|
||||
assert_eq!(v.get_max(10), 1);
|
||||
|
||||
assert_eq!(v.get_max(2), 0);
|
||||
assert_eq!(v.get_max(2), 1);
|
||||
assert_eq!(v.get_max(2), 1);
|
||||
assert_eq!(v.get_max(2), 1);
|
||||
assert_eq!(v.get_max(2), 0);
|
||||
assert_eq!(v.get_max(2), 1);
|
||||
assert_eq!(v.get_max(2), 0);
|
||||
assert_eq!(v.get_max(2), 0);
|
||||
assert_eq!(v.get_max(2), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_random_with_limit_with_range() {
|
||||
let mut v = Random::new(10);
|
||||
assert_eq!(v.get_between(10, 30), 18);
|
||||
assert_eq!(v.get_between(10, 30), 15);
|
||||
assert_eq!(v.get_between(10, 30), 18);
|
||||
assert_eq!(v.get_between(10, 30), 10);
|
||||
assert_eq!(v.get_between(10, 30), 12);
|
||||
assert_eq!(v.get_between(10, 30), 29);
|
||||
assert_eq!(v.get_between(10, 30), 13);
|
||||
assert_eq!(v.get_between(10, 30), 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_random_distribution() {
|
||||
let mut v = Random::new(10);
|
||||
const AMOUNT: usize = 100_000;
|
||||
|
||||
let mut arr: [i32; AMOUNT] = [0; AMOUNT];
|
||||
for i in 0..AMOUNT {
|
||||
arr[i] = v.get_between(0, 2);
|
||||
}
|
||||
|
||||
let mut num_zeros = 0;
|
||||
let mut num_ones = 0;
|
||||
for v in arr.iter() {
|
||||
if *v == 0 {
|
||||
num_zeros += 1;
|
||||
} else if *v == 1 {
|
||||
num_ones += 1;
|
||||
}
|
||||
}
|
||||
let div = num_zeros as f32 / num_ones as f32;
|
||||
assert_approx_eq::assert_approx_eq!(div, 1.0, 0.01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_random_distribution_0_to_3() {
|
||||
let mut v = Random::new(10);
|
||||
const AMOUNT: usize = 100_000;
|
||||
|
||||
let mut arr: [i32; AMOUNT] = [0; AMOUNT];
|
||||
for i in 0..AMOUNT {
|
||||
arr[i] = v.get_between(0, 3);
|
||||
}
|
||||
|
||||
let mut num_zeros = 0;
|
||||
let mut num_ones = 0;
|
||||
let mut num_twos = 0;
|
||||
for v in arr.iter() {
|
||||
if *v == 0 {
|
||||
num_zeros += 1;
|
||||
} else if *v == 1 {
|
||||
num_ones += 1;
|
||||
} else if *v == 2 {
|
||||
num_twos += 1;
|
||||
}
|
||||
}
|
||||
assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_ones as f32, 1.0, 0.01);
|
||||
assert_approx_eq::assert_approx_eq!(num_zeros as f32 / num_twos as f32, 1.0, 0.01);
|
||||
assert_approx_eq::assert_approx_eq!(num_ones as f32 / num_twos as f32, 1.0, 0.01);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue