1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
use crate::dice::roll_formula;
use crate::rules::ability_scores::{AbilityScore, AbilityScoreCollection};
use crate::rules::classes::Class;
use std::collections::HashMap;
// use std::fmt;
pub struct Npc {
pub alignment: Option<String>,
pub race: Option<String>,
pub class: Option<&'static Class>,
pub ability_scores: Option<AbilityScoreCollection>,
}
impl Npc {
pub fn new(
alignment: Option<String>,
race: Option<String>,
class: Option<&'static Class>,
ability_scores: Option<AbilityScoreCollection>,
) -> Self {
Npc {
alignment,
race,
class,
ability_scores,
}
}
pub fn roll_henchman_ability_scores(&mut self) {
rand::thread_rng();
let mut ability_score_rolls: HashMap<AbilityScore, u32> = HashMap::new();
for &ability in &[
AbilityScore::Strength,
AbilityScore::Intelligence,
AbilityScore::Wisdom,
AbilityScore::Dexterity,
AbilityScore::Constitution,
AbilityScore::Charisma,
] {
// Roll 3d6 down the line.
let mut roll_result = roll_formula("3d6").unwrap();
let class_ref = self.class.unwrap();
// For ability scores which are prime requisites of the class,
// increase all dice by 1 which do not already show a 6.
if class_ref.prime_requisites.contains(&ability) {
roll_result.increase_sides_below_max(6, 1);
}
// At this point we don't need the individual dice anymore.
let mut total = roll_result.total();
// Add NPC-specific class ability score modifiers.
total += class_ref
.npc_ability_score_modifiers
.get(&ability)
.copied()
.unwrap_or(0) as u32;
ability_score_rolls.insert(ability, total);
}
// Create an AbilityScoreCollection for the Npc, using the above results.
let mut score_collection = AbilityScoreCollection::new();
for (ability, value) in &ability_score_rolls {
score_collection.add_score(*ability, *value);
}
self.ability_scores = Some(score_collection);
// TODO: Modify results for race.
// TODO: Apply racial minimums and maximums.
// TODO: Verify legality of class based on alignment.
// TODO: Verify legality of class based on race.
// TODO: Verify legality of class based on ability scores.
}
}
// impl fmt::Display for Npc {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// let values: Vec<&str> = vec![
// self.alignment.as_deref().unwrap_or(""),
// self.race.as_deref().unwrap_or(""),
// self.class.as_ref().map_or("", |class| &class.name),
// ]
// .into_iter()
// .filter(|&s| !s.is_empty())
// .collect();
//
// let formatted_string = values.join(" ");
// write!(f, "{}", formatted_string)
// }
// }
#[cfg(test)]
mod tests {
use crate::rules::classes::CLASSES;
use super::*;
#[test]
#[ignore]
#[should_panic(expected = "Ability score generation isn't testable yet.")]
fn test_roll_henchman_ability_scores() {
let class_ref = CLASSES.get("fighter").unwrap();
let mut npc = Npc {
alignment: Some(String::from("Lawful Good")),
race: Some(String::from("Dwarf")),
class: Some(class_ref),
ability_scores: None,
};
// Roll ability scores for the Npc.
npc.roll_henchman_ability_scores();
// TODO: Need to actually test this process.
// Check if ability scores are modified correctly based on class requirements and modifiers.
// let ability_scores = npc.ability_scores.unwrap();
}
}
|