tiger_lib/ck3/data/
title_history.rs1use std::path::PathBuf;
2
3use crate::block::Block;
4use crate::ck3::data::titles::Tier;
5use crate::date::Date;
6use crate::everything::Everything;
7use crate::fileset::{FileEntry, FileHandler};
8use crate::helpers::TigerHashMap;
9use crate::item::Item;
10use crate::parse::ParserMemory;
11use crate::pdxfile::PdxFile;
12use crate::report::{ErrorKey, err, warn};
13use crate::scopes::Scopes;
14use crate::token::Token;
15use crate::tooltipped::Tooltipped;
16use crate::validator::Validator;
17use crate::variables::Variables;
18
19#[derive(Clone, Debug, Default)]
20pub struct TitleHistories {
21 histories: TigerHashMap<&'static str, TitleHistory>,
22}
23
24impl TitleHistories {
25 pub fn load_item(&mut self, key: Token, mut block: Block) {
26 if let Some(other) = self.histories.get_mut(key.as_str()) {
27 if other.key.loc.kind >= key.loc.kind {
29 warn(ErrorKey::DuplicateItem)
30 .msg("title has two definition blocks, they will be added together")
31 .loc(&other.key)
32 .loc_msg(key, "the other one is here")
33 .push();
34 }
35 other.block.append(&mut block);
36 } else {
37 self.histories.insert(key.as_str(), TitleHistory::new(key.clone(), block));
38 }
39 }
40
41 pub fn scan_variables(&self, registry: &mut Variables) {
42 for item in self.histories.values() {
43 registry.scan(&item.block);
44 }
45 }
46
47 pub fn exists(&self, key: &str) -> bool {
48 self.histories.contains_key(key)
49 }
50
51 pub fn iter_keys(&self) -> impl Iterator<Item = &Token> {
52 self.histories.values().map(|item| &item.key)
53 }
54
55 pub fn validate(&self, data: &Everything) {
56 for item in self.histories.values() {
57 item.validate(data);
58 }
59 }
60
61 pub fn verify_has_holder(&self, key: &Token, date: Date, data: &Everything, overlord: &str) {
62 if let Some(item) = self.histories.get(key.as_str()) {
63 item.verify_has_holder(key, date, data, overlord);
64 } else {
65 let msg = format!("{key} has no title history");
66 err(ErrorKey::MissingItem).msg(msg).loc(key).push();
67 }
68 }
69}
70
71impl FileHandler<Block> for TitleHistories {
72 fn subpath(&self) -> PathBuf {
73 PathBuf::from("history/titles")
74 }
75
76 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
77 if !entry.filename().to_string_lossy().ends_with(".txt") {
78 return None;
79 }
80
81 PdxFile::read_detect_encoding(entry, parser)
82 }
83
84 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
85 for (key, block) in block.drain_definitions_warn() {
86 if Tier::try_from(&key).is_ok() {
87 self.load_item(key, block);
88 } else {
89 warn(ErrorKey::Validation).msg("expected title").loc(key).push();
90 }
91 }
92 }
93}
94
95#[derive(Clone, Debug)]
96pub struct TitleHistory {
97 key: Token,
98 block: Block,
99 tier: Tier,
100}
101
102impl TitleHistory {
103 pub fn new(key: Token, block: Block) -> Self {
104 let tier = Tier::try_from(&key).unwrap(); Self { key, block, tier }
106 }
107
108 pub fn verify_has_holder(&self, token: &Token, date: Date, data: &Everything, overlord: &str) {
109 let info = format!("setting the {overlord} will not have effect here");
110
111 if let Some(holder) = self.block.get_field_at_date("holder", date) {
112 if let Some(holder) = holder.get_value() {
114 if holder.is("0") {
115 let msg = format!("{token} has no holder at {date}");
116 err(ErrorKey::History).msg(msg).info(info).loc(token).push();
117 } else if !data.characters.is_alive(holder, date) {
118 let msg = format!("holder of {token} is not alive at {date}");
119 err(ErrorKey::History).msg(msg).info(info).loc(token).push();
120 }
121 }
122 } else {
123 let msg = format!("{token} has no holder at {date}");
124 err(ErrorKey::History).msg(msg).info(info).loc(token).push();
125 }
126 }
127
128 pub fn validate_history(&self, date: Date, block: &Block, data: &Everything) {
129 let mut vd = Validator::new(block, data);
130 vd.field_numeric("change_development_level");
131 if let Some(token) = vd.field_value("holder") {
132 if !token.is("0") {
133 data.verify_exists(Item::Character, token);
134 if data.item_exists(Item::Character, token.as_str()) {
135 data.characters.verify_alive(token, date);
136 }
137 }
138 }
139 if let Some(token) = vd.field_value("holder_ignore_head_of_faith_requirement") {
140 if !token.is("0") {
141 data.verify_exists(Item::Character, token);
142 if data.item_exists(Item::Character, token.as_str()) {
143 data.characters.verify_alive(token, date);
144 }
145 }
146 }
147
148 if let Some(token) = vd.field_value("liege") {
149 if !token.is("0") {
150 data.verify_exists(Item::Title, token);
151 if let Some(title) = data.titles.get(token.as_str()) {
152 if title.tier <= self.tier {
153 let msg = format!("liege must be higher tier than {}", self.key);
154 err(ErrorKey::TitleTier).msg(msg).loc(token).push();
155 }
156 data.title_history.verify_has_holder(token, date, data, "liege");
157 }
158 }
159 }
160
161 if let Some(token) = vd.field_value("de_jure_liege") {
162 if !token.is("0") {
163 data.verify_exists(Item::Title, token);
164 if let Some(title) = data.titles.get(token.as_str()) {
165 if title.tier <= self.tier {
166 let msg = format!("liege must be higher tier than {}", self.key);
167 err(ErrorKey::TitleTier).msg(msg).loc(token).push();
168 }
169 }
170 }
171 }
172
173 vd.field_validated_block("tributary_of", |block, data| {
174 let mut vd = Validator::new(block, data);
175 vd.field_validated_value("suzerain", |_, mut vvd| {
176 vvd.item(Item::Title);
177 data.title_history.verify_has_holder(vvd.value(), date, data, "suzerain");
178 });
179 vd.field_item("contract_group", Item::SubjectContractGroup);
180 });
181
182 vd.field_item("government", Item::GovernmentType);
183
184 vd.field_block("succession_laws"); vd.field_bool("remove_succession_laws");
186
187 vd.field_item("name", Item::Localization);
188 vd.field_bool("reset_name");
189
190 vd.field_item("insert_title_history", Item::TitleHistory);
191
192 vd.field_effect_rooted("effect", Tooltipped::No, Scopes::LandedTitle);
193 }
194
195 pub fn validate(&self, data: &Everything) {
196 data.verify_exists(Item::Title, &self.key);
197
198 let mut vd = Validator::new(&self.block, data);
199 vd.validate_history_blocks(|date, _key, block, data| {
200 self.validate_history(date, block, data);
201 });
202 }
203}