ck3_history_extractor/structures/
culture.rs

1use std::path::Path;
2
3use jomini::common::Date;
4use serde::Serialize;
5
6use super::{
7    super::{
8        display::{Grapher, ProceduralPath, Renderable, TreeNode},
9        game_data::{GameData, Localizable, LocalizationError, Localize, MapGenerator, MapImage},
10        jinja_env::CUL_TEMPLATE_NAME,
11        parser::{GameObjectMap, GameObjectMapping, GameState, ParsingError},
12        types::{GameString, Wrapper, WrapperMut},
13    },
14    EntityRef, FromGameObject, GameObjectDerived, GameObjectEntity, GameRef, Title,
15};
16
17/// A struct representing a culture in the game
18#[derive(Serialize)]
19pub struct Culture {
20    name: GameString,
21    ethos: Option<GameString>,
22    heritage: GameString,
23    martial: GameString,
24    date: Option<Date>,
25    children: Vec<GameRef<Culture>>,
26    parents: Vec<GameRef<Culture>>,
27    traditions: Vec<GameString>,
28    language: Option<GameString>,
29    eras: Vec<(GameString, Option<u16>, Option<u16>)>,
30    // TODO innovations
31}
32
33impl FromGameObject for Culture {
34    fn from_game_object(
35        base: &GameObjectMap,
36        game_state: &mut GameState,
37    ) -> Result<Self, ParsingError> {
38        let mut culture = Self {
39            name: base.get_string("name")?,
40            //this is possible, shoutout u/Kinc4id
41            ethos: base.get("ethos").map(|n| n.as_string()).transpose()?,
42            heritage: base.get_string("heritage")?,
43            martial: base.get_string("martial_custom")?,
44            date: base.get("created").map(|n| n.as_date()).transpose()?,
45            language: None,
46            traditions: base
47                .get("traditions")
48                .map(|n| n.as_object().and_then(|obj| obj.as_array()))
49                .transpose()?
50                .map_or(Vec::new(), |n| {
51                    n.iter().filter_map(|t| t.as_string().ok()).collect()
52                }),
53            children: Vec::new(),
54            parents: Vec::new(),
55            eras: Vec::with_capacity(4),
56        };
57        if let Some(language) = base.get("language") {
58            culture.language = Some(language.as_string()?);
59        }
60
61        if let Some(parents_obj) = base.get("parents") {
62            for p in parents_obj.as_object()?.as_array()? {
63                culture.parents.push(game_state.get_culture(&p.as_id()?));
64            }
65        }
66        if let Some(era_data) = base.get("culture_era_data") {
67            for era in era_data.as_object()?.as_array()? {
68                let obj = era.as_object()?.as_map()?;
69                culture.eras.push((
70                    obj.get_string("type")?,
71                    obj.get("join")
72                        .and_then(|n| n.as_integer().ok().and_then(|n| Some(n as u16))),
73                    obj.get("left")
74                        .and_then(|n| n.as_integer().ok().and_then(|n| Some(n as u16))),
75                ));
76            }
77        }
78        return Ok(culture);
79    }
80
81    fn finalize(&mut self, reference: &GameRef<Culture>) {
82        for p in &self.parents {
83            if let Ok(mut r) = p.try_get_internal_mut() {
84                if let Some(parent) = r.inner_mut() {
85                    parent.register_child(reference.clone());
86                }
87            }
88        }
89
90        if self.language.is_none() {
91            for p in &self.parents {
92                if let Some(lang) = p.get_internal().inner().unwrap().get_language() {
93                    self.language = Some(lang);
94                    break;
95                }
96            }
97        }
98    }
99}
100
101impl GameObjectDerived for Culture {
102    fn get_name(&self) -> GameString {
103        self.name.clone()
104    }
105
106    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
107        for p in &self.parents {
108            collection.extend([E::from(p.clone().into())]);
109        }
110        for c in &self.children {
111            collection.extend([E::from(c.clone().into())]);
112        }
113    }
114}
115
116impl TreeNode<Vec<GameRef<Culture>>> for Culture {
117    fn get_children(&self) -> Option<Vec<GameRef<Culture>>> {
118        if self.children.is_empty() {
119            return None;
120        }
121        Some(self.children.clone())
122    }
123
124    fn get_parent(&self) -> Option<Vec<GameRef<Culture>>> {
125        if self.parents.is_empty() {
126            return None;
127        }
128        Some(self.parents.clone())
129    }
130
131    fn get_class(&self) -> Option<GameString> {
132        Some(self.heritage.clone())
133    }
134}
135
136impl Culture {
137    pub fn register_child(&mut self, child: GameRef<Culture>) {
138        self.children.push(child);
139    }
140
141    pub fn get_language(&self) -> Option<GameString> {
142        self.language.clone()
143    }
144}
145
146impl ProceduralPath for Culture {
147    fn get_subdir() -> &'static str {
148        "cultures"
149    }
150}
151
152impl Renderable for GameObjectEntity<Culture> {
153    fn get_template() -> &'static str {
154        CUL_TEMPLATE_NAME
155    }
156
157    fn render(
158        &self,
159        path: &Path,
160        game_state: &GameState,
161        grapher: Option<&Grapher>,
162        data: &GameData,
163    ) {
164        if let Some(grapher) = grapher {
165            let mut path = path.join(Culture::get_subdir());
166            path.push(self.id.to_string() + ".svg");
167            grapher.create_culture_graph(self.id, &path);
168        }
169        if let Some(map) = data.get_map() {
170            let filter = |title: &Title| {
171                if let Title::County { culture, .. } = title {
172                    if let Some(culture) = culture {
173                        return culture.get_internal().id == self.id;
174                    }
175                }
176                return false;
177            };
178            let keys = game_state.get_baronies_of_counties(filter);
179            if !keys.is_empty() {
180                let mut path = path.join(Culture::get_subdir());
181                path.push(self.id.to_string() + ".png");
182                let mut culture_map = map.create_map_flat(keys, [70, 255, 70]);
183                if let Some(inner) = self.inner() {
184                    culture_map.draw_text(format!("Map of the {} culture", &inner.name));
185                }
186                culture_map.save_in_thread(&path);
187            }
188        }
189    }
190}
191
192impl Localizable for Culture {
193    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
194        self.name = localization.localize(&self.name)?;
195        if let Some(eth) = &self.ethos {
196            self.ethos = Some(localization.localize(eth.to_string() + "_name")?);
197        }
198        self.heritage = localization.localize(self.heritage.to_string() + "_name")?;
199        self.martial = localization.localize(self.martial.to_string() + "_name")?;
200        if let Some(language) = &self.language {
201            self.language = Some(localization.localize(language.to_string() + "_name")?);
202        }
203        for t in &mut self.traditions {
204            *t = localization.localize(t.to_string() + "_name")?;
205        }
206        for (era, _, _) in &mut self.eras {
207            *era = localization.localize(era.to_string())?;
208        }
209        Ok(())
210    }
211}