ck3_history_extractor/structures/
artifact.rs

1use jomini::common::Date;
2use serde::Serialize;
3
4use crate::types::Wrapper;
5
6use super::{
7    super::{
8        game_data::{GameData, Localizable, LocalizationError, Localize},
9        parser::{GameObjectMap, GameObjectMapping, GameState, ParsingError},
10        types::GameString,
11    },
12    Character, EntityRef, FromGameObject, GameObjectDerived, GameRef,
13};
14
15#[derive(Serialize)]
16pub struct Artifact {
17    name: GameString,
18    description: GameString,
19    r#type: GameString,
20    rarity: GameString,
21    quality: u32,
22    wealth: u32,
23    owner: GameRef<Character>,
24    history: Vec<(
25        GameString,
26        Date,
27        Option<GameRef<Character>>,
28        Option<GameRef<Character>>,
29    )>,
30}
31
32impl FromGameObject for Artifact {
33    fn from_game_object(
34        base: &GameObjectMap,
35        game_state: &mut GameState,
36    ) -> Result<Self, ParsingError> {
37        let mut artifact = Self {
38            name: base.get_string("name")?,
39            description: base.get_string("description")?,
40            r#type: base.get_string("type")?,
41            rarity: base.get_string("rarity")?,
42            quality: base
43                .get("quality")
44                .map_or(Ok(0), |n| n.as_integer().and_then(|v| Ok(v as u32)))?,
45            wealth: base
46                .get("wealth")
47                .map_or(Ok(0), |n| n.as_integer().and_then(|v| Ok(v as u32)))?,
48            owner: game_state.get_character(&base.get_game_id("owner")?),
49            history: Vec::default(),
50        };
51        if let Some(history_node) = base.get("history") {
52            let history_node = history_node.as_object()?;
53            if let Ok(map) = history_node.as_map() {
54                if let Some(entries_node) = map.get("entries") {
55                    for h in entries_node.as_object()?.as_array()? {
56                        let h = h.as_object()?.as_map()?;
57                        let actor = if let Some(actor_node) = h.get("actor") {
58                            Some(game_state.get_character(&actor_node.as_id()?))
59                        } else {
60                            None
61                        };
62                        let recipient = if let Some(recipient_node) = h.get("recipient") {
63                            Some(game_state.get_character(&recipient_node.as_id()?))
64                        } else {
65                            None
66                        };
67                        artifact.history.push((
68                            h.get_string("type")?,
69                            h.get_date("date")?,
70                            actor,
71                            recipient,
72                        ));
73                    }
74                }
75            }
76        }
77        Ok(artifact)
78    }
79}
80
81impl GameObjectDerived for Artifact {
82    fn get_name(&self) -> GameString {
83        self.name.clone()
84    }
85
86    fn get_references<E: From<EntityRef>, C: Extend<E>>(&self, collection: &mut C) {
87        for h in self.history.iter() {
88            if let Some(actor) = &h.2 {
89                collection.extend([E::from(actor.clone().into())]);
90            }
91            if let Some(recipient) = &h.3 {
92                collection.extend([E::from(recipient.clone().into())]);
93            }
94        }
95    }
96}
97
98fn handle_tooltips(text: &GameString) -> String {
99    let mut result = String::new();
100    let mut in_tooltip = false;
101    let mut in_tooltip_text = false;
102    for c in text.chars() {
103        match c {
104            '\x15' => {
105                // NAK character precedes a tooltip
106                in_tooltip = true;
107                in_tooltip_text = false;
108            }
109            ' ' => {
110                if in_tooltip && !in_tooltip_text {
111                    in_tooltip_text = true;
112                } else {
113                    result.push(c);
114                }
115            }
116            '!' => {
117                // NAK! character ends a tooltip? I think?
118                if in_tooltip {
119                    in_tooltip = false;
120                    in_tooltip_text = false;
121                } else {
122                    result.push(c);
123                }
124            }
125            _ => {
126                if !in_tooltip || in_tooltip_text {
127                    result.push(c);
128                }
129            }
130        }
131    }
132    return result;
133}
134
135impl Localizable for Artifact {
136    fn localize(&mut self, localization: &GameData) -> Result<(), LocalizationError> {
137        self.rarity = localization.localize(&self.rarity)?;
138        self.r#type = localization.localize("artifact_".to_string() + self.r#type.as_ref())?;
139        self.description = handle_tooltips(&self.description).into();
140        self.name = handle_tooltips(&self.name).into();
141        Ok(())
142    }
143}
144
145impl Serialize for GameRef<Artifact> {
146    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
147    where
148        S: serde::Serializer,
149    {
150        self.get_internal().serialize(serializer)
151    }
152}