ck3_history_extractor/parser/
game_state.rs

1use std::collections::BTreeMap;
2
3use super::{
4    super::{
5        display::{Grapher, RealmDifference, Timeline},
6        game_data::{GameData, Localizable, LocalizationError},
7        structures::{
8            Artifact, Character, Culture, Dynasty, Faith, FromGameObject, GameObjectDerived,
9            GameObjectEntity, House, Memory, Title,
10        },
11        types::{GameId, GameString, HashMap, Shared, Wrapper, WrapperMut},
12    },
13    game_object::GameObjectMap,
14    ParsingError,
15};
16
17use jomini::common::{Date, PdsDate};
18
19use serde::{ser::SerializeMap, Serialize, Serializer};
20
21pub type GameRef<T> = Shared<GameObjectEntity<T>>;
22
23/// Returns a reference to the object with the given key in the map, or inserts a dummy object if it does not exist and returns a reference to that.
24fn get_or_insert_dummy<T: GameObjectDerived + FromGameObject>(
25    map: &mut HashMap<GameId, GameRef<T>>,
26    key: &GameId,
27) -> GameRef<T> {
28    if let Some(val) = map.get(key) {
29        return val.clone();
30    } else {
31        let v = Shared::wrap(GameObjectEntity::new(*key));
32        map.insert(*key, v.clone());
33        v
34    }
35}
36
37impl Serialize for Shared<Option<GameRef<Character>>> {
38    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
39    where
40        S: serde::Serializer,
41    {
42        match self.get_internal().as_ref() {
43            Some(c) => c.serialize(serializer),
44            None => serializer.serialize_none(),
45        }
46    }
47}
48
49fn serialize_ref_map<T: Serialize + GameObjectDerived, S: Serializer>(
50    map: &HashMap<GameId, GameRef<T>>,
51    serializer: S,
52) -> Result<S::Ok, S::Error> {
53    let mut state = serializer.serialize_map(Some(map.len()))?;
54    for (k, v) in map.iter() {
55        state.serialize_entry(k, &*v.get_internal())?;
56    }
57    state.end()
58}
59
60/* TODO future refactor
61There is a way I can make this object hold Boxes, and then return &Box.
62This would require me, to properly rework the parser, to use a WIP game state
63for entity storage, that would then @ finalize transform into this. Structures
64would have to be reworked, to hold only raw IDs, and then in finalize
65modify the game state, then create final structures that would have refs
66to the newly finalized game state.
67This would allow me to remove the need for Shared, and make the code
68much cleaner.
69This would also eliminate uninitalized objects.
70
71struct ShallowRef<T: Sized> {
72    casper: PhantomData<T>,
73    id: GameId,
74}
75*/
76
77/// A struct representing all known game objects.
78/// It is guaranteed to always return a reference to the same object for the same key.
79/// Naturally the value of that reference may change as values are added to the game state.
80/// This is mainly used during the process of gathering data from the parsed save file.
81#[derive(Serialize)]
82pub struct GameState {
83    /// A character id->Character transform
84    #[serde(serialize_with = "serialize_ref_map")]
85    characters: HashMap<GameId, GameRef<Character>>,
86    /// A title id->Title transform
87    #[serde(serialize_with = "serialize_ref_map")]
88    titles: HashMap<GameId, GameRef<Title>>,
89    /// A faith id->Title transform
90    #[serde(serialize_with = "serialize_ref_map")]
91    faiths: HashMap<GameId, GameRef<Faith>>,
92    /// A culture id->Culture transform
93    #[serde(serialize_with = "serialize_ref_map")]
94    cultures: HashMap<GameId, GameRef<Culture>>,
95    /// A dynasty id->Dynasty transform
96    #[serde(serialize_with = "serialize_ref_map")]
97    dynasties: HashMap<GameId, GameRef<Dynasty>>,
98    #[serde(serialize_with = "serialize_ref_map")]
99    houses: HashMap<GameId, GameRef<House>>,
100    /// A memory id->Memory transform
101    #[serde(serialize_with = "serialize_ref_map")]
102    memories: HashMap<GameId, GameRef<Memory>>,
103    /// A artifact id->Artifact transform
104    #[serde(serialize_with = "serialize_ref_map")]
105    artifacts: HashMap<GameId, GameRef<Artifact>>,
106    /// A trait id->Trait identifier transform
107    traits_lookup: Vec<GameString>,
108    /// A vassal contract id->Character transform
109    contract_transform: HashMap<GameId, Shared<Option<GameRef<Character>>>>,
110    character_transform: HashMap<GameId, GameId>,
111    #[serde(skip)]
112    county_data: HashMap<String, (GameRef<Faith>, GameRef<Culture>)>,
113    /// The current date from the meta section
114    current_date: Option<Date>,
115    /// The time Y.M.D from which the game started
116    offset_date: Option<Date>,
117}
118
119impl Default for GameState {
120    fn default() -> Self {
121        GameState {
122            characters: HashMap::default(),
123            titles: HashMap::default(),
124            faiths: HashMap::default(),
125            cultures: HashMap::default(),
126            dynasties: HashMap::default(),
127            houses: HashMap::default(),
128            memories: HashMap::default(),
129            artifacts: HashMap::default(),
130            traits_lookup: Vec::new(),
131            contract_transform: HashMap::default(),
132            character_transform: HashMap::default(),
133            county_data: HashMap::default(),
134            current_date: None,
135            offset_date: None,
136        }
137    }
138}
139
140impl GameState {
141    /// Add a lookup table for traits
142    pub fn add_lookup(&mut self, array: Vec<GameString>) {
143        self.traits_lookup = array;
144    }
145
146    pub fn add_character_transform(&mut self, transform: HashMap<GameId, GameId>) {
147        self.character_transform = transform;
148    }
149
150    /// Get a trait by id
151    pub fn get_trait(&self, id: u16) -> GameString {
152        self.traits_lookup[id as usize].clone()
153    }
154
155    /// Set the current date
156    pub fn set_current_date(&mut self, date: Date, offset: Date) {
157        self.current_date = Some(date);
158        self.offset_date = Some(offset);
159    }
160
161    /// Get the current date
162    pub fn get_current_date(&self) -> Option<Date> {
163        return self.current_date;
164    }
165
166    /// Get a character by key
167    pub fn get_character(&mut self, key: &GameId) -> GameRef<Character> {
168        get_or_insert_dummy(&mut self.characters, key)
169    }
170
171    /// Gets the vassal associated with the contract with the given id
172    pub fn get_vassal(&mut self, contract_id: &GameId) -> Shared<Option<GameRef<Character>>> {
173        if let Some(v) = self.contract_transform.get(contract_id) {
174            return v.clone();
175        } else {
176            let v = Shared::wrap(None);
177            self.contract_transform.insert(*contract_id, v.clone());
178            return v;
179        }
180    }
181
182    /// Adds a new vassal contract
183    pub fn add_contract(&mut self, contract_id: &GameId, character_id: &GameId) {
184        let char = self.get_character(character_id);
185        if let Some(contract) = self.contract_transform.get_mut(contract_id) {
186            contract.get_internal_mut().replace(char);
187        } else {
188            self.contract_transform
189                .insert(*contract_id, Shared::wrap(Some(char)));
190        }
191    }
192
193    /// Get a title by key
194    pub fn get_title(&mut self, key: &GameId) -> GameRef<Title> {
195        get_or_insert_dummy(&mut self.titles, key)
196    }
197
198    /// Get a faith by key
199    pub fn get_faith(&mut self, key: &GameId) -> GameRef<Faith> {
200        get_or_insert_dummy(&mut self.faiths, key)
201    }
202
203    /// Get a culture by key
204    pub fn get_culture(&mut self, key: &GameId) -> GameRef<Culture> {
205        get_or_insert_dummy(&mut self.cultures, key)
206    }
207
208    /// Get a dynasty by key
209    pub fn get_dynasty(&mut self, key: &GameId) -> GameRef<Dynasty> {
210        get_or_insert_dummy(&mut self.dynasties, key)
211    }
212
213    /// Get a memory by key
214    pub fn get_memory(&mut self, key: &GameId) -> GameRef<Memory> {
215        get_or_insert_dummy(&mut self.memories, key)
216    }
217
218    /// Get an artifact by key
219    pub fn get_artifact(&mut self, key: &GameId) -> GameRef<Artifact> {
220        get_or_insert_dummy(&mut self.artifacts, key)
221    }
222
223    pub fn get_house(&mut self, key: &GameId) -> GameRef<House> {
224        get_or_insert_dummy(&mut self.houses, key)
225    }
226
227    pub fn add_house(&mut self, key: &GameId, value: &GameObjectMap) -> Result<(), ParsingError> {
228        self.get_house(key).get_internal_mut().init(value, self)
229    }
230
231    pub fn add_artifact(
232        &mut self,
233        key: &GameId,
234        value: &GameObjectMap,
235    ) -> Result<(), ParsingError> {
236        self.get_artifact(key).get_internal_mut().init(value, self)
237    }
238
239    /// Add a character to the game state    
240    pub fn add_character(
241        &mut self,
242        key: &GameId,
243        value: &GameObjectMap,
244    ) -> Result<(), ParsingError> {
245        let char = self.get_character(key);
246        char.get_internal_mut().init(value, self)?;
247        if let Some(alt) = self.character_transform.get(key) {
248            if let Some(alt_char) = self.characters.get(alt) {
249                if alt_char.get_internal().inner().is_none() {
250                    // TODO this clone is very wasteful
251                    alt_char
252                        .get_internal_mut()
253                        .replace(char.get_internal().inner().unwrap().clone());
254                }
255            }
256        }
257        Ok(())
258    }
259
260    /// Add a title to the game state
261    pub fn add_title(&mut self, key: &GameId, value: &GameObjectMap) -> Result<(), ParsingError> {
262        self.get_title(key).get_internal_mut().init(value, self)
263    }
264
265    /// Add a faith to the game state
266    pub fn add_faith(&mut self, key: &GameId, value: &GameObjectMap) -> Result<(), ParsingError> {
267        self.get_faith(key).get_internal_mut().init(value, self)
268    }
269
270    /// Add a culture to the game state
271    pub fn add_culture(&mut self, key: &GameId, value: &GameObjectMap) -> Result<(), ParsingError> {
272        self.get_culture(key).get_internal_mut().init(value, self)
273    }
274
275    /// Add a dynasty to the game state
276    pub fn add_dynasty(&mut self, key: &GameId, value: &GameObjectMap) -> Result<(), ParsingError> {
277        self.get_dynasty(key).get_internal_mut().init(value, self)
278    }
279
280    /// Add a memory to the game state
281    pub fn add_memory(&mut self, key: &GameId, value: &GameObjectMap) -> Result<(), ParsingError> {
282        self.get_memory(key).get_internal_mut().init(value, self)
283    }
284
285    pub fn get_baronies_of_counties<F: Fn(&Title) -> bool>(&self, filter: F) -> Vec<GameString> {
286        let mut res = Vec::new();
287        for title in self.titles.values() {
288            if let Some(title) = title.get_internal().inner() {
289                if filter(&title) {
290                    res.append(&mut title.get_barony_keys());
291                }
292            }
293        }
294        res
295    }
296
297    pub fn add_county_data(
298        &mut self,
299        county_data: HashMap<String, (GameRef<Faith>, GameRef<Culture>)>,
300    ) {
301        self.county_data = county_data;
302    }
303
304    pub fn new_grapher(&self) -> Grapher {
305        let mut total_yearly_deaths: BTreeMap<i16, u32> = BTreeMap::default();
306        let mut faith_yearly_deaths = HashMap::default();
307        let mut culture_yearly_deaths = HashMap::default();
308        let start_year = self.current_date.unwrap().year() - self.offset_date.unwrap().year();
309        for character in self.characters.values() {
310            if let Some(char) = character.get_internal().inner() {
311                if let Some(death_date) = char.get_death_date() {
312                    if death_date.year() <= start_year
313                        || death_date.year() >= self.current_date.unwrap().year()
314                    {
315                        continue;
316                    }
317                    let count = total_yearly_deaths.entry(death_date.year()).or_insert(0);
318                    *count += 1;
319                    {
320                        let entry = faith_yearly_deaths
321                            .entry(char.get_faith().as_ref().unwrap().get_internal().get_id())
322                            .or_insert(BTreeMap::default());
323                        let count = entry.entry(death_date.year()).or_insert(0);
324                        *count += 1;
325                    }
326                    {
327                        let entry = culture_yearly_deaths
328                            .entry(char.get_culture().as_ref().unwrap().get_internal().get_id())
329                            .or_insert(BTreeMap::default());
330                        let count = entry.entry(death_date.year()).or_insert(0);
331                        *count += 1;
332                    }
333                }
334            }
335        }
336        Grapher::new(
337            faith_yearly_deaths,
338            culture_yearly_deaths,
339            total_yearly_deaths,
340        )
341    }
342
343    pub fn new_timeline(&self) -> Timeline {
344        const DESTROYED_STR: &str = "destroyed";
345        const USURPED_STR: &str = "usurped";
346        const CONQUERED_START_STR: &str = "conq"; //this should match both 'conquered' and 'conquest holy war'
347
348        let mut lifespans = Vec::new();
349        let mut latest_event = 0;
350        let mut event_checkout = Vec::new();
351        for title in self.titles.values() {
352            //first we handle the empires and collect titles that might be relevant for events
353            if let Some(t) = title.get_internal().inner() {
354                let hist = t.get_history_iter();
355                if hist.len() == 0 {
356                    continue;
357                }
358                let k = t.get_key();
359                //if the key is there
360                let kingdom = k.as_ref().starts_with("k_");
361                if kingdom {
362                    event_checkout.push(title.clone());
363                    //event_checkout.push(title.get_internal().get_capital().unwrap().clone());
364                    continue;
365                }
366                let empire = k.as_ref().starts_with("e_");
367                if !empire {
368                    continue;
369                }
370                event_checkout.push(title.clone());
371                event_checkout.push(t.get_capital().unwrap().clone());
372                let mut item = (title.clone(), Vec::new());
373                let mut empty = true;
374                let mut start = 0;
375                for entry in hist {
376                    let yr = entry.0.year();
377                    if yr > latest_event {
378                        latest_event = yr;
379                    }
380                    let event = entry.2.as_ref();
381                    if event == DESTROYED_STR {
382                        //if it was destroyed we mark the end of the lifespan
383                        item.1.push((start, yr));
384                        empty = true;
385                    } else if empty {
386                        //else if we are not in a lifespan we start a new one
387                        start = yr;
388                        empty = false;
389                    }
390                }
391                if empire {
392                    if !empty {
393                        item.1.push((start, 0));
394                    }
395                    //println!("{} {:?}", title.get_internal().get_key().unwrap(), item.1);
396                    lifespans.push(item);
397                }
398            }
399        }
400        let mut events: Vec<(
401            i16,
402            GameRef<Character>,
403            GameRef<Title>,
404            GameString,
405            RealmDifference,
406        )> = Vec::new();
407        for title in event_checkout {
408            if let Some(tit) = title.get_internal().inner() {
409                //find the first event that has a character attached
410                let mut hist = tit.get_history_iter().skip_while(|a| a.1.is_none());
411                let next = hist.next();
412                if next.is_none() {
413                    continue;
414                }
415                if let Some(first_char) = next.unwrap().1.as_ref().unwrap().get_internal().inner() {
416                    let mut faith = first_char
417                        .get_faith()
418                        .as_ref()
419                        .unwrap()
420                        .get_internal()
421                        .get_id();
422                    let mut culture = first_char
423                        .get_culture()
424                        .as_ref()
425                        .unwrap()
426                        .get_internal()
427                        .get_id();
428                    for entry in hist {
429                        let char = entry.1.as_ref();
430                        if char.is_none() {
431                            continue;
432                        }
433                        let char = char.unwrap();
434                        let event = entry.2.as_ref();
435                        if let Some(ch) = char.get_internal().inner() {
436                            let char_faith = ch.get_faith().as_ref().unwrap().clone();
437                            let ch_faith = char_faith.get_internal();
438                            let char_culture = ch.get_culture().as_ref().unwrap().clone();
439                            let ch_culture = char_culture.get_internal();
440                            if event == USURPED_STR || event.starts_with(CONQUERED_START_STR) {
441                                let year: i16 = entry.0.year();
442                                if ch_faith.get_id() != faith {
443                                    events.push((
444                                        year,
445                                        char.clone(),
446                                        title.clone(),
447                                        GameString::from("faith"),
448                                        RealmDifference::Faith(char_faith.clone()),
449                                    ));
450                                    faith = ch_faith.get_id();
451                                } else if ch_culture.get_id() != culture {
452                                    events.push((
453                                        year,
454                                        char.clone(),
455                                        title.clone(),
456                                        GameString::from("people"),
457                                        RealmDifference::Culture(char_culture.clone()),
458                                    ));
459                                    culture = ch_culture.get_id();
460                                }
461                            } else {
462                                if ch_faith.get_id() != faith {
463                                    faith = ch_faith.get_id();
464                                }
465                                if ch_culture.get_id() != culture {
466                                    culture = ch_culture.get_id();
467                                }
468                            }
469                        }
470                    }
471                }
472            }
473        }
474        events.sort_by(|a, b| a.0.cmp(&b.0));
475        return Timeline::new(lifespans, self.current_date.unwrap().year(), events);
476    }
477}
478
479impl Localizable for GameState {
480    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
481        for character in self.characters.values_mut() {
482            character.localize(localization)?;
483        }
484        for title in &mut self.titles.values_mut() {
485            title.localize(localization)?;
486            if let Some(internal) = title.get_internal_mut().inner_mut() {
487                if let Some(assoc) = self.county_data.get_mut(internal.get_key().as_ref()) {
488                    if let Title::County { faith, culture, .. } = internal {
489                        *faith = Some(assoc.0.clone());
490                        *culture = Some(assoc.1.clone());
491                    }
492                }
493            }
494        }
495        for faith in &mut self.faiths.values_mut() {
496            faith.localize(localization)?;
497        }
498        for culture in &mut self.cultures.values_mut() {
499            culture.localize(localization)?;
500        }
501        for house in &mut self.houses.values_mut() {
502            house.localize(localization)?;
503        }
504        for dynasty in &mut self.dynasties.values_mut() {
505            dynasty.localize(localization)?;
506        }
507        for memory in &mut self.memories.values_mut() {
508            memory.localize(localization)?;
509        }
510        for artifact in &mut self.artifacts.values_mut() {
511            artifact.localize(localization)?;
512        }
513        Ok(())
514    }
515}
516
517#[cfg(test)]
518mod tests {
519
520    use super::*;
521
522    #[test]
523    fn test_get_or_insert_dummy() {
524        let mut map = HashMap::default();
525        let key = 1;
526        let val = get_or_insert_dummy::<Artifact>(&mut map, &key);
527        assert_eq!(val.get_internal().get_id(), key);
528        let val2 = get_or_insert_dummy(&mut map, &key);
529        assert_eq!(val.get_internal().get_id(), val2.get_internal().get_id());
530    }
531}