ck3_history_extractor/structures/
dynasty.rs1use std::path::Path;
2
3use serde::Serialize;
4
5use super::{
6 super::{
7 display::{Grapher, ProceduralPath, Renderable},
8 game_data::{GameData, Localizable, LocalizationError, Localize},
9 jinja_env::DYN_TEMPLATE_NAME,
10 parser::{GameObjectMap, GameObjectMapping, GameState, ParsingError, SaveFileValue},
11 types::{GameString, HashMap, Wrapper},
12 },
13 Character, EntityRef, FromGameObject, GameObjectDerived, GameObjectEntity, GameRef, House,
14};
15
16#[derive(Serialize)]
17pub struct Dynasty {
18 name: Option<GameString>,
19 prestige_tot: f32,
20 prestige: f32,
21 perks: HashMap<GameString, u8>,
22 leader: Option<GameRef<Character>>,
23 houses: Vec<GameRef<House>>,
24}
25
26impl FromGameObject for Dynasty {
27 fn from_game_object(
28 base: &GameObjectMap,
29 game_state: &mut GameState,
30 ) -> Result<Self, ParsingError> {
31 let mut val = Self {
32 name: base
34 .get("name")
35 .or(base.get("dynasty_name"))
36 .or(base.get("localized_name"))
37 .and_then(|n| n.as_string().ok()),
38 prestige_tot: 0.0,
39 prestige: 0.0,
40 perks: HashMap::new(),
41 leader: None,
42 houses: Vec::new(),
43 };
44 if let Some(leader) = base.get("dynasty_head") {
45 val.leader = Some(game_state.get_character(&leader.as_id()?).clone());
46 }
47 if let Some(perks_obj) = base.get("perk") {
48 for p in perks_obj.as_object()?.as_array()? {
49 let perk = p.as_string()?;
50 let mut i: u8 = 0;
52 let mut key: Option<&str> = None;
53 let mut level: u8 = 0;
54 for el in perk.rsplitn(2, '_') {
55 if i == 0 {
56 level = el.parse::<u8>().unwrap();
57 } else {
58 key = Some(el);
59 }
60 i += 1;
61 }
62 if let Some(key) = key {
63 let key = GameString::from(key);
64 if *val.perks.entry(key.clone()).or_default() < level {
65 val.perks.insert(key, level);
66 }
67 }
68 }
69 }
70 if let Some(currency) = base.get("prestige") {
71 let o = currency.as_object()?.as_map()?;
72 if let Some(acc) = o.get("accumulated") {
73 if let SaveFileValue::Object(o) = acc {
74 val.prestige_tot = o.as_map()?.get_real("value")? as f32;
75 } else {
76 val.prestige_tot = acc.as_real()? as f32;
77 }
78 }
79 if let Some(c) = o.get("currency") {
80 if let SaveFileValue::Object(o) = c {
81 val.prestige = o.as_map()?.get_real("value")? as f32;
82 } else {
83 val.prestige = c.as_real()? as f32;
84 }
85 }
86 }
87 return Ok(val);
88 }
89
90 fn finalize(&mut self, _reference: &GameRef<Self>) {
91 self.houses.sort_by(|a, b| {
92 a.get_internal()
93 .inner()
94 .unwrap()
95 .get_found_date()
96 .cmp(&b.get_internal().inner().unwrap().get_found_date())
97 });
98 if self.name.is_none() {
100 self.name = Some(
101 self.houses
102 .first()
103 .unwrap()
104 .clone()
105 .get_internal()
106 .inner()
107 .unwrap()
108 .get_name()
109 .clone(),
110 );
111 }
112 }
113}
114
115impl Dynasty {
116 pub fn register_house(&mut self, house: GameRef<House>) {
117 self.houses.push(house);
118 }
119
120 pub fn get_founder(&self) -> GameRef<Character> {
121 self.houses
122 .first()
123 .unwrap()
124 .clone()
125 .get_internal()
126 .inner()
127 .unwrap()
128 .get_founder()
129 }
130
131 pub fn get_leader(&self) -> Option<GameRef<Character>> {
132 self.leader.clone()
133 }
134}
135
136impl GameObjectDerived for Dynasty {
137 fn get_name(&self) -> GameString {
138 self.name.as_ref().unwrap().clone()
139 }
140
141 fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
142 if let Some(leader) = &self.leader {
143 collection.extend([E::from(leader.clone().into())]);
144 }
145 for house in self.houses.iter() {
146 collection.extend([E::from(house.clone().into())]);
147 }
148 }
149}
150
151impl ProceduralPath for Dynasty {
152 fn get_subdir() -> &'static str {
153 "dynasties"
154 }
155}
156
157impl Renderable for GameObjectEntity<Dynasty> {
158 fn get_template() -> &'static str {
159 DYN_TEMPLATE_NAME
160 }
161
162 fn render(&self, path: &Path, _: &GameState, grapher: Option<&Grapher>, _: &GameData) {
163 if let Some(grapher) = grapher {
164 if let Some(dynasty) = self.inner() {
165 let mut buf = path.join(Dynasty::get_subdir());
166 buf.push(self.id.to_string() + ".svg");
167 grapher.create_dynasty_graph(dynasty, &buf);
168 }
169 }
170 }
171}
172
173impl Localizable for Dynasty {
174 fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
175 if let Some(name) = &self.name {
176 self.name = Some(localization.localize(name)?);
177 }
178 let drained_perks: Vec<_> = self.perks.drain().collect();
179 for (perk, level) in drained_perks {
180 self.perks.insert(
181 localization.localize(perk.to_string() + "_track_name")?,
182 level,
183 );
184 }
185 Ok(())
186 }
187}