tiger_lib/ck3/data/
wars.rs

1use std::path::PathBuf;
2
3use crate::block::Block;
4use crate::everything::Everything;
5use crate::fileset::{FileEntry, FileHandler};
6use crate::item::Item;
7use crate::parse::ParserMemory;
8use crate::pdxfile::PdxFile;
9use crate::report::{ErrorKey, err, warn};
10use crate::token::Token;
11use crate::validator::Validator;
12
13#[derive(Clone, Debug, Default)]
14pub struct Wars {
15    wars: Vec<War>,
16}
17
18impl Wars {
19    fn load_item(&mut self, key: Token, block: Block) {
20        if key.is("war") {
21            self.wars.push(War::new(block));
22        } else {
23            let msg = format!("unexpected key {key}, expected only `war`");
24            err(ErrorKey::History).msg(msg).loc(key).push();
25        }
26    }
27
28    pub fn validate(&self, data: &Everything) {
29        for item in &self.wars {
30            item.validate(data);
31        }
32    }
33}
34
35impl FileHandler<Block> for Wars {
36    fn subpath(&self) -> PathBuf {
37        PathBuf::from("history/wars")
38    }
39
40    fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
41        if !entry.filename().to_string_lossy().ends_with(".txt") {
42            return None;
43        }
44
45        PdxFile::read_optional_bom(entry, parser)
46    }
47
48    fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
49        for (key, block) in block.drain_definitions_warn() {
50            self.load_item(key, block);
51        }
52    }
53}
54
55#[derive(Clone, Debug)]
56pub struct War {
57    block: Block,
58}
59
60impl War {
61    pub fn new(block: Block) -> Self {
62        Self { block }
63    }
64
65    fn validate(&self, data: &Everything) {
66        let mut vd = Validator::new(&self.block, data);
67
68        // These are not actually required by the game engine,
69        // but they are logically needed to define a war.
70        vd.req_field("start_date");
71        vd.req_field("end_date");
72        vd.req_field("casus_belli");
73        vd.req_field("attackers");
74        vd.req_field("defenders");
75
76        vd.field_item("name", Item::Localization);
77
78        let mut start_date_token = None;
79        let mut end_date_token = None;
80        vd.field_validated_value("start_date", |_, mut vd| {
81            vd.date();
82            start_date_token = Some(vd.value().clone());
83        });
84        vd.field_validated_value("end_date", |_, mut vd| {
85            vd.date();
86            end_date_token = Some(vd.value().clone());
87        });
88
89        if let Some(start_date) = start_date_token.as_ref().and_then(Token::get_date) {
90            if let Some(end_date) = end_date_token.as_ref().and_then(Token::get_date) {
91                if start_date > end_date {
92                    err(ErrorKey::Range)
93                        .msg("start date is after end date")
94                        .loc_msg(start_date_token.as_ref().unwrap(), "start date")
95                        .loc_msg(end_date_token.as_ref().unwrap(), "end date")
96                        .push();
97                }
98            }
99        }
100
101        vd.field_list_items("targeted_titles", Item::Title);
102        vd.field_item("casus_belli", Item::CasusBelli);
103        vd.field_list_items("attackers", Item::Character);
104        vd.field_list_items("defenders", Item::Character);
105        vd.field_item("claimant", Item::Character);
106
107        vd.unknown_block_fields(|key, block| {
108            if data.item_exists(Item::Character, key.as_str()) {
109                let mut vd = Validator::new(block, data);
110                vd.validate_history_blocks(|date, key, block, data| {
111                    if let Some(start_date) = start_date_token.as_ref().and_then(Token::get_date) {
112                        if date < start_date {
113                            err(ErrorKey::Range)
114                                .msg("date is before start date")
115                                .loc(key)
116                                .loc_msg(start_date_token.as_ref().unwrap(), "start date")
117                                .push();
118                        }
119                    }
120                    if let Some(end_date) = end_date_token.as_ref().and_then(Token::get_date) {
121                        if date > end_date {
122                            err(ErrorKey::Range)
123                                .msg("date is after end date")
124                                .loc(key)
125                                .loc_msg(end_date_token.as_ref().unwrap(), "end date")
126                                .push();
127                        }
128                    }
129                    let mut vd = Validator::new(block, data);
130                    vd.req_field("location");
131                    vd.field_item("location", Item::Province);
132                });
133            } else {
134                let msg = format!("character id {key} not found in history");
135                warn(ErrorKey::MissingItem).msg(msg).loc(key).push();
136            }
137        });
138    }
139}