From e63dbbde68f81f8a65b48608d41ef6bf6be799e6 Mon Sep 17 00:00:00 2001 From: David Gay Date: Wed, 31 Jan 2024 03:14:53 -0500 Subject: `random npc` accepts level argument --- src/cli.rs | 8 ++++++++ src/main.rs | 31 ++++++++++++++++++++++++++----- src/rules/npcs.rs | 13 ++++++++----- 3 files changed, 42 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/cli.rs b/src/cli.rs index bc6c3a8..0c58461 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -33,6 +33,14 @@ pub fn cli() -> Command { .short('c') .long("class") .help("The class of the NPC, e.g. fighter"), + ) + .arg( + Arg::new("level") + .short('l') + .long("level") + .help("The level of the NPC, e.g 18") + .default_value("1") + .value_parser(clap::value_parser!(i32)), ), ), ) diff --git a/src/main.rs b/src/main.rs index 44c162c..85c90fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,10 +27,19 @@ fn main() { std::process::exit(1); }); + let level = npc_matches + .get_one::("level") + .unwrap_or_else(|| { + eprintln!("Error: Couldn't parse level."); + std::process::exit(1); + }) + .clone(); + let mut npc = Npc::new( Some(RANDOM_TABLES.roll_table("npc_alignment")), None, Some(class_ref), + level, None, None, Vec::new(), @@ -39,9 +48,14 @@ fn main() { npc.add_random_magic_items(); println!( - "{} {}. {}", - npc.alignment.unwrap(), + "{} {} {}. {}", + npc.alignment + .unwrap() + .split_whitespace() + .map(|word| word.chars().next().unwrap()) + .collect::(), npc.class.unwrap().name, + npc.level, npc.magic_items .iter() .map(|item| item.name.clone()) @@ -70,6 +84,7 @@ fn main() { Some(RANDOM_TABLES.roll_table("npc_alignment")), Some(race_ref), Some(class_ref), + 1, None, None, Vec::new(), @@ -86,7 +101,7 @@ fn main() { // string literal at compile time. if output_csv { println!( - "{},{},{},{},{},{},{},{},{},\"{}\"", + "{},{},{},{},{},{},{},{},{},{},\"{}\"", npc.alignment .unwrap() .split_whitespace() @@ -94,6 +109,7 @@ fn main() { .collect::(), npc.race.unwrap().name, npc.class.unwrap().name, + npc.level, ability_scores.get_score(AbilityScore::Strength).unwrap(), ability_scores .get_score(AbilityScore::Intelligence) @@ -108,10 +124,15 @@ fn main() { ); } else { println!( - "{} {} {}. STR {}, INT {}, WIS {}, CON {}, DEX {}, CHA {}. {}", - npc.alignment.unwrap(), + "{} {} {} {}. STR {}, INT {}, WIS {}, CON {}, DEX {}, CHA {}. {}", + npc.alignment + .unwrap() + .split_whitespace() + .map(|word| word.chars().next().unwrap()) + .collect::(), npc.race.unwrap().name, npc.class.unwrap().name, + npc.level, ability_scores.get_score(AbilityScore::Strength).unwrap(), ability_scores .get_score(AbilityScore::Intelligence) diff --git a/src/rules/npcs.rs b/src/rules/npcs.rs index fe87e76..5477306 100644 --- a/src/rules/npcs.rs +++ b/src/rules/npcs.rs @@ -13,6 +13,7 @@ pub struct Npc { pub alignment: Option, pub race: Option<&'static Race>, pub class: Option<&'static Class>, + pub level: i32, pub ability_scores: Option, pub persona: Option, pub magic_items: Vec<&'static MagicItem>, @@ -23,6 +24,7 @@ impl Npc { alignment: Option, race: Option<&'static Race>, class: Option<&'static Class>, + level: i32, // TODO: Multiclass and dualclass. ability_scores: Option, persona: Option, magic_items: Vec<&'static MagicItem>, @@ -31,6 +33,7 @@ impl Npc { alignment, race, class, + level, ability_scores, persona, magic_items, @@ -120,7 +123,6 @@ impl Npc { // This uses the Appendix C method provided in the city/town section. // I prefer it to the Monster Manual method and the NPC party method // because it provides more variance. - // TODO: Support other levels than 1st. pub fn add_random_magic_items(&mut self) { let mut rng = rand::thread_rng(); let class_ref = self.class.unwrap(); @@ -139,13 +141,13 @@ impl Npc { ]; for &kind_string in kind_strings_for_chances.iter() { - if class_ref + let chance_per_level = class_ref .chances_for_magic .get(kind_string) .copied() - .unwrap_or(0) - >= rng.gen_range(1..=100) - { + .unwrap_or(0); + let chance = chance_per_level * self.level; + if chance >= rng.gen_range(1..=100) { // For now, just get a dummy item named after the item type. // Later, we'll add the actual magic items. self.magic_items.push( @@ -202,6 +204,7 @@ mod tests { alignment: Some(String::from("Lawful Good")), race: Some(race_ref), class: Some(class_ref), + level: 1, ability_scores: None, persona: None, magic_items: Vec::new(), -- cgit v1.2.3