Skip to main content

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            && let Some(end_date) = end_date_token.as_ref().and_then(Token::get_date)
91            && start_date > end_date
92        {
93            err(ErrorKey::Range)
94                .msg("start date is after end date")
95                .loc_msg(start_date_token.as_ref().unwrap(), "start date")
96                .loc_msg(end_date_token.as_ref().unwrap(), "end date")
97                .push();
98        }
99
100        vd.field_list_items("targeted_titles", Item::Title);
101        vd.field_item("casus_belli", Item::CasusBelli);
102        vd.field_list_items("attackers", Item::Character);
103        vd.field_list_items("defenders", Item::Character);
104        vd.field_item("claimant", Item::Character);
105
106        vd.unknown_block_fields(|key, block| {
107            if data.item_exists(Item::Character, key.as_str()) {
108                let mut vd = Validator::new(block, data);
109                vd.validate_history_blocks(|date, key, block, data| {
110                    if let Some(start_date) = start_date_token.as_ref().and_then(Token::get_date)
111                        && date < start_date
112                    {
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                    if let Some(end_date) = end_date_token.as_ref().and_then(Token::get_date)
120                        && date > end_date
121                    {
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                    let mut vd = Validator::new(block, data);
129                    vd.req_field("location");
130                    vd.field_item("location", Item::Province);
131                });
132            } else {
133                let msg = format!("character id {key} not found in history");
134                warn(ErrorKey::MissingItem).msg(msg).loc(key).push();
135            }
136        });
137    }
138}