ck3_history_extractor/structures/
memory.rs

1use jomini::common::Date;
2use serde::Serialize;
3
4use super::{
5    super::{
6        game_data::{GameData, Localizable, LocalizationError, Localize},
7        parser::{
8            GameObjectMap, GameObjectMapping, GameState, ParsingError, SaveFileObject,
9            SaveFileValue,
10        },
11        types::{GameId, GameString, HashMap, Wrapper},
12    },
13    Character, EntityRef, FromGameObject, GameObjectDerived, GameRef,
14};
15
16#[derive(Serialize, Clone, Debug)]
17enum MemoryVariable {
18    Id(GameId),
19    String(GameString),
20    Bool(bool),
21    None,
22}
23
24impl From<&GameObjectMap> for MemoryVariable {
25    fn from(value: &GameObjectMap) -> Self {
26        let tp = value.get_string("type").unwrap();
27        match tp.as_ref() {
28            "value" => MemoryVariable::None,
29            "boolean" => MemoryVariable::Bool(value.get_integer("identity").unwrap() != 0),
30            "trait" => MemoryVariable::String(value.get_string("key").unwrap()),
31            "flag" => {
32                if let Some(v) = value.get("flag") {
33                    match v {
34                        SaveFileValue::Integer(int) => MemoryVariable::Bool(*int != 0),
35                        SaveFileValue::Boolean(b) => MemoryVariable::Bool(*b),
36                        SaveFileValue::String(s) => MemoryVariable::String(s.clone()),
37                        _ => unimplemented!("Unsupported type for flag: {:?}", v),
38                    }
39                } else {
40                    MemoryVariable::None
41                }
42            }
43            _ => value
44                .get_game_id("identity")
45                .and_then(|id| Ok(MemoryVariable::Id(id)))
46                .unwrap_or(MemoryVariable::None),
47        }
48    }
49}
50
51/// A struct representing a memory in the game
52#[derive(Serialize)]
53pub struct Memory {
54    date: Date,
55    r#type: GameString,
56    participants: HashMap<String, GameRef<Character>>,
57    variables: HashMap<GameString, MemoryVariable>,
58}
59
60impl FromGameObject for Memory {
61    fn from_game_object(
62        base: &GameObjectMap,
63        game_state: &mut GameState,
64    ) -> Result<Self, ParsingError> {
65        let mut val = Self {
66            date: base.get_date("creation_date")?,
67            r#type: base.get_string("type")?,
68            participants: HashMap::new(),
69            variables: HashMap::new(),
70        };
71        if let Some(participants_node) = base.get("participants") {
72            for part in participants_node.as_object()?.as_map()? {
73                val.participants.insert(
74                    part.0.clone(),
75                    game_state.get_character(&part.1.as_id()?).clone(),
76                );
77            }
78        }
79        if let Some(variables_node) = base.get("variables") {
80            let data_node = variables_node
81                .as_object()?
82                .as_map()?
83                .get_object("data")?
84                .as_array()?;
85            for variable in data_node {
86                let variable = variable.as_object()?.as_map()?;
87                let key = variable.get_string("flag")?;
88                if let SaveFileObject::Map(data) = variable.get_object("data")? {
89                    val.variables
90                        .insert(key.clone(), MemoryVariable::from(data));
91                }
92            }
93        }
94        Ok(val)
95    }
96}
97
98impl GameObjectDerived for Memory {
99    fn get_name(&self) -> GameString {
100        self.r#type.clone()
101    }
102
103    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
104        for part in self.participants.iter() {
105            collection.extend([E::from(part.1.clone().into())]);
106        }
107    }
108}
109
110impl Localizable for Memory {
111    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
112        self.r#type = localization.localize_query(&self.r#type, |stack| {
113            match stack.len() {
114                4 => {
115                    if stack[1].0 == "Var" {
116                        if stack[2].0 == "GetProvince" {
117                            if let Some(province_id) = self.variables.get(stack[1].1[0].as_str()) {
118                                if let MemoryVariable::Id(id) = province_id {
119                                    if let Some(province_name) = localization.lookup_title(id) {
120                                        if let Ok(province_name) =
121                                            localization.localize(province_name)
122                                        {
123                                            return Some(province_name);
124                                        }
125                                    }
126                                }
127                            } else {
128                                return Some(stack[1].1[0].clone().into());
129                            }
130                        } else if stack[2].0 == "Trait" {
131                            if let Some(trait_name) = self.variables.get(stack[1].1[0].as_str()) {
132                                if let MemoryVariable::String(trait_name) = trait_name {
133                                    return Some(trait_name.clone());
134                                }
135                            }
136                        }
137                    }
138                }
139                3 => {
140                    if stack[1].0 == "Var" {
141                        if let Some(var_name) = self.variables.get(stack[1].1[0].as_str()) {
142                            if let MemoryVariable::String(var_name) = var_name {
143                                return Some(var_name.clone());
144                            }
145                        }
146                    }
147                }
148                2 => {
149                    if stack[0].0 == "owner" {
150                        if stack[1].0 == "GetName" || stack[1].0 == "GetTitledFirstName" {
151                            return Some("".into());
152                        } else if stack[1].0 == "GetHerHis" {
153                            return Some("my".into());
154                        } else if stack[1].1[0] == "RelationToMeShort"
155                            || stack[1].1[1] == "RelationToMeShort"
156                        {
157                            if let Some(guy) =
158                                self.participants.get(stack[1].1[1].as_str().trim_start())
159                            {
160                                return Some(guy.get_internal().inner().unwrap().get_name());
161                            }
162                        }
163                    } else if stack[0].0 == "predecessor" {
164                        if stack[1].0 == "GetHerHis" {
165                            return Some("their".into());
166                        } else if stack[1].0 == "GetHerHim" {
167                            return Some("them".into());
168                        }
169                    } else if let Some(part) = self.participants.get(stack[0].0.as_str()) {
170                        if stack[1].0 == "GetName" || stack[1].0 == "GetTitledFirstName" {
171                            return Some(part.get_internal().inner().unwrap().get_name());
172                        } else if stack[1].0 == "GetHerHis" || stack[1].0 == "GetNamePossessive" {
173                            if part.get_internal().inner().unwrap().get_female() {
174                                return Some("Her".into());
175                            } else {
176                                return Some("His".into());
177                            }
178                        }
179                    } else if stack[1].0 == "GetName" || stack[1].0 == "GetTitledFirstName" {
180                        return Some(stack[0].1[0].clone().into());
181                    }
182                    if stack[1].0 == "Custom" {
183                        if stack[1].1[0] == "KnightCultureNoTooltip" {
184                            return Some("Knight".into());
185                        }
186                    }
187                }
188                _ => {}
189            }
190            None
191        })?;
192        Ok(())
193    }
194}
195
196impl Serialize for GameRef<Memory> {
197    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
198    where
199        S: serde::Serializer,
200    {
201        self.get_internal().serialize(serializer)
202    }
203}