homm5_scaner/entity/
creature.rs

1use super::{configure_path, CollectFiles, FileObjects, FileRef, FileStructure, Output, Scan};
2use quick_xml::{Reader, events::Event};
3use std::collections::HashMap;
4use homm5_types::creature::{CreatureVisual, AdvMapCreatureShared};
5
6pub struct CreatureFileCollector {}
7
8impl CollectFiles for CreatureFileCollector {
9    fn collect(&self, files: &HashMap<String, FileStructure>, collected_files: &mut Vec<(String, FileStructure)>){
10        let creatures_xdb = files.iter()
11            .find(|f| f.0 == "GameMechanics/RefTables/Creatures.xdb".to_lowercase().as_str())
12            .unwrap();
13        println!("creatures xdb pak - {}", &creatures_xdb.1.pak);
14        let mut buf = Vec::new();
15        let mut reader = Reader::from_str(creatures_xdb.1.content.as_str());
16        reader.trim_text(true);
17        reader.expand_empty_elements(true);
18        loop {
19            match reader.read_event_into(&mut buf) {
20                Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
21                Ok(Event::Eof) => break,
22                Ok(Event::Start(e)) => {
23                    match e.name().as_ref() {
24                        b"objects" => {
25                            let end = e.to_end().into_owned();
26                            let text = reader.read_text(end.name()).unwrap().to_string();
27                            let text = format!("<objects>{}</objects>", text);
28                            let creatures_de: Result<FileObjects, quick_xml::DeError> = quick_xml::de::from_str(&text);
29                            match creatures_de {
30                                Ok(creatures) => {
31                                    for creature in creatures.objects {
32                                        match creature.Obj {
33                                            Some(obj) => {
34                                                let creature_key = obj.href.as_ref().unwrap()
35                                                    .replace("#xpointer(/Creature)", "")
36                                                    .trim_start_matches("/")
37                                                    .to_lowercase();
38                                                let creature_entity = files.get(&creature_key);
39                                                match creature_entity {
40                                                    Some(entity) => {
41                                                        collected_files.push((creature_key.clone(), entity.clone()));
42                                                    },
43                                                    None => println!("Key {} is not in files", &creature_key)
44                                                }
45                                            }
46                                            None => {}
47                                        }
48                                    }
49                                },
50                                Err(e) => println!("Error deserializing creatures.xdb, {}", e.to_string())
51                            }
52                        }
53                        _=> {}
54                    }
55                }
56                _ => ()
57            }
58            buf.clear();
59        }
60    }
61}
62
63// impl ToJson for AdvMapCreatureShared {
64// }
65
66impl Output for AdvMapCreatureShared {
67    type ID = u16;
68    fn to_lua(&self, id: Option<u16>) -> String {
69        let is_generatable = if self.SubjectOfRandomGeneration == true {"1"} else {"nil"};
70        let is_flying = if self.Flying == true {"1"} else {"nil"};
71        let is_upgrade = if self.Upgrade == true {"1"} else {"nil"};
72        let mut abilities_string = String::new();
73        match &self.Abilities.Abilities {
74            Some(abilities) => {
75                for ability in abilities {
76                    abilities_string += &format!("{}, ", &ability);
77                }
78            },
79            None => {}
80        }
81        let mut spells_string = String::new();
82        match &self.KnownSpells.spells {
83            Some(spells) => {
84                for spell in spells {
85                    spells_string += &format!("[{}] = {}, ", &spell.Spell, &spell.Mastery);
86                }
87            },
88            None => {}
89
90        }
91        format!(
92            "\t[{}] = {{
93        is_generatable = {},
94        attack = {},
95        defence = {},
96        dmg_min = {},
97        dmg_max = {},
98        speed = {},
99        ini = {},
100        health = {},
101        sp = {},
102        size = {},
103        exp = {},
104        power = {},
105        town = {},
106        first_element = {},
107        second_element = {},
108        grow = {},
109        tier = {},
110        cost = {},
111        range = {},
112        name = \"{}\",
113        desc = \"{}\",
114        icon = \"{}\",
115        is_flying = {},
116        abilities = {{{}}},
117        known_spells = {{{}}},
118        is_upgrade = {}
119    }},\n", 
120            id.unwrap() - 1,
121            is_generatable, 
122            self.AttackSkill, 
123            self.DefenceSkill, 
124            self.MinDamage, 
125            self.MaxDamage,
126            self.Speed,
127            self.Initiative,
128            self.Health,
129            self.SpellPoints,
130            self.CombatSize,
131            self.Exp,
132            self.Power,
133            self.CreatureTown,
134            self.MagicElement.First,
135            self.MagicElement.Second,
136            self.WeeklyGrowth,
137            self.CreatureTier,
138            self.Cost.Gold,
139            self.Range,
140            self.VisualExplained.as_ref().unwrap().CreatureNameFileRef.as_ref().unwrap().href.as_ref().unwrap_or(&String::new()),
141            self.VisualExplained.as_ref().unwrap().DescriptionFileRef.as_ref().unwrap().href.as_ref().unwrap_or(&String::new()),
142            self.VisualExplained.as_ref().unwrap().Icon128.as_ref().unwrap().href.as_ref().unwrap_or(&String::new()),
143            is_flying,
144            abilities_string,
145            spells_string,
146            is_upgrade
147        )
148    }
149
150    fn to_json(&self) -> String {
151        serde_json::to_string_pretty(self).unwrap()
152    }
153}
154
155pub struct CreatureScaner {
156    pub id: u16
157}
158
159impl CreatureScaner {
160    fn check_visual(&self, file_key: &String, content: &String, files: &HashMap<String, FileStructure>) -> Option<CreatureVisual> {
161        let mut buf = Vec::new();
162        let mut reader = Reader::from_str(&content);
163        reader.trim_text(true);
164        reader.expand_empty_elements(true);
165        loop {
166            match reader.read_event_into(&mut buf) {
167                Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
168                Ok(Event::Eof) => break None,
169                Ok(Event::Start(e)) => {
170                    match e.name().as_ref() {
171                        b"CreatureVisual" => {
172                            let end = e.to_end().into_owned();
173                            let possible_text = reader.read_text(end.name());
174                            match possible_text {
175                                Ok(text) => {
176                                    let text = text.to_string();
177                                    let de_res: Result<CreatureVisual, quick_xml::DeError> = quick_xml::de::from_str(&format!("<CreatureVisual>{}</CreatureVisual>", text));
178                                    match de_res {
179                                        Ok(visual) => {
180                                            let name = configure_path(visual.CreatureNameFileRef.as_ref().unwrap().href.as_ref(), file_key, files);
181                                            let desc = configure_path(visual.DescriptionFileRef.as_ref().unwrap().href.as_ref(), file_key, files);
182                                            let icon_key = visual.Icon128.as_ref().unwrap().href.as_ref().unwrap_or(&String::new()).replace("#xpointer(/Texture)", "");
183                                            let icon = configure_path(Some(&icon_key), file_key, files);
184                                            break Some(CreatureVisual { 
185                                                CreatureNameFileRef: Some(FileRef { href: Some(name) }), 
186                                                DescriptionFileRef: Some(FileRef { href: Some(desc) }), 
187                                                Icon128: Some(FileRef { href: Some(icon) }) 
188                                            })
189                                        }
190                                        Err(e) => {
191                                            println!("error while deserializing file key {}, {:?}", file_key, e.to_string());
192                                        }
193                                    }
194                                },
195                                Err(_e) => println!("error reading file content: {}", file_key)
196                            }
197                        }
198                        _=> {}
199                    }
200                }
201                _ => ()
202            }
203            buf.clear();
204        }
205    }
206}
207
208impl Scan<u16> for CreatureScaner {
209    fn scan(&mut self, file_key: &String, entity: &String, files: &HashMap<String, FileStructure>) -> Option<Box<dyn Output<ID = u16>>> {
210        let mut buf = Vec::new();
211        let mut reader = Reader::from_str(entity);
212        reader.trim_text(true);
213        reader.expand_empty_elements(true);
214        loop {
215            match reader.read_event_into(&mut buf) {
216                Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
217                Ok(Event::Eof) => break None,
218                Ok(Event::Start(e)) => {
219                    match e.name().as_ref() {
220                        b"Creature" => {
221                            let end = e.to_end().into_owned();
222                            let possible_text = reader.read_text(end.name());
223                            match possible_text {
224                                Ok(text) => {
225                                    let text = text.to_string();
226                                    let de_res: Result<AdvMapCreatureShared, quick_xml::DeError> = quick_xml::de::from_str(
227                                        &format!("<Creature>{}</Creature>", text)
228                                    );
229                                    match de_res {
230                                        Ok(mut creature) => {
231                                            //println!("Creature scanned: {:?}", &creature);
232                                            let visual = creature.Visual.as_ref();
233                                            match visual {
234                                                Some(actual_visual) => {
235                                                    let visual_key = actual_visual.href.as_ref().unwrap()
236                                                        .replace("#xpointer(/CreatureVisual)", "")
237                                                        .trim_start_matches("/")
238                                                        .to_lowercase();
239                                                    println!("visual key: {}", &visual_key);
240                                                    let actual_visual_key = configure_path(Some(&visual_key), file_key, files);
241                                                    let visual_file = files.get(&actual_visual_key);
242                                                    match visual_file {
243                                                        Some(actual_visual_file) => {
244                                                            let visual_checked = self.check_visual(&actual_visual_key, &actual_visual_file.content, files);
245                                                            creature.VisualExplained = visual_checked;
246                                                        },
247                                                        None => println!("Can't find visual of {}", &actual_visual_key)
248                                                    }
249                                                },
250                                                None => {}
251                                            }
252                                            //println!("Creature's visual: {:?}", &creature.VisualExplained);
253                                            self.id+=1;
254                                            break Some(Box::new(creature));
255                                        }
256                                        Err(e) => {
257                                            println!("error while deserializing file key {}, {:?}", file_key, e.to_string());
258                                        }
259                                    }
260                                },
261                                Err(_e) => println!("error reading file content: {}", file_key)
262                            }
263                        }
264                        _=> {}
265                    }
266                }
267                _ => ()
268            }
269            buf.clear();
270        }
271    }
272
273    fn get_id(&self) -> Option<u16> {
274        Some(self.id)
275    }
276}