summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 44c162c5800debe162e7367af07071294e3be8c2 (plain)
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use dmn::dice;
use dmn::random_tables::RANDOM_TABLES;
use dmn::rules::ability_scores::AbilityScore;
use dmn::rules::classes::CLASSES;
use dmn::rules::npcs::Npc;
use dmn::rules::races::RACES;
use env_logger;

mod cli;

fn main() {
    env_logger::init();

    let matches = cli::cli().get_matches();

    match matches.subcommand() {
        Some(("random", sub_matches)) => {
            let random_command = sub_matches.subcommand().unwrap();
            match random_command {
                ("npc", npc_matches) => {
                    let class_name = npc_matches.get_one::<String>("class").unwrap_or_else(|| {
                        eprintln!("Error: Class is required.");
                        std::process::exit(1);
                    });
                    let class_ref = CLASSES.get(&*class_name.to_lowercase()).unwrap_or_else(|| {
                        eprintln!("Class '{}' not found.", &*class_name);
                        std::process::exit(1);
                    });

                    let mut npc = Npc::new(
                        Some(RANDOM_TABLES.roll_table("npc_alignment")),
                        None,
                        Some(class_ref),
                        None,
                        None,
                        Vec::new(),
                    );

                    npc.add_random_magic_items();

                    println!(
                        "{} {}. {}",
                        npc.alignment.unwrap(),
                        npc.class.unwrap().name,
                        npc.magic_items
                            .iter()
                            .map(|item| item.name.clone())
                            .collect::<Vec<String>>()
                            .join(", "),
                    );
                }
                ("henchman", henchman_matches) => {
                    let class_name = RANDOM_TABLES.roll_table("henchman_class").to_string();
                    let race_name = RANDOM_TABLES.roll_table("henchman_race").to_string();
                    // HACK: Need a proper way to do lookups, shouldn't rely on
                    // downcasing the class name. This whole situation is really
                    // indicative of the need for an architectural improvement.
                    // Need to think about roll_table()'s return type,
                    // and how we want to get table results in general.
                    let class_ref = CLASSES.get(&*class_name.to_lowercase()).unwrap_or_else(|| {
                        eprintln!("Class '{}' not found.", &*class_name);
                        std::process::exit(1);
                    });
                    let race_ref = RACES.get(&*race_name.to_lowercase()).unwrap_or_else(|| {
                        eprintln!("Race '{}' not found.", &*class_name);
                        std::process::exit(1);
                    });

                    let mut npc = Npc::new(
                        Some(RANDOM_TABLES.roll_table("npc_alignment")),
                        Some(race_ref),
                        Some(class_ref),
                        None,
                        None,
                        Vec::new(),
                    );

                    npc.roll_henchman_ability_scores();
                    npc.randomize_persona();

                    let ability_scores = npc.ability_scores.unwrap();

                    let output_csv = henchman_matches.get_flag("csv");

                    // TODO: DRY. Can't store template in a variable since println! needs the
                    //  string literal at compile time.
                    if output_csv {
                        println!(
                            "{},{},{},{},{},{},{},{},{},\"{}\"",
                            npc.alignment
                                .unwrap()
                                .split_whitespace()
                                .map(|word| word.chars().next().unwrap())
                                .collect::<String>(),
                            npc.race.unwrap().name,
                            npc.class.unwrap().name,
                            ability_scores.get_score(AbilityScore::Strength).unwrap(),
                            ability_scores
                                .get_score(AbilityScore::Intelligence)
                                .unwrap(),
                            ability_scores.get_score(AbilityScore::Wisdom).unwrap(),
                            ability_scores
                                .get_score(AbilityScore::Constitution)
                                .unwrap(),
                            ability_scores.get_score(AbilityScore::Dexterity).unwrap(),
                            ability_scores.get_score(AbilityScore::Charisma).unwrap(),
                            npc.persona.unwrap(),
                        );
                    } else {
                        println!(
                            "{} {} {}. STR {}, INT {}, WIS {}, CON {}, DEX {}, CHA {}. {}",
                            npc.alignment.unwrap(),
                            npc.race.unwrap().name,
                            npc.class.unwrap().name,
                            ability_scores.get_score(AbilityScore::Strength).unwrap(),
                            ability_scores
                                .get_score(AbilityScore::Intelligence)
                                .unwrap(),
                            ability_scores.get_score(AbilityScore::Wisdom).unwrap(),
                            ability_scores
                                .get_score(AbilityScore::Constitution)
                                .unwrap(),
                            ability_scores.get_score(AbilityScore::Dexterity).unwrap(),
                            ability_scores.get_score(AbilityScore::Charisma).unwrap(),
                            npc.persona.unwrap(),
                        );
                    };
                }
                ("magic", _) => {
                    let magic = RANDOM_TABLES.roll_table("ua_magic");
                    println!("{}", magic);
                }
                _ => unreachable!(),
            }
        }
        Some(("roll", sub_matches)) => {
            let formula = sub_matches.get_one::<String>("FORMULA").expect("required");
            match dice::roll_formula(formula) {
                Some(roll_result) => println!("Rolled: {}", roll_result),
                None => eprintln!("Error: Invalid roll formula or calculation failed."),
            }
        }
        Some(("table", sub_matches)) => {
            let table_name = sub_matches.get_one::<String>("TABLE").expect("required");
            let output_text = RANDOM_TABLES.roll_table(table_name);
            println!("{}", output_text)
        }
        _ => unreachable!(),
    }
}