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 = if let Some(key) = base.get("key") {
281            key.as_string()?
282        } else {
283            base.get_string("name")?
284        };
285        let inner = TitleData::new(key.clone(), base, game_state)?;
286        Ok(match key.as_ref().chars().next().unwrap() {
287            'e' => Self::Empire(inner),
288            'k' => Self::Kingdom(inner),
289            'd' => Self::Duchy(inner),
290            'c' => Self::County {
291                data: inner,
292                culture: None,
293                faith: None,
294            },
295            'b' => Self::Barony(inner),
296            _ => Self::Other(inner),
297        })
298    }
299
300    fn finalize(&mut self, reference: &GameRef<Title>) {
301        if let Some(de_jure) = &self.de_jure {
302            if let Some(de_jure) = de_jure.get_internal_mut().inner_mut() {
303                de_jure.add_jure_vassal(reference.clone());
304            }
305        }
306        if let Some(de_facto) = &self.de_facto {
307            if let Some(de_facto) = de_facto.get_internal_mut().inner_mut() {
308                de_facto.add_facto_vassal(reference.clone());
309            }
310        }
311    }
312}
313
314impl GameObjectDerived for Title {
315    fn get_name(&self) -> GameString {
316        self.name.clone()
317    }
318
319    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
320        if let Some(de_jure) = &self.de_jure {
321            collection.extend([E::from(de_jure.clone().into())]);
322        }
323        if let Some(de_facto) = &self.de_facto {
324            collection.extend([E::from(de_facto.clone().into())]);
325        }
326        for v in &self.de_jure_vassals {
327            collection.extend([E::from(v.clone().into())]);
328        }
329        for v in &self.de_facto_vassals {
330            collection.extend([E::from(v.clone().into())]);
331        }
332        for c in &self.claims {
333            collection.extend([E::from(c.clone().into())]);
334        }
335        if let Some(capital) = &self.capital {
336            collection.extend([E::from(capital.clone().into())]);
337        }
338    }
339}
340
341impl TreeNode<Vec<GameRef<Title>>> for Title {
342    fn get_children(&self) -> Option<Vec<GameRef<Title>>> {
343        if self.de_jure_vassals.is_empty() {
344            return None;
345        }
346        Some(self.de_jure_vassals.clone())
347    }
348
349    fn get_class(&self) -> Option<GameString> {
350        if let Some(tp) = self.get_type() {
351            return Some(tp.into());
352        }
353        None
354    }
355
356    fn get_parent(&self) -> Option<Vec<GameRef<Title>>> {
357        if let Some(de_jure) = &self.de_jure {
358            return Some(vec![de_jure.clone()]);
359        }
360        None
361    }
362}
363
364impl ProceduralPath for Title {
365    fn get_subdir() -> &'static str {
366        "titles"
367    }
368}
369
370impl Renderable for GameObjectEntity<Title> {
371    fn get_template() -> &'static str {
372        TITLE_TEMPLATE_NAME
373    }
374
375    fn render(&self, path: &Path, game_state: &GameState, _: Option<&Grapher>, data: &GameData) {
376        if let Some(map) = data.get_map() {
377            if let Some(title) = self.inner() {
378                if title.de_facto_vassals.len() == 0 {
379                    return;
380                }
381                let mut buf = path.join(Title::get_subdir());
382                buf.push(self.id.to_string() + ".png");
383                let mut title_map = map.create_map_flat(title.get_barony_keys(), title.color);
384                title_map.draw_text(format!(
385                    "{} at {}",
386                    title.name,
387                    game_state.get_current_date().unwrap().iso_8601()
388                ));
389                title_map.save_in_thread(&buf);
390            }
391        }
392    }
393}
394
395impl Localizable for Title {
396    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
397        if self.name == self.key {
398            self.name = localization.localize(&self.key)?;
399        }
400        //for o in self.history.iter_mut() {
401        //    o.2 = localization.localize(o.2.as_str());
402        //}
403        Ok(())
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410
411    #[test]
412    fn test_serialize_title_barony() {
413        let title = Title::Barony(TitleData {
414            key: "b_test".into(),
415            name: "Test".into(),
416            de_jure: None,
417            de_facto: None,
418            de_jure_vassals: Vec::new(),
419            de_facto_vassals: Vec::new(),
420            history: Vec::new(),
421            claims: Vec::new(),
422            capital: None,
423            color: [70, 255, 70],
424        });
425        let serialized = serde_json::to_string(&title).unwrap();
426        assert_eq!(
427            serialized,
428            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]}"#
429        );
430    }
431
432    #[test]
433    fn test_serialize_county() {
434        let title = Title::County {
435            data: TitleData {
436                key: "c_test".into(),
437                name: "Test".into(),
438                de_jure: None,
439                de_facto: None,
440                de_jure_vassals: Vec::new(),
441                de_facto_vassals: Vec::new(),
442                history: Vec::new(),
443                claims: Vec::new(),
444                capital: None,
445                color: [70, 255, 70],
446            },
447            culture: None,
448            faith: None,
449        };
450        let serialized = serde_json::to_string(&title).unwrap();
451        assert_eq!(
452            serialized,
453            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}"#
454        );
455    }
456}