ck3_history_extractor/structures/
title.rs

1use std::{
2    ops::{Deref, DerefMut},
3    path::Path,
4    slice::Iter,
5    str::FromStr,
6};
7
8use jomini::common::{Date, PdsDate};
9use serde::Serialize;
10
11use super::{
12    super::{
13        display::{Grapher, ProceduralPath, Renderable, TreeNode},
14        game_data::{GameData, Localizable, LocalizationError, Localize, MapGenerator, MapImage},
15        jinja_env::TITLE_TEMPLATE_NAME,
16        parser::{
17            GameObjectMap, GameObjectMapping, GameState, ParsingError, SaveFileObject,
18            SaveFileValue,
19        },
20        types::{GameString, Wrapper, WrapperMut},
21    },
22    Character, Culture, EntityRef, Faith, FromGameObject, GameObjectDerived, GameObjectEntity,
23    GameRef,
24};
25
26#[derive(Serialize)]
27pub struct TitleData {
28    key: GameString,
29    name: GameString,
30    de_jure: Option<GameRef<Title>>,
31    de_facto: Option<GameRef<Title>>,
32    de_jure_vassals: Vec<GameRef<Title>>,
33    de_facto_vassals: Vec<GameRef<Title>>,
34    history: Vec<(Date, Option<GameRef<Character>>, GameString)>,
35    claims: Vec<GameRef<Character>>,
36    capital: Option<GameRef<Title>>,
37    color: [u8; 3],
38}
39
40impl TitleData {
41    fn new(
42        key: GameString,
43        base: &GameObjectMap,
44        game_state: &mut GameState,
45    ) -> Result<Self, ParsingError> {
46        let mut title = Self {
47            key: key,
48            name: base.get_string("name")?,
49            color: base
50                .get("color")
51                .map(|v| v.as_object().and_then(|obj| obj.as_array()))
52                .transpose()?
53                .map_or([70, 255, 70], |color_obj| {
54                    [
55                        color_obj[0].as_integer().unwrap() as u8,
56                        color_obj[1].as_integer().unwrap() as u8,
57                        color_obj[2].as_integer().unwrap() as u8,
58                    ]
59                }),
60            de_jure: base
61                .get("de_jure_liege")
62                .map(|liege| {
63                    liege
64                        .as_id()
65                        .and_then(|liege_id| Ok(game_state.get_title(&liege_id)))
66                })
67                .transpose()?,
68            de_facto: base
69                .get("de_facto_liege")
70                .map(|liege| {
71                    liege
72                        .as_id()
73                        .and_then(|liege_id| Ok(game_state.get_title(&liege_id).clone()))
74                })
75                .transpose()?,
76            de_jure_vassals: Vec::default(),
77            de_facto_vassals: Vec::default(),
78            history: Vec::default(),
79            claims: Vec::default(),
80            capital: base
81                .get("capital")
82                .map(|capital| capital.as_id().and_then(|id| Ok(game_state.get_title(&id))))
83                .transpose()?,
84        };
85        if let Some(claims) = base.get("claim") {
86            if let SaveFileValue::Object(claims) = claims {
87                for claim in claims.as_array()? {
88                    title
89                        .claims
90                        .push(game_state.get_character(&claim.as_id()?).clone());
91                }
92            } else {
93                title
94                    .claims
95                    .push(game_state.get_character(&claims.as_id()?).clone());
96            }
97        }
98
99        if let Some(hist) = base.get("history") {
100            for (h, val) in hist.as_object()?.as_map()? {
101                let character;
102                let action: GameString;
103                if let SaveFileValue::Object(o) = val {
104                    match o {
105                        SaveFileObject::Array(arr) => {
106                            for entry in arr {
107                                let loc_action;
108                                let loc_character;
109                                if let SaveFileValue::Object(o) = entry {
110                                    let o = o.as_map()?;
111                                    loc_action = o.get_string("type")?;
112                                    if let Some(holder) = o.get("holder") {
113                                        loc_character = Some(
114                                            game_state.get_character(&holder.as_id()?).clone(),
115                                        );
116                                    } else {
117                                        loc_character = None;
118                                    }
119                                } else {
120                                    loc_action = GameString::from("Inherited");
121                                    loc_character =
122                                        Some(game_state.get_character(&entry.as_id()?).clone());
123                                }
124                                title
125                                    .history
126                                    .push((Date::from_str(h)?, loc_character, loc_action))
127                            }
128                            continue; //if it's an array we handled all the adding already in the loop above
129                        }
130                        SaveFileObject::Map(o) => {
131                            action = o.get_string("type")?;
132                            match o.get("holder") {
133                                Some(h) => {
134                                    character = Some(game_state.get_character(&h.as_id()?).clone());
135                                }
136                                None => {
137                                    character = None;
138                                }
139                            }
140                        }
141                    }
142                } else {
143                    action = GameString::from("Inherited");
144                    character = Some(game_state.get_character(&val.as_id()?).clone());
145                }
146                title.history.push((Date::from_str(h)?, character, action));
147            }
148        }
149        //sort history by the first element of the tuple (the date) in descending order
150        title.history.sort_by(|a, b| a.0.cmp(&b.0));
151        Ok(title)
152    }
153}
154
155#[derive(Serialize)]
156#[serde(tag = "tier")]
157pub enum Title {
158    Empire(TitleData),
159    Kingdom(TitleData),
160    Duchy(TitleData),
161    County {
162        #[serde(flatten)]
163        data: TitleData,
164        culture: Option<GameRef<Culture>>,
165        faith: Option<GameRef<Faith>>,
166    },
167    Barony(TitleData),
168    Other(TitleData),
169}
170
171impl Deref for Title {
172    type Target = TitleData;
173
174    fn deref(&self) -> &Self::Target {
175        match self {
176            Title::Empire(data) => data,
177            Title::Kingdom(data) => data,
178            Title::Duchy(data) => data,
179            Title::County { data, .. } => data,
180            Title::Barony(data) => data,
181            Title::Other(data) => data,
182        }
183    }
184}
185
186impl DerefMut for Title {
187    fn deref_mut(&mut self) -> &mut Self::Target {
188        match self {
189            Title::Empire(data) => data,
190            Title::Kingdom(data) => data,
191            Title::Duchy(data) => data,
192            Title::County { data, .. } => data,
193            Title::Barony(data) => data,
194            Title::Other(data) => data,
195        }
196    }
197}
198
199impl Title {
200    /// Adds a de jure vassal to the title
201    pub fn add_jure_vassal(&mut self, vassal: GameRef<Title>) {
202        self.de_jure_vassals.push(vassal);
203    }
204
205    /// Adds a de facto vassal to the title
206    pub fn add_facto_vassal(&mut self, vassal: GameRef<Title>) {
207        self.de_facto_vassals.push(vassal);
208    }
209
210    /// Recursively gets all the de facto barony keys of the title
211    pub fn get_barony_keys(&self) -> Vec<GameString> {
212        if let Title::Barony(_) = self {
213            return vec![self.key.clone()];
214        } else {
215            let mut provinces = Vec::new();
216            for v in &self.de_facto_vassals {
217                if let Some(v) = v.get_internal().inner() {
218                    provinces.append(&mut v.get_barony_keys());
219                }
220            }
221            return provinces;
222        }
223    }
224
225    pub fn get_de_jure_barony_keys(&self) -> Vec<GameString> {
226        if let Title::Barony(_) = self {
227            return vec![self.key.clone()];
228        } else {
229            let mut provinces = Vec::new();
230            for v in &self.de_facto_vassals {
231                if let Some(v) = v.get_internal().inner() {
232                    provinces.append(&mut v.get_de_jure_barony_keys());
233                }
234            }
235            return provinces;
236        }
237    }
238
239    /// Returns the key of the title
240    pub fn get_key(&self) -> GameString {
241        self.key.clone()
242    }
243
244    /// Returns an iterator over the history of the title
245    pub fn get_history_iter(&self) -> Iter<(Date, Option<GameRef<Character>>, GameString)> {
246        self.history.iter()
247    }
248
249    /// Returns the capital of the title
250    pub fn get_capital(&self) -> Option<GameRef<Title>> {
251        self.capital.clone()
252    }
253
254    /// Returns the holder of the title
255    pub fn get_holder(&self) -> Option<GameRef<Character>> {
256        if let Some(entry) = self.history.last() {
257            return entry.1.clone();
258        }
259        None
260    }
261
262    /// Returns the type of the title
263    pub fn get_type(&self) -> Option<&'static str> {
264        match self {
265            Title::Empire(_) => Some("Empire"),
266            Title::Kingdom(_) => Some("Kingdom"),
267            Title::Duchy(_) => Some("Duchy"),
268            Title::County { .. } => Some("County"),
269            Title::Barony(_) => Some("Barony"),
270            Title::Other(_) => None,
271        }
272    }
273}
274
275impl FromGameObject for Title {
276    fn from_game_object(
277        base: &GameObjectMap,
278        game_state: &mut GameState,
279    ) -> Result<Self, ParsingError> {
280        let key = base.get_string("key")?;
281        let inner = TitleData::new(key.clone(), base, game_state)?;
282        Ok(match key.as_ref().chars().next().unwrap() {
283            'e' => Self::Empire(inner),
284            'k' => Self::Kingdom(inner),
285            'd' => Self::Duchy(inner),
286            'c' => Self::County {
287                data: inner,
288                culture: None,
289                faith: None,
290            },
291            'b' => Self::Barony(inner),
292            _ => Self::Other(inner),
293        })
294    }
295
296    fn finalize(&mut self, reference: &GameRef<Title>) {
297        if let Some(de_jure) = &self.de_jure {
298            if let Some(de_jure) = de_jure.get_internal_mut().inner_mut() {
299                de_jure.add_jure_vassal(reference.clone());
300            }
301        }
302        if let Some(de_facto) = &self.de_facto {
303            if let Some(de_facto) = de_facto.get_internal_mut().inner_mut() {
304                de_facto.add_facto_vassal(reference.clone());
305            }
306        }
307    }
308}
309
310impl GameObjectDerived for Title {
311    fn get_name(&self) -> GameString {
312        self.name.clone()
313    }
314
315    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
316        if let Some(de_jure) = &self.de_jure {
317            collection.extend([E::from(de_jure.clone().into())]);
318        }
319        if let Some(de_facto) = &self.de_facto {
320            collection.extend([E::from(de_facto.clone().into())]);
321        }
322        for v in &self.de_jure_vassals {
323            collection.extend([E::from(v.clone().into())]);
324        }
325        for v in &self.de_facto_vassals {
326            collection.extend([E::from(v.clone().into())]);
327        }
328        for c in &self.claims {
329            collection.extend([E::from(c.clone().into())]);
330        }
331        if let Some(capital) = &self.capital {
332            collection.extend([E::from(capital.clone().into())]);
333        }
334    }
335}
336
337impl TreeNode<Vec<GameRef<Title>>> for Title {
338    fn get_children(&self) -> Option<Vec<GameRef<Title>>> {
339        if self.de_jure_vassals.is_empty() {
340            return None;
341        }
342        Some(self.de_jure_vassals.clone())
343    }
344
345    fn get_class(&self) -> Option<GameString> {
346        if let Some(tp) = self.get_type() {
347            return Some(tp.into());
348        }
349        None
350    }
351
352    fn get_parent(&self) -> Option<Vec<GameRef<Title>>> {
353        if let Some(de_jure) = &self.de_jure {
354            return Some(vec![de_jure.clone()]);
355        }
356        None
357    }
358}
359
360impl ProceduralPath for Title {
361    fn get_subdir() -> &'static str {
362        "titles"
363    }
364}
365
366impl Renderable for GameObjectEntity<Title> {
367    fn get_template() -> &'static str {
368        TITLE_TEMPLATE_NAME
369    }
370
371    fn render(&self, path: &Path, game_state: &GameState, _: Option<&Grapher>, data: &GameData) {
372        if let Some(map) = data.get_map() {
373            if let Some(title) = self.inner() {
374                if title.de_facto_vassals.len() == 0 {
375                    return;
376                }
377                let mut buf = path.join(Title::get_subdir());
378                buf.push(self.id.to_string() + ".png");
379                let mut title_map = map.create_map_flat(title.get_barony_keys(), title.color);
380                title_map.draw_text(format!(
381                    "{} at {}",
382                    title.name,
383                    game_state.get_current_date().unwrap().iso_8601()
384                ));
385                title_map.save_in_thread(&buf);
386            }
387        }
388    }
389}
390
391impl Localizable for Title {
392    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
393        if self.name == self.key {
394            self.name = localization.localize(&self.key)?;
395        }
396        //for o in self.history.iter_mut() {
397        //    o.2 = localization.localize(o.2.as_str());
398        //}
399        Ok(())
400    }
401}
402
403#[cfg(test)]
404mod tests {
405    use super::*;
406
407    #[test]
408    fn test_serialize_title_barony() {
409        let title = Title::Barony(TitleData {
410            key: "b_test".into(),
411            name: "Test".into(),
412            de_jure: None,
413            de_facto: None,
414            de_jure_vassals: Vec::new(),
415            de_facto_vassals: Vec::new(),
416            history: Vec::new(),
417            claims: Vec::new(),
418            capital: None,
419            color: [70, 255, 70],
420        });
421        let serialized = serde_json::to_string(&title).unwrap();
422        assert_eq!(
423            serialized,
424            r#"{"tier":"Barony","key":"b_test","name":"Test","de_jure":null,"de_facto":null,"de_jure_vassals":[],"de_facto_vassals":[],"history":[],"claims":[],"capital":null,"color":[70,255,70]}"#
425        );
426    }
427
428    #[test]
429    fn test_serialize_county() {
430        let title = Title::County {
431            data: TitleData {
432                key: "c_test".into(),
433                name: "Test".into(),
434                de_jure: None,
435                de_facto: None,
436                de_jure_vassals: Vec::new(),
437                de_facto_vassals: Vec::new(),
438                history: Vec::new(),
439                claims: Vec::new(),
440                capital: None,
441                color: [70, 255, 70],
442            },
443            culture: None,
444            faith: None,
445        };
446        let serialized = serde_json::to_string(&title).unwrap();
447        assert_eq!(
448            serialized,
449            r#"{"tier":"County","key":"c_test","name":"Test","de_jure":null,"de_facto":null,"de_jure_vassals":[],"de_facto_vassals":[],"history":[],"claims":[],"capital":null,"color":[70,255,70],"culture":null,"faith":null}"#
450        );
451    }
452}