ck3_history_extractor/structures/
character.rs1use 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#[derive(Serialize, Clone)]
22#[serde(untagged)]
23enum Vassal {
24 Character(Shared<GameObjectEntity<Character>>),
25 Reference(Shared<Option<Shared<GameObjectEntity<Character>>>>),
26}
27
28#[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
64fn 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 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 pub fn register_parent(&mut self, parent: GameRef<Character>) {
98 self.parents.push(parent);
99 }
100
101 pub fn get_death_date(&self) -> Option<Date> {
103 self.date.clone()
104 }
105
106 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 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 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 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 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 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 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 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 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()); }
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 "trait_traveler_1".to_string()
571 } else if t.starts_with("viking") {
572 "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}