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
63impl 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 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 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}