tiger_lib/ck3/data/
prov_history.rs

1use std::path::PathBuf;
2
3use crate::block::{BV, Block};
4use crate::ck3::data::provinces::ProvId;
5use crate::ck3::data::titles::Titles;
6use crate::date::Date;
7use crate::everything::Everything;
8use crate::fileset::{FileEntry, FileHandler};
9use crate::helpers::{TigerHashMap, dup_error};
10use crate::item::Item;
11use crate::parse::ParserMemory;
12use crate::pdxfile::PdxFile;
13use crate::report::{ErrorKey, Severity, warn};
14use crate::scopes::Scopes;
15use crate::token::Token;
16use crate::tooltipped::Tooltipped;
17use crate::validator::Validator;
18use crate::variables::Variables;
19
20#[derive(Clone, Debug, Default)]
21pub struct ProvinceHistories {
22    provinces: TigerHashMap<ProvId, ProvinceHistory>,
23}
24
25impl ProvinceHistories {
26    fn load_item(&mut self, id: ProvId, key: Token, mut block: Block) {
27        if let Some(province) = self.provinces.get_mut(&id) {
28            // Multiple entries are valid but could easily be a mistake.
29            if province.key.loc.kind >= key.loc.kind {
30                dup_error(&key, &province.key, "province");
31            }
32            province.block.append(&mut block);
33        } else {
34            self.provinces.insert(id, ProvinceHistory::new(key, block));
35        }
36    }
37
38    pub fn scan_variables(&self, registry: &mut Variables) {
39        for item in self.provinces.values() {
40            registry.scan(&item.block);
41        }
42    }
43
44    pub fn validate(&self, data: &Everything) {
45        for (provid, item) in &self.provinces {
46            item.validate(*provid, data);
47        }
48    }
49
50    pub fn check_pod_faiths(&self, data: &Everything, titles: &Titles) {
51        for bookmark in [
52            Date::new(1230, 1, 4),
53            Date::new(1230, 1, 5),
54            Date::new(1230, 1, 6),
55            Date::new(1375, 7, 5),
56            Date::new(1510, 1, 3),
57        ] {
58            for (provid, provhist) in &self.provinces {
59                if let Some(capital) = titles.capital_of(*provid) {
60                    let religion = provhist.block.get_field_at_date("religion", bookmark);
61                    if let Some(religion) = religion.and_then(BV::get_value) {
62                        if !data.item_has_property(Item::Faith, religion.as_str(), "is_modded") {
63                            let msg = format!(
64                                "Vanilla or unknown religion in prov {} (county {}) at {}",
65                                provhist.key, capital, bookmark
66                            );
67                            warn(ErrorKey::PrincesOfDarkness).msg(msg).loc(religion).push();
68                        }
69                    } else {
70                        warn(ErrorKey::PrincesOfDarkness)
71                            .msg("no religion")
72                            .loc(&provhist.key)
73                            .push();
74                    }
75                }
76            }
77        }
78    }
79}
80
81impl FileHandler<Block> for ProvinceHistories {
82    fn subpath(&self) -> PathBuf {
83        PathBuf::from("history/provinces")
84    }
85
86    fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
87        if !entry.filename().to_string_lossy().ends_with(".txt") {
88            return None;
89        }
90
91        PdxFile::read_detect_encoding(entry, parser)
92    }
93
94    fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
95        for (key, block) in block.drain_definitions_warn() {
96            if let Ok(id) = key.as_str().parse() {
97                self.load_item(id, key, block);
98            } else {
99                let msg = "unexpected key, expected only province ids";
100                warn(ErrorKey::Validation).msg(msg).loc(key).push();
101            }
102        }
103    }
104}
105
106#[derive(Clone, Debug)]
107pub struct ProvinceHistory {
108    key: Token,
109    block: Block,
110}
111
112impl ProvinceHistory {
113    fn new(key: Token, block: Block) -> Self {
114        Self { key, block }
115    }
116
117    fn validate_common(vd: &mut Validator, data: &Everything) {
118        vd.field_item("culture", Item::Culture);
119        vd.field_item("religion", Item::Faith);
120        vd.field_item("faith", Item::Faith);
121        if let Some(token) = vd.field_value("holding") {
122            if !token.is("auto") && !token.is("none") {
123                data.verify_exists(Item::HoldingType, token);
124            }
125        }
126        vd.field_list_items("buildings", Item::Building);
127        vd.multi_field_item("special_building_slot", Item::SpecialBuilding);
128        vd.multi_field_item("special_building", Item::SpecialBuilding);
129        // TODO: check if province is duchy capital
130        // TODO: check if building is duchy capital building
131        vd.field_item("duchy_capital_building", Item::Building);
132
133        vd.field_effect_rooted("effect", Tooltipped::No, Scopes::Province);
134    }
135
136    fn validate_history(_date: Date, _key: &Token, block: &Block, data: &Everything) {
137        let mut vd = Validator::new(block, data);
138        Self::validate_common(&mut vd, data);
139    }
140
141    fn validate(&self, provid: ProvId, data: &Everything) {
142        data.provinces_ck3.verify_exists_provid(provid, &self.key, Severity::Error);
143        // TODO: verify that all county-capital provinces have a culture and religion
144        // This needs province mappings to be loaded too
145        let mut vd = Validator::new(&self.block, data);
146        Self::validate_common(&mut vd, data);
147        vd.field_value("terrain"); // TODO: this does not seem to be an Item::Terrain
148        vd.validate_history_blocks(Self::validate_history);
149    }
150}