ck3_history_extractor/structures/
house.rs

1use jomini::common::Date;
2
3use serde::Serialize;
4
5use super::{
6    super::{
7        display::{ProceduralPath, Renderable},
8        game_data::{GameData, Localizable, LocalizationError, Localize},
9        jinja_env::HOUSE_TEMPLATE_NAME,
10        parser::{
11            GameObjectMap, GameObjectMapping, GameState, KeyError, ParsingError, SaveFileValue,
12            SaveObjectError,
13        },
14        types::{GameString, HashMap, Wrapper, WrapperMut},
15    },
16    Character, Culture, Dynasty, EntityRef, Faith, FromGameObject, GameObjectDerived,
17    GameObjectEntity, GameRef,
18};
19
20#[derive(Serialize)]
21pub struct House {
22    name: GameString,
23    parent: GameRef<Dynasty>,
24    leaders: Vec<GameRef<Character>>,
25    motto: Option<(GameString, HashMap<i64, GameString>)>,
26    found_date: Option<Date>,
27}
28
29fn get_house_name(base: &GameObjectMap) -> Result<GameString, ParsingError> {
30    if let Some(name) = base.get("name").or(base.get("localized_name")) {
31        Ok(name.as_string()?)
32    } else {
33        match base.get_err("key")? {
34            SaveFileValue::Integer(id) => Ok(id.to_string().into()),
35            SaveFileValue::String(name) => Ok(name.clone()),
36            _ => Err(ParsingError::StructureError(SaveObjectError::KeyError(
37                KeyError::MissingKey("house_name or localized_name".to_string(), base.to_owned()),
38            ))),
39        }
40    }
41}
42
43impl FromGameObject for House {
44    fn from_game_object(
45        base: &GameObjectMap,
46        game_state: &mut GameState,
47    ) -> Result<Self, ParsingError> {
48        let mut this = Self {
49            name: get_house_name(base)?,
50            parent: game_state
51                .get_dynasty(&base.get_game_id("dynasty")?)
52                .clone(),
53            found_date: base.get("found_date").map(|n| n.as_date()).transpose()?,
54            leaders: Vec::new(),
55            motto: None,
56        };
57        if let Some(motto_node) = base.get("motto") {
58            if let SaveFileValue::Object(obj) = motto_node {
59                let o = obj.as_map()?;
60                let mut vars = HashMap::new();
61                for v in o.get_object("variables")?.as_array()? {
62                    let pair = v.as_object()?.as_map()?;
63                    vars.insert(pair.get_integer("key")?, pair.get_string("value")?);
64                }
65                this.motto = Some((o.get_string("key")?.clone(), vars));
66            } else {
67                this.motto = Some((motto_node.as_string()?, HashMap::default()));
68            }
69        }
70        if let Some(leaders_obj) = base.get("historical") {
71            for l in leaders_obj.as_object()?.as_array()? {
72                this.leaders
73                    .push(game_state.get_character(&l.as_id()?).clone());
74            }
75        }
76        if let Some(leader) = base.get("head_of_house") {
77            let char = game_state.get_character(&leader.as_id()?);
78            if !this.leaders.contains(&char) {
79                this.leaders.push(char.clone());
80            }
81        }
82        Ok(this)
83    }
84
85    fn finalize(&mut self, reference: &GameRef<Self>) {
86        self.parent
87            .get_internal_mut()
88            .inner_mut()
89            .unwrap()
90            .register_house(reference.clone());
91    }
92}
93
94impl GameObjectDerived for House {
95    fn get_name(&self) -> GameString {
96        self.name.clone()
97    }
98
99    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
100        for leader in self.leaders.iter() {
101            collection.extend([E::from(leader.clone().into())]);
102        }
103        collection.extend([E::from(self.parent.clone().into())]);
104    }
105}
106
107impl Localizable for House {
108    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
109        self.name = localization.localize(&self.name)?;
110        let query = |stack: &Vec<(String, Vec<String>)>| {
111            match stack.len() {
112                2 => {
113                    match stack[0].0.as_str() {
114                        "CHARACTER" => {
115                            if let Some(leader) = self
116                                .leaders
117                                .iter()
118                                .find(|l| l.get_internal().inner().is_some())
119                            {
120                                let leader = leader.get_internal();
121                                let leader = leader.inner().unwrap();
122                                match stack[1].0.as_str() {
123                                    "Custom" => {
124                                        if stack[1].1[0] == "GetAppropriateGodname" {
125                                            // TODO localize the godname properly here
126                                            return Some("God".into());
127                                        } else if stack[1].1[0] == "QueenKing" {
128                                            if leader.get_female() {
129                                                return Some("Queen".into());
130                                            } else {
131                                                return Some("King".into());
132                                            }
133                                        } else if stack[1].1[0] == "GetDaughterSon" {
134                                            if leader.get_female() {
135                                                return Some("Daughter".into());
136                                            } else {
137                                                return Some("Son".into());
138                                            }
139                                        }
140                                    }
141                                    "GetFirstName" => {
142                                        return Some(leader.get_name().clone());
143                                    }
144                                    "GetSheHe" => {
145                                        if leader.get_female() {
146                                            return Some("She".into());
147                                        } else {
148                                            return Some("He".into());
149                                        }
150                                    }
151                                    "GetWomenMen" => {
152                                        if leader.get_female() {
153                                            return Some("Women".into());
154                                        } else {
155                                            return Some("Men".into());
156                                        }
157                                    }
158                                    _ => {}
159                                }
160                            } else {
161                                return Some("House".into());
162                            }
163                        }
164                        _ => {}
165                    }
166                }
167                3 => {
168                    if stack[2].0 == "GetBaseNameNoTooltip" {
169                        return Some(self.name.clone());
170                    }
171                }
172                _ => {}
173            };
174            None
175        };
176        if let Some((motto, variables)) = &mut self.motto {
177            for (_, v) in variables.iter_mut() {
178                *v = localization.localize_query(&v, query)?;
179            }
180            *motto = localization.localize_query(&motto, |stack| {
181                match stack.len() {
182                    1 => {
183                        if let Ok(k) = stack[0].0.parse::<i64>() {
184                            if let Some(v) = variables.get(&k) {
185                                return Some(v.clone());
186                            }
187                        }
188                    }
189                    _ => {
190                        return query(stack);
191                    }
192                }
193                None
194            })?;
195        }
196        Ok(())
197    }
198}
199
200impl House {
201    pub fn get_faith(&self) -> GameRef<Faith> {
202        for leader in self.leaders.iter().rev() {
203            if let Ok(faith) = leader.try_get_internal() {
204                if let Some(faith) = faith.inner().unwrap().get_faith() {
205                    return faith;
206                }
207            }
208        }
209        unimplemented!()
210    }
211
212    pub fn get_culture(&self) -> GameRef<Culture> {
213        for leader in self.leaders.iter().rev() {
214            if let Ok(culture) = leader.try_get_internal() {
215                if let Some(culture) = culture.inner().unwrap().get_culture() {
216                    return culture;
217                }
218            }
219        }
220        unimplemented!()
221    }
222
223    pub fn get_founder(&self) -> GameRef<Character> {
224        if let Some(leader) = self.leaders.first() {
225            leader.clone()
226        } else {
227            self.parent
228                .get_internal()
229                .inner()
230                .unwrap()
231                .get_leader()
232                .unwrap()
233        }
234    }
235
236    pub fn get_dynasty(&self) -> GameRef<Dynasty> {
237        self.parent.clone()
238    }
239
240    pub fn get_found_date(&self) -> Option<Date> {
241        self.found_date
242    }
243}
244
245impl ProceduralPath for House {
246    fn get_subdir() -> &'static str {
247        "houses"
248    }
249}
250
251impl Renderable for GameObjectEntity<House> {
252    fn get_template() -> &'static str {
253        HOUSE_TEMPLATE_NAME
254    }
255}