ck3_history_extractor/structures/
player.rs1use image::{
2 buffer::ConvertBuffer,
3 codecs::gif::{GifEncoder, Repeat},
4 Delay, Frame, Rgb,
5};
6
7use jomini::common::PdsDate;
8use serde::Serialize;
9
10use super::{
11 super::{
12 display::{GetPath, Grapher, Renderable},
13 game_data::{GameData, Localizable, LocalizationError, MapGenerator, MapImage},
14 jinja_env::H_TEMPLATE_NAME,
15 parser::{GameObjectMap, GameObjectMapping, GameState, ParsingError},
16 types::{GameString, Wrapper},
17 },
18 Character, EntityRef, FromGameObject, GameObjectDerived, GameRef, LineageNode,
19};
20
21use std::{
22 collections::HashSet,
23 fs::File,
24 ops::Deref,
25 path::{Path, PathBuf},
26};
27
28const TARGET_COLOR: Rgb<u8> = Rgb([70, 255, 70]);
29const SECONDARY_COLOR: Rgb<u8> = Rgb([255, 255, 70]);
30const BASE_COLOR: Rgb<u8> = Rgb([255, 255, 255]);
31
32#[derive(Serialize)]
34pub struct Player {
35 name: GameString,
36 character: Option<GameRef<Character>>,
37 lineage: Vec<LineageNode>,
38}
39
40impl FromGameObject for Player {
41 fn from_game_object(
42 base: &GameObjectMap,
43 game_state: &mut GameState,
44 ) -> Result<Self, ParsingError> {
45 let mut player = Self {
46 name: base.get_string("name")?,
47 character: Some(
48 game_state
49 .get_character(&base.get_game_id("character")?)
50 .clone(),
51 ),
52 lineage: Vec::new(),
53 };
54 for leg in base.get_object("legacy")?.as_array()? {
55 player.lineage.push(LineageNode::from_game_object(
56 leg.as_object()?.as_map()?,
57 game_state,
58 )?)
59 }
60 Ok(player)
61 }
62}
63
64impl GameObjectDerived for Player {
65 fn get_name(&self) -> GameString {
66 self.name.clone()
67 }
68
69 fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
70 collection.extend([E::from(self.character.as_ref().unwrap().clone().into())]);
71 for node in self.lineage.iter() {
72 collection.extend([E::from(node.get_character().clone().into())]);
73 }
74 }
75}
76
77impl GetPath for Player {
78 fn get_path(&self, path: &Path) -> PathBuf {
79 path.join("index.html")
80 }
81}
82
83impl Renderable for Player {
84 fn get_template() -> &'static str {
85 H_TEMPLATE_NAME
86 }
87
88 fn render(
89 &self,
90 path: &Path,
91 game_state: &GameState,
92 grapher: Option<&Grapher>,
93 data: &GameData,
94 ) {
95 if let Some(map) = data.get_map() {
96 let mut file = File::create(path.join("timelapse.gif")).unwrap();
98 let mut gif_encoder = GifEncoder::new(&mut file);
99 for char in self.lineage.iter() {
100 if let Some(char) = char.get_character().get_internal().inner() {
108 let death_date = char.get_death_date();
111 let date = if let Some(death_date) = &death_date {
112 death_date.iso_8601()
113 } else {
114 game_state.get_current_date().unwrap().iso_8601()
115 };
116 let mut barony_map =
117 map.create_map_flat(char.get_barony_keys(true), TARGET_COLOR);
118 barony_map.draw_text(date.to_string());
119 let fbytes = barony_map.convert();
120 let width = fbytes.width();
122 let height = fbytes.height();
123 let frame = Frame::from_parts(
124 fbytes,
125 width,
126 height,
127 Delay::from_numer_denom_ms(3000, 1),
128 );
129 gif_encoder.encode_frame(frame).unwrap();
130 }
131 }
132 gif_encoder.set_repeat(Repeat::Infinite).unwrap();
133 let mut direct_titles = HashSet::new();
134 let mut descendant_title = HashSet::new();
135 let first = self.lineage.first().unwrap().get_character();
136 if let Some(first) = first.get_internal().inner() {
137 let dynasty = first.get_house();
138 let dynasty = dynasty.as_ref().unwrap().get_internal();
139 for desc in dynasty
140 .inner()
141 .unwrap()
142 .get_founder()
143 .get_internal()
144 .inner()
145 .unwrap()
146 .get_descendants()
147 {
148 if let Some(desc) = desc.get_internal().inner() {
149 if desc.get_death_date().is_some() {
150 continue;
151 }
152 let target = if desc.get_house().map_or(false, |d| {
153 d.get_internal()
154 .inner()
155 .unwrap()
156 .get_dynasty()
157 .get_internal()
158 .deref()
159 == dynasty
160 .inner()
161 .unwrap()
162 .get_dynasty()
163 .get_internal()
164 .deref()
165 }) {
166 &mut direct_titles
167 } else {
168 &mut descendant_title
169 };
170 for title in desc.get_barony_keys(false) {
171 target.insert(title.clone());
172 }
173 }
174 }
175 }
176 let mut dynasty_map = map.create_map::<_, _, Vec<GameString>>(
177 |key: &str| {
178 if direct_titles.contains(key) {
179 return TARGET_COLOR;
180 } else if descendant_title.contains(key) {
181 return SECONDARY_COLOR;
182 } else {
183 return BASE_COLOR;
184 }
185 },
186 None,
187 );
188 dynasty_map.draw_legend([
189 ("Dynastic titles".to_string(), TARGET_COLOR),
190 ("Descendant titles".to_string(), SECONDARY_COLOR),
191 ]);
192 dynasty_map.save_in_thread(path.join("dynastyMap.png"));
193 }
194 if let Some(grapher) = grapher {
195 let last = self.lineage.last().unwrap().get_character();
196 grapher.create_tree_graph(last, true, &path.join("line.svg"));
197 }
198 }
199}
200
201impl Localizable for Player {
202 fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
203 for node in self.lineage.iter_mut() {
204 node.localize(localization)?;
205 }
206 Ok(())
207 }
208}