You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
4.8 KiB
Rust
155 lines
4.8 KiB
Rust
use rand::seq::IteratorRandom;
|
|
use rand_distr::{Distribution, Standard};
|
|
use strum::IntoEnumIterator;
|
|
use strum_macros::{Display, EnumIter};
|
|
|
|
use crate::{
|
|
skills::{Proficiencies, ToProficiencies},
|
|
stats::{StatBlock, ToStatBlock},
|
|
};
|
|
|
|
#[derive(Debug, PartialEq, Eq, EnumIter, Clone, Copy, Display)]
|
|
pub enum Class {
|
|
Guard,
|
|
Knight,
|
|
Brawler,
|
|
Thief,
|
|
Swashbuckler,
|
|
Survivalist,
|
|
Hunter,
|
|
Witch,
|
|
Wizard,
|
|
NPC, // Stubbed in for "no class"
|
|
}
|
|
|
|
impl ToStatBlock for Class {
|
|
fn stat_block(&self) -> StatBlock {
|
|
match self {
|
|
Class::Guard => StatBlock::from((1, 0, 1, 1, 0, 0, 0, 0)),
|
|
Class::Knight => StatBlock::from((1, 0, 0, 1, 0, 0, 0, 1)),
|
|
Class::Brawler => StatBlock::from((1, 1, 1, 0, 0, 0, 0, 0)),
|
|
// -----
|
|
Class::Thief => StatBlock::from((0, 1, 0, 0, 1, 0, 1, 0)),
|
|
Class::Swashbuckler => StatBlock::from((1, 1, 0, 0, 0, 0, 0, 1)),
|
|
// -----
|
|
Class::Survivalist => StatBlock::from((0, 1, 1, 0, 1, 0, 0, 0)),
|
|
Class::Hunter => StatBlock::from((0, 1, 1, 0, 0, 1, 0, 0)),
|
|
// -----
|
|
Class::Witch => StatBlock::from((0, 0, 0, 0, 1, 1, 1, 0)),
|
|
Class::Wizard => StatBlock::from((0, 0, 0, 0, 1, 1, 0, 1)),
|
|
// -----
|
|
Class::NPC => StatBlock::from((0, 0, 0, 0, 0, 0, 0, 0)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ToProficiencies for Class {
|
|
fn proficiencies(&self) -> Proficiencies {
|
|
match self {
|
|
Class::Guard => Proficiencies {
|
|
light_armor: true,
|
|
medium_armor: true,
|
|
heavy_armor: true,
|
|
// -----
|
|
// bladed_weapons: true,
|
|
simple_weapons: true,
|
|
pole_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Knight => Proficiencies {
|
|
light_armor: true,
|
|
medium_armor: true,
|
|
heavy_armor: true,
|
|
// -----
|
|
bladed_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Brawler => Proficiencies {
|
|
light_armor: true,
|
|
medium_armor: true,
|
|
// -----
|
|
simple_weapons: true,
|
|
blunt_weapons: true,
|
|
thrown_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Thief => Proficiencies {
|
|
simple_weapons: true,
|
|
thrown_weapons: true,
|
|
ranged_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Swashbuckler => Proficiencies {
|
|
light_armor: true,
|
|
simple_weapons: true,
|
|
bladed_weapons: true,
|
|
thrown_weapons: true,
|
|
ranged_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Survivalist => Proficiencies {
|
|
// -----
|
|
simple_weapons: true,
|
|
thrown_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Hunter => Proficiencies {
|
|
light_armor: true,
|
|
// -----
|
|
simple_weapons: true,
|
|
pole_weapons: true,
|
|
ranged_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Witch => Proficiencies {
|
|
simple_weapons: true,
|
|
..Default::default()
|
|
},
|
|
Class::Wizard => Proficiencies::default(),
|
|
Class::NPC => Proficiencies::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Distribution<Class> for Standard {
|
|
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Class {
|
|
Class::iter().choose(rng).unwrap()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod class_tests {
|
|
use strum::IntoEnumIterator;
|
|
|
|
use crate::{class::Class, stats::ToStatBlock};
|
|
|
|
#[test]
|
|
fn stat_sums() {
|
|
for class in Class::iter() {
|
|
let stat_block = class.stat_block();
|
|
if class != Class::NPC {
|
|
assert_eq!(
|
|
3,
|
|
stat_block.STR
|
|
+ stat_block.DEX
|
|
+ stat_block.CON
|
|
+ stat_block.STB
|
|
+ stat_block.INT
|
|
+ stat_block.KND
|
|
+ stat_block.CMP
|
|
+ stat_block.CHA
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn all_unique() {
|
|
for class1 in Class::iter() {
|
|
for class2 in Class::iter() {
|
|
assert!(class1 == class2 || class1.stat_block() != class2.stat_block())
|
|
}
|
|
}
|
|
}
|
|
}
|