ck3_history_extractor/structures/
character.rs

1use std::collections::HashSet;
2
3use jomini::common::Date;
4use serde::Serialize;
5
6use super::{
7    super::{
8        display::{ProceduralPath, Renderable, TreeNode},
9        game_data::{GameData, Localizable, LocalizationError, Localize},
10        jinja_env::C_TEMPLATE_NAME,
11        parser::{GameObjectMap, GameObjectMapping, GameState, ParsingError, SaveFileValue},
12        types::{GameString, Shared, Wrapper, WrapperMut},
13    },
14    Artifact, Culture, EntityRef, Faith, FromGameObject, GameObjectDerived, GameObjectEntity,
15    GameRef, House, Memory, Title,
16};
17
18/// An enum that holds either a character or a reference to a character.
19/// Effectively either a vassal([Character]) or a vassal contract.
20/// This is done so that we can hold a reference to a vassal contract, and also manually added characters from vassals registering themselves via [Character::add_vassal].
21#[derive(Serialize, Clone)]
22#[serde(untagged)]
23enum Vassal {
24    Character(Shared<GameObjectEntity<Character>>),
25    Reference(Shared<Option<Shared<GameObjectEntity<Character>>>>),
26}
27
28// MAYBE enum for dead and alive character?
29
30/// Represents a character in the game.
31#[derive(Serialize, Clone)]
32pub struct Character {
33    name: GameString,
34    nick: Option<GameString>,
35    birth: Date,
36    dead: bool,
37    date: Option<Date>,
38    reason: Option<GameString>,
39    faith: Option<GameRef<Faith>>,
40    culture: Option<GameRef<Culture>>,
41    house: Option<GameRef<House>>,
42    skills: Vec<i8>,
43    traits: Vec<GameString>,
44    spouses: HashSet<GameRef<Character>>,
45    former: Vec<GameRef<Character>>,
46    children: Vec<GameRef<Character>>,
47    parents: Vec<GameRef<Character>>,
48    dna: Option<GameString>,
49    memories: Vec<GameRef<Memory>>,
50    titles: Vec<GameRef<Title>>,
51    gold: f32,
52    piety: f32,
53    prestige: f32,
54    dread: f32,
55    strength: f32,
56    kills: Vec<GameRef<Character>>,
57    languages: Vec<GameString>,
58    vassals: Vec<Vassal>,
59    liege: Option<GameRef<Character>>,
60    female: bool,
61    artifacts: Vec<GameRef<Artifact>>,
62}
63
64// So both faith and culture can be stored for a character in the latest leader of their house.
65// The problem with reading that now is that while Houses are already likely loaded,
66// the characters that the houses hold reference to are likely still dummy, so we can't read the faith and culture from the house leader.
67// So we will be returning None for now in case either is missing, but later during serialization read the one from house.
68
69/// Processes a currency node of the character
70fn process_currency(currency_node: Option<&SaveFileValue>) -> Result<f32, ParsingError> {
71    if let Some(o) = currency_node {
72        if let Some(currency) = o.as_object()?.as_map()?.get("accumulated") {
73            return Ok(currency.as_real()? as f32);
74        } else {
75            return Ok(0.0);
76        }
77    } else {
78        return Ok(0.0);
79    }
80}
81
82impl Character {
83    /// Gets whether the character is female
84    pub fn get_female(&self) -> bool {
85        self.female
86    }
87
88    pub fn get_faith(&self) -> Option<GameRef<Faith>> {
89        self.faith.clone()
90    }
91
92    pub fn get_culture(&self) -> Option<GameRef<Culture>> {
93        self.culture.clone()
94    }
95
96    /// Adds a character as a parent of this character
97    pub fn register_parent(&mut self, parent: GameRef<Character>) {
98        self.parents.push(parent);
99    }
100
101    /// Gets the death date string of the character
102    pub fn get_death_date(&self) -> Option<Date> {
103        self.date.clone()
104    }
105
106    /// Adds a character as a vassal of this character
107    pub fn add_vassal(&mut self, vassal: GameRef<Character>) {
108        self.vassals.push(Vassal::Character(vassal));
109    }
110
111    pub fn set_liege(&mut self, liege: GameRef<Character>) {
112        self.liege = Some(liege);
113    }
114
115    /// Gets all of the held de jure barony keys of the character and their vassals
116    pub fn get_barony_keys(&self, de_jure: bool) -> Vec<GameString> {
117        let mut provinces = Vec::new();
118        for title in self.titles.iter() {
119            if let Some(title) = title.get_internal().inner() {
120                let key = title.get_key();
121                if key.starts_with("e_") || key.starts_with("k_") {
122                    //for kingdoms and empires we don't want to add the de jure baronies
123                    continue;
124                } else {
125                    if de_jure {
126                        provinces.append(&mut title.get_de_jure_barony_keys());
127                    } else {
128                        provinces.append(&mut title.get_barony_keys());
129                    }
130                }
131            }
132        }
133        for vassal in self.vassals.iter() {
134            match vassal {
135                Vassal::Character(c) => {
136                    if let Some(c) = c.get_internal().inner() {
137                        provinces.append(&mut c.get_barony_keys(de_jure));
138                    }
139                }
140                Vassal::Reference(c) => {
141                    if let Some(c) = c.get_internal().as_ref() {
142                        if let Some(c) = c.get_internal().inner() {
143                            provinces.append(&mut c.get_barony_keys(de_jure))
144                        }
145                    }
146                }
147            }
148        }
149        return provinces;
150    }
151
152    /// Gets the descendants of the character
153    pub fn get_descendants(&self) -> Vec<GameRef<Character>> {
154        let mut res = Vec::new();
155        let mut stack: Vec<GameRef<Character>> = Vec::new();
156        for child in self.children.iter() {
157            stack.push(child.clone());
158            res.push(child.clone());
159        }
160        while let Some(c) = stack.pop() {
161            if let Some(c) = c.get_internal().inner() {
162                for child in c.children.iter() {
163                    stack.push(child.clone());
164                    res.push(child.clone());
165                }
166            }
167        }
168        return res;
169    }
170
171    /// Gets the dynasty of the character
172    pub fn get_house(&self) -> Option<GameRef<House>> {
173        if let Some(house) = &self.house {
174            return Some(house.clone());
175        } else {
176            return None;
177        }
178    }
179}
180
181impl FromGameObject for Character {
182    fn from_game_object(
183        base: &GameObjectMap,
184        game_state: &mut GameState,
185    ) -> Result<Self, ParsingError> {
186        let mut val = Self {
187            // a few simple keys
188            female: base
189                .get("female")
190                .map_or(Ok(false), |val| val.as_boolean())?,
191            name: base.get_string("first_name")?,
192            birth: base.get_date("birth")?,
193            // non mandatory, yet simple keys
194            nick: base
195                .get("nickname_text")
196                .or(base.get("nickname"))
197                .map(|x| x.as_string())
198                .transpose()?,
199            faith: base
200                .get("faith")
201                .map(|x| x.as_id().and_then(|id| Ok(game_state.get_faith(&id))))
202                .transpose()?,
203            culture: base
204                .get("culture")
205                .map(|x| x.as_id().and_then(|id| Ok(game_state.get_culture(&id))))
206                .transpose()?,
207            dna: base.get("dna").map(|x| x.as_string()).transpose()?,
208            traits: Vec::new(),
209            skills: Vec::new(),
210            // keys grouped together in sections, we resolve these later
211            dead: false,
212            date: None,
213            reason: None,
214            house: None,
215            spouses: HashSet::new(),
216            former: Vec::new(),
217            children: Vec::new(),
218            parents: Vec::new(),
219            memories: Vec::new(),
220            titles: Vec::new(),
221            gold: 0.0,
222            piety: 0.0,
223            prestige: 0.0,
224            dread: 0.0,
225            strength: 0.0,
226            kills: Vec::new(),
227            languages: Vec::new(),
228            vassals: Vec::new(),
229            liege: None,
230            artifacts: Vec::new(),
231        };
232        for s in base.get_object("skill")?.as_array()?.into_iter() {
233            val.skills.push(s.as_integer()? as i8);
234        }
235        if let Some(traits_node) = base.get("traits") {
236            for t in traits_node.as_object()?.as_array()? {
237                let integer = t.as_integer()?;
238                if integer >= 0 {
239                    // WTF? a negative index? ok schizo save file go back to bed
240                    val.traits.push(game_state.get_trait(integer as u16));
241                }
242            }
243        }
244        if let Some(dynasty_id) = base.get("dynasty_house") {
245            val.house = Some(game_state.get_house(&dynasty_id.as_id()?));
246        }
247        if let Some(landed_data_node) = base.get("landed_data") {
248            let landed_data = landed_data_node.as_object()?.as_map()?;
249            if let Some(dread_node) = landed_data.get("dread") {
250                val.dread = dread_node.as_real()? as f32;
251            }
252            if let Some(strength_node) = landed_data.get("strength") {
253                val.strength = strength_node.as_real()? as f32;
254            }
255            if let Some(titles_node) = landed_data.get("domain") {
256                for t in titles_node.as_object()?.as_array()? {
257                    val.titles.push(game_state.get_title(&t.as_id()?));
258                }
259            }
260            if let Some(vassals_node) = landed_data.get("vassal_contracts") {
261                for v in vassals_node.as_object()?.as_array()? {
262                    val.vassals
263                        .push(Vassal::Reference(game_state.get_vassal(&v.as_id()?)));
264                }
265            }
266        }
267        if let Some(dead_data) = base.get("dead_data") {
268            val.dead = true;
269            let o = dead_data.as_object()?.as_map()?;
270            if let Some(reason_node) = o.get("reason") {
271                val.reason = Some(reason_node.as_string()?);
272            }
273            if let Some(domain_node) = o.get("domain") {
274                for t in domain_node.as_object()?.as_array()? {
275                    val.titles.push(game_state.get_title(&t.as_id()?));
276                }
277            }
278            val.date = Some(o.get_date("date")?);
279            if let Some(liege_node) = o.get("liege") {
280                val.liege = Some(game_state.get_character(&liege_node.as_id()?));
281            }
282            if let Some(memory_node) = o.get("memories") {
283                for m in memory_node.as_object()?.as_array()? {
284                    val.memories
285                        .push(game_state.get_memory(&m.as_id()?).clone());
286                }
287            }
288        } else if let Some(alive_data) = base.get("alive_data") {
289            val.dead = false;
290            let alive_data = alive_data.as_object()?.as_map()?;
291            val.piety = process_currency(alive_data.get("piety"))?;
292            val.prestige = process_currency(alive_data.get("prestige"))?;
293            if let Some(kills_node) = alive_data.get("kills") {
294                for k in kills_node.as_object()?.as_array()? {
295                    val.kills
296                        .push(game_state.get_character(&k.as_id()?).clone());
297                }
298            }
299            if let Some(gold_node) = alive_data.get("gold") {
300                match gold_node {
301                    SaveFileValue::Object(o) => {
302                        if let Some(gold_node) = o.as_map()?.get("value") {
303                            val.gold = gold_node.as_real()? as f32;
304                        }
305                    }
306                    SaveFileValue::Real(r) => {
307                        val.gold = *r as f32;
308                    }
309                    _ => {}
310                }
311            }
312            for l in alive_data.get_object("languages")?.as_array()? {
313                val.languages.push(l.as_string()?);
314            }
315            if let Some(perk_node) = alive_data.get("perks") {
316                for p in perk_node.as_object()?.as_array()? {
317                    val.traits.push(p.as_string()?);
318                }
319            }
320            if let Some(memory_node) = alive_data.get("memories") {
321                for m in memory_node.as_object()?.as_array()? {
322                    val.memories
323                        .push(game_state.get_memory(&m.as_id()?).clone());
324                }
325            }
326            if let Some(inventory_node) = alive_data.get("inventory") {
327                if let Some(artifacts_node) = inventory_node.as_object()?.as_map()?.get("artifacts")
328                {
329                    for a in artifacts_node.as_object()?.as_array()? {
330                        val.artifacts
331                            .push(game_state.get_artifact(&a.as_id()?).clone());
332                    }
333                }
334            }
335        }
336        if let Some(family_data) = base.get("family_data") {
337            let f = family_data.as_object()?;
338            if !f.is_empty() {
339                let f = f.as_map()?;
340                if let Some(former_spouses_node) = f.get("former_spouses") {
341                    for s in former_spouses_node.as_object()?.as_array()? {
342                        val.former
343                            .push(game_state.get_character(&s.as_id()?).clone());
344                    }
345                }
346                if let Some(spouse_node) = f.get("spouse") {
347                    if let SaveFileValue::Object(o) = spouse_node {
348                        for s in o.as_array()? {
349                            let c = game_state.get_character(&s.as_id()?).clone();
350                            val.spouses.insert(c);
351                        }
352                    } else {
353                        let c = game_state.get_character(&spouse_node.as_id()?).clone();
354                        val.spouses.insert(c);
355                    }
356                }
357                if let Some(primary_spouse_node) = f.get("primary_spouse") {
358                    let c = game_state
359                        .get_character(&primary_spouse_node.as_id()?)
360                        .clone();
361                    val.spouses.insert(c);
362                }
363                if let Some(children_node) = f.get("child") {
364                    for s in children_node.as_object()?.as_array()? {
365                        val.children
366                            .push(game_state.get_character(&s.as_id()?).clone());
367                    }
368                }
369            }
370        }
371        Ok(val)
372    }
373
374    fn finalize(&mut self, reference: &GameRef<Character>) {
375        if let Some(liege) = self.liege.clone() {
376            if let Ok(mut inner) = liege.try_get_internal_mut() {
377                if let Some(liege) = inner.inner_mut() {
378                    liege.add_vassal(reference.clone());
379                } else {
380                    self.liege = None;
381                }
382            }
383        }
384        for child in self.children.iter() {
385            if let Some(child) = child.get_internal_mut().inner_mut() {
386                child.register_parent(reference.clone());
387            }
388        }
389        if self.faith.is_none() {
390            if let Some(house) = &self.house {
391                if let Some(house) = house.get_internal().inner() {
392                    self.faith = Some(house.get_faith());
393                }
394            }
395        }
396        if self.culture.is_none() {
397            if let Some(house) = &self.house {
398                if let Some(house) = house.get_internal().inner() {
399                    self.culture = Some(house.get_culture());
400                }
401            }
402        }
403        for vassal in self.vassals.iter_mut() {
404            match vassal {
405                Vassal::Character(c) => {
406                    if let Some(c) = c.get_internal_mut().inner_mut() {
407                        c.set_liege(reference.clone());
408                    }
409                }
410                Vassal::Reference(c) => {
411                    if let Some(c) = c.get_internal_mut().as_mut() {
412                        if let Some(c) = c.get_internal_mut().inner_mut() {
413                            c.set_liege(reference.clone());
414                        }
415                    }
416                }
417            }
418        }
419    }
420}
421
422impl GameObjectDerived for Character {
423    fn get_name(&self) -> GameString {
424        self.name.clone()
425    }
426
427    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
428        if let Some(faith) = &self.faith {
429            collection.extend([E::from(faith.clone().into())]);
430        }
431        if let Some(culture) = &self.culture {
432            collection.extend([E::from(culture.clone().into())]);
433        }
434        if let Some(house) = &self.house {
435            collection.extend([E::from(house.clone().into())]);
436        }
437        if let Some(liege) = &self.liege {
438            collection.extend([E::from(liege.clone().into())]);
439        }
440        for s in self.spouses.iter() {
441            collection.extend([E::from(s.clone().into())]);
442        }
443        for s in self.former.iter() {
444            collection.extend([E::from(s.clone().into())]);
445        }
446        for s in self.children.iter() {
447            collection.extend([E::from(s.clone().into())]);
448        }
449        for s in self.parents.iter() {
450            collection.extend([E::from(s.clone().into())]);
451        }
452        for s in self.kills.iter() {
453            collection.extend([E::from(s.clone().into())]);
454        }
455        for s in self.vassals.iter() {
456            match s {
457                Vassal::Character(c) => collection.extend([E::from(c.clone().into())]),
458                Vassal::Reference(c) => {
459                    collection.extend([E::from(c.get_internal().as_ref().unwrap().clone().into())])
460                }
461            }
462        }
463        for s in self.titles.iter() {
464            collection.extend([E::from(s.clone().into())]);
465        }
466        for m in self.memories.iter() {
467            collection.extend([E::from(m.clone().into())]);
468        }
469        for a in self.artifacts.iter() {
470            collection.extend([E::from(a.clone().into())]);
471        }
472    }
473}
474
475impl TreeNode<Vec<GameRef<Character>>> for Character {
476    fn get_children(&self) -> Option<Vec<GameRef<Character>>> {
477        if self.children.is_empty() {
478            return None;
479        }
480        Some(self.children.clone())
481    }
482
483    fn get_parent(&self) -> Option<Vec<GameRef<Character>>> {
484        if self.parents.is_empty() {
485            return None;
486        }
487        Some(self.parents.clone())
488    }
489
490    fn get_class(&self) -> Option<GameString> {
491        if let Some(house) = &self.house {
492            if let Some(house) = house.get_internal().inner() {
493                return Some(house.get_name());
494            } else {
495                None
496            }
497        } else {
498            None
499        }
500    }
501}
502
503impl ProceduralPath for Character {
504    fn get_subdir() -> &'static str {
505        "characters"
506    }
507}
508
509impl Renderable for GameObjectEntity<Character> {
510    fn get_template() -> &'static str {
511        C_TEMPLATE_NAME
512    }
513}
514
515impl Localizable for Character {
516    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
517        self.name = localization.localize(&self.name)?;
518        if let Some(nick) = &self.nick {
519            self.nick = Some(localization.localize(nick)?);
520        }
521        if let Some(reason) = &self.reason {
522            self.reason = Some(localization.localize_query(reason, |stack| {
523                if stack.len() == 2 {
524                    if stack[0].0 == "GetTrait" {
525                        return localization
526                            .localize("trait_".to_string() + &stack[0].1[0])
527                            .ok();
528                    } else if stack[1].0 == "GetHerHis" {
529                        if self.female {
530                            return Some("her".into());
531                        } else {
532                            return Some("his".into());
533                        }
534                    } else if stack[1].0 == "GetHerHim" {
535                        if self.female {
536                            return Some("her".into());
537                        } else {
538                            return Some("him".into());
539                        }
540                    } else if stack[1].0 == "GetHerselfHimself" {
541                        if self.female {
542                            return Some("herself".into());
543                        } else {
544                            return Some("himself".into());
545                        }
546                    } else if stack[1].0 == "GetSheHe" {
547                        if self.female {
548                            return Some("she".into());
549                        } else {
550                            return Some("he".into());
551                        }
552                    } else if stack[0].0 == "TARGET_CHARACTER" && stack[1].0 == "GetUIName" {
553                        return Some("an unknown assailant".into()); // TODO
554                    }
555                }
556                None
557            })?);
558        }
559        for t in self.traits.iter_mut() {
560            let key = if t.starts_with("child_of_concubine") {
561                "trait_child_of_concubine".to_string()
562            } else if t.ends_with("hajjaj") {
563                if self.female {
564                    "trait_hajjah".to_string()
565                } else {
566                    "trait_hajji".to_string()
567                }
568            } else if t.as_ref() == "lifestyle_traveler" {
569                // TODO this should reflect the traveler level? i think
570                "trait_traveler_1".to_string()
571            } else if t.starts_with("viking") {
572                // TODO viking should be displayed if the culture has longships (trait_viking_has_longships)
573                "trait_viking_fallback".to_string()
574            } else if t.starts_with("shieldmaiden") {
575                if self.female {
576                    "trait_shieldmaiden_female".to_string()
577                } else {
578                    "trait_shieldmaiden_male".to_string()
579                }
580            } else {
581                "trait_".to_string() + t
582            };
583            *t = localization.localize(key)?;
584        }
585        for t in self.languages.iter_mut() {
586            *t = localization.localize(t.to_string() + "_name")?;
587        }
588        Ok(())
589    }
590}