tiger_lib/ck3/data/
wars.rs

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
use std::path::PathBuf;

use crate::block::Block;
use crate::everything::Everything;
use crate::fileset::{FileEntry, FileHandler};
use crate::item::Item;
use crate::parse::ParserMemory;
use crate::pdxfile::PdxFile;
use crate::report::{err, warn, ErrorKey};
use crate::token::Token;
use crate::validator::Validator;

#[derive(Clone, Debug, Default)]
pub struct Wars {
    wars: Vec<War>,
}

impl Wars {
    fn load_item(&mut self, key: Token, block: Block) {
        if key.is("war") {
            self.wars.push(War::new(block));
        } else {
            let msg = format!("unexpected key {key}, expected only `war`");
            err(ErrorKey::History).msg(msg).loc(key).push();
        }
    }

    pub fn validate(&self, data: &Everything) {
        for item in &self.wars {
            item.validate(data);
        }
    }
}

impl FileHandler<Block> for Wars {
    fn subpath(&self) -> PathBuf {
        PathBuf::from("history/wars")
    }

    fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
        if !entry.filename().to_string_lossy().ends_with(".txt") {
            return None;
        }

        PdxFile::read_optional_bom(entry, parser)
    }

    fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
        for (key, block) in block.drain_definitions_warn() {
            self.load_item(key, block);
        }
    }
}

#[derive(Clone, Debug)]
pub struct War {
    block: Block,
}

impl War {
    pub fn new(block: Block) -> Self {
        Self { block }
    }

    fn validate(&self, data: &Everything) {
        let mut vd = Validator::new(&self.block, data);

        // These are not actually required by the game engine,
        // but they are logically needed to define a war.
        vd.req_field("start_date");
        vd.req_field("end_date");
        vd.req_field("casus_belli");
        vd.req_field("attackers");
        vd.req_field("defenders");

        vd.field_item("name", Item::Localization);

        let mut start_date_token = None;
        let mut end_date_token = None;
        vd.field_validated_value("start_date", |_, mut vd| {
            vd.date();
            start_date_token = Some(vd.value().clone());
        });
        vd.field_validated_value("end_date", |_, mut vd| {
            vd.date();
            end_date_token = Some(vd.value().clone());
        });

        if let Some(start_date) = start_date_token.as_ref().and_then(Token::get_date) {
            if let Some(end_date) = end_date_token.as_ref().and_then(Token::get_date) {
                if start_date > end_date {
                    err(ErrorKey::Range)
                        .msg("start date is after end date")
                        .loc_msg(start_date_token.as_ref().unwrap(), "start date")
                        .loc_msg(end_date_token.as_ref().unwrap(), "end date")
                        .push();
                }
            }
        }

        vd.field_list_items("targeted_titles", Item::Title);
        vd.field_item("casus_belli", Item::CasusBelli);
        vd.field_list_items("attackers", Item::Character);
        vd.field_list_items("defenders", Item::Character);
        vd.field_item("claimant", Item::Character);

        vd.unknown_block_fields(|key, block| {
            if data.item_exists(Item::Character, key.as_str()) {
                let mut vd = Validator::new(block, data);
                vd.validate_history_blocks(|date, key, block, data| {
                    if let Some(start_date) = start_date_token.as_ref().and_then(Token::get_date) {
                        if date < start_date {
                            err(ErrorKey::Range)
                                .msg("date is before start date")
                                .loc(key)
                                .loc_msg(start_date_token.as_ref().unwrap(), "start date")
                                .push();
                        }
                    }
                    if let Some(end_date) = end_date_token.as_ref().and_then(Token::get_date) {
                        if date > end_date {
                            err(ErrorKey::Range)
                                .msg("date is after end date")
                                .loc(key)
                                .loc_msg(end_date_token.as_ref().unwrap(), "end date")
                                .push();
                        }
                    }
                    let mut vd = Validator::new(block, data);
                    vd.req_field("location");
                    vd.field_item("location", Item::Province);
                });
            } else {
                let msg = format!("character id {key} not found in history");
                warn(ErrorKey::MissingItem).msg(msg).loc(key).push();
            }
        });
    }
}