tiger_lib/vic3/data/
history.rs

1use std::path::PathBuf;
2
3use crate::block::Block;
4use crate::context::ScopeContext;
5use crate::effect::validate_effect;
6use crate::everything::Everything;
7use crate::fileset::{FileEntry, FileHandler};
8use crate::helpers::TigerHashMap;
9use crate::parse::ParserMemory;
10use crate::pdxfile::PdxFile;
11use crate::report::{ErrorKey, err, warn};
12use crate::scopes::Scopes;
13use crate::token::Token;
14use crate::tooltipped::Tooltipped;
15use crate::variables::Variables;
16
17/// The history files in Vic3 are fairly simple. Files under `common/history/` have `keyword = { effect... }` as top-level blocks,
18/// where the effects from the same keywords are all added together. The keywords seem to be arbitrary, except for GLOBAL which
19/// is documented to go last.
20
21#[derive(Clone, Debug, Default)]
22pub struct History {
23    history: TigerHashMap<&'static str, HistoryEffect>,
24}
25
26impl History {
27    fn load_item(&mut self, key: Token, mut block: Block) {
28        if let Some(entry) = self.history.get_mut(key.as_str()) {
29            entry.block.append(&mut block);
30        } else {
31            self.history.insert(key.as_str(), HistoryEffect::new(key, block));
32        }
33    }
34
35    pub fn scan_variables(&self, registry: &mut Variables) {
36        for item in self.history.values() {
37            registry.scan(&item.block);
38        }
39    }
40
41    pub fn validate(&self, data: &Everything) {
42        for name in HISTORY_SEQUENCE {
43            if let Some(item) = self.history.get(name) {
44                item.validate(data);
45            }
46        }
47
48        // Validate the remaining ones even if we don't know about them.
49        // They may be for a newer game version.
50        for (name, item) in &self.history {
51            if HISTORY_SEQUENCE.contains(name) {
52                continue;
53            }
54            if *name == "CONSCRIPTION" {
55                let msg = "CONSCRIPTION history is not processed by the game";
56                let info = "as of 1.9.7";
57                warn(ErrorKey::Bugs).msg(msg).info(info).loc(&item.key).push();
58                continue;
59            }
60            let msg = format!("unknown history classification `{name}`");
61            err(ErrorKey::UnknownField).msg(msg).loc(&item.key).push();
62            item.validate(data);
63        }
64    }
65}
66
67impl FileHandler<Block> for History {
68    fn subpath(&self) -> PathBuf {
69        PathBuf::from("common/history/")
70    }
71
72    fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
73        if !entry.filename().to_string_lossy().ends_with(".txt") {
74            return None;
75        }
76
77        PdxFile::read(entry, parser)
78    }
79
80    fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
81        for (key, block) in block.drain_definitions_warn() {
82            self.load_item(key, block);
83        }
84    }
85}
86
87#[derive(Clone, Debug)]
88pub struct HistoryEffect {
89    key: Token,
90    block: Block,
91}
92
93impl HistoryEffect {
94    pub fn new(key: Token, block: Block) -> Self {
95        Self { key, block }
96    }
97
98    pub fn validate(&self, data: &Everything) {
99        let mut sc = ScopeContext::new(Scopes::None, &self.key);
100        validate_effect(&self.block, data, &mut sc, Tooltipped::No);
101    }
102}
103
104/// The order in which history files are processed by the game engine.
105/// It was determined by adding `debug_log` entries to the history files.
106/// LAST UPDATED VIC3 VERSION 1.9.7
107/// TODO: order was rearranged in 1.10
108const HISTORY_SEQUENCE: &[&str] = &[
109    "STATES",
110    "COUNTRIES",
111    "POPS",
112    "DIPLOMACY",
113    "POPULATION",
114    "POWER_BLOCS",
115    "INTERESTS",
116    "BUILDINGS",
117    "PRODUCTION_METHODS",
118    "MILITARY_FORMATIONS",
119    "AI",
120    "TREATIES",
121    "LOBBIES",
122    "DIPLOMATIC_PLAYS",
123    "TRADE",
124    // GLOBAL is documented to go last, but it doesn't.
125    "GLOBAL",
126    "POLITICAL_MOVEMENTS",
127    "CHARACTERS",
128    "GOVERNMENT",
129    "GOVERNMENT_SETUP",
130    "MILITARY_DEPLOYMENTS",
131    // new in 1.10
132    "CULTURES",
133];