1 /*
2 Copyright © 2015 Igor Paliychuk
3
4 This file is part of FLARE.
5
6 FLARE is free software: you can redistribute it and/or modify it under the terms
7 of the GNU General Public License as published by the Free Software Foundation,
8 either version 3 of the License, or (at your option) any later version.
9
10 FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along with
15 FLARE. If not, see http://www.gnu.org/licenses/
16 */
17
18 #ifdef FLARE_MAP_SAVER
19
20 #include "MapSaver.h"
21 #include "Settings.h"
22
MapSaver(Map * _map)23 MapSaver::MapSaver(Map *_map) : map(_map)
24 {
25 EVENT_COMPONENT_NAME[EC::TOOLTIP] = "tooltip";
26 EVENT_COMPONENT_NAME[EC::POWER_PATH] = "power_path";
27 EVENT_COMPONENT_NAME[EC::POWER_DAMAGE] = "power_damage";
28 EVENT_COMPONENT_NAME[EC::INTERMAP] = "intermap";
29 EVENT_COMPONENT_NAME[EC::INTRAMAP] = "intramap";
30 EVENT_COMPONENT_NAME[EC::MAPMOD] = "mapmod";
31 EVENT_COMPONENT_NAME[EC::SOUNDFX] = "soundfx";
32 EVENT_COMPONENT_NAME[EC::LOOT] = "loot"; // HALF-IMPLEMENTED
33 EVENT_COMPONENT_NAME[EC::LOOT_COUNT] = "loot_count"; // UNIMPLEMENTED
34 EVENT_COMPONENT_NAME[EC::MSG] = "msg";
35 EVENT_COMPONENT_NAME[EC::SHAKYCAM] = "shakycam";
36 EVENT_COMPONENT_NAME[EC::REQUIRES_STATUS] = "requires_status";
37 EVENT_COMPONENT_NAME[EC::REQUIRES_NOT_STATUS] = "requires_not_status";
38 EVENT_COMPONENT_NAME[EC::REQUIRES_LEVEL] = "requires_level";
39 EVENT_COMPONENT_NAME[EC::REQUIRES_NOT_LEVEL] = "requires_not_level";
40 EVENT_COMPONENT_NAME[EC::REQUIRES_CURRENCY] = "requires_currency";
41 EVENT_COMPONENT_NAME[EC::REQUIRES_NOT_CURRENCY] = "requires_not_currency";
42 EVENT_COMPONENT_NAME[EC::REQUIRES_ITEM] = "requires_item";
43 EVENT_COMPONENT_NAME[EC::REQUIRES_NOT_ITEM] = "requires_not_item";
44 EVENT_COMPONENT_NAME[EC::REQUIRES_CLASS] = "requires_class";
45 EVENT_COMPONENT_NAME[EC::REQUIRES_NOT_CLASS] = "requires_not_class";
46 EVENT_COMPONENT_NAME[EC::SET_STATUS] = "set_status";
47 EVENT_COMPONENT_NAME[EC::UNSET_STATUS] = "unset_status";
48 EVENT_COMPONENT_NAME[EC::REMOVE_CURRENCY] = "remove_currency";
49 EVENT_COMPONENT_NAME[EC::REMOVE_ITEM] = "remove_item";
50 EVENT_COMPONENT_NAME[EC::REWARD_XP] = "reward_xp";
51 EVENT_COMPONENT_NAME[EC::REWARD_CURRENCY] = "reward_currency";
52 EVENT_COMPONENT_NAME[EC::REWARD_ITEM] = "reward_item";
53 EVENT_COMPONENT_NAME[EC::RESTORE] = "restore";
54 EVENT_COMPONENT_NAME[EC::POWER] = "power";
55 EVENT_COMPONENT_NAME[EC::SPAWN] = "spawn";
56 EVENT_COMPONENT_NAME[EC::STASH] = "stash";
57 EVENT_COMPONENT_NAME[EC::NPC] = "npc";
58 EVENT_COMPONENT_NAME[EC::MUSIC] = "music";
59 EVENT_COMPONENT_NAME[EC::CUTSCENE] = "cutscene";
60 EVENT_COMPONENT_NAME[EC::REPEAT] = "repeat";
61 EVENT_COMPONENT_NAME[EC::SAVE_GAME] = "save_game";
62 EVENT_COMPONENT_NAME[EC::BOOK] = "book";
63 EVENT_COMPONENT_NAME[EC::SCRIPT] = "script";
64 EVENT_COMPONENT_NAME[EC::CHANCE_EXEC] = "chance_exec";
65 EVENT_COMPONENT_NAME[EC::RESPEC] = "respec";
66
67 dest_file = map->getFilename();
68 }
69
70
~MapSaver()71 MapSaver::~MapSaver()
72 {
73 }
74
75 /*
76 * tileset_definitions is a string, containing tileset description
77 * for editing map in Tiled Editor. This data is not present in map,
78 * loaded by game, so when saving map from game tileset_definitions
79 * should be empty string
80 */
saveMap(std::string tileset_definitions)81 bool MapSaver::saveMap(std::string tileset_definitions)
82 {
83 std::ofstream outfile;
84
85 outfile.open(dest_file.c_str(), std::ios::out);
86
87 if (outfile.is_open()) {
88
89 outfile << "## flare-engine generated map file ##" << "\n";
90
91 writeHeader(outfile);
92 writeTilesets(outfile, tileset_definitions);
93 writeLayers(outfile);
94
95 writeEvents(outfile);
96 writeNPCs(outfile);
97 writeEnemies(outfile);
98
99 if (outfile.bad())
100 {
101 logError("MapSaver: Unable to save the map. No write access or disk is full!");
102 return false;
103 }
104 outfile.close();
105 outfile.clear();
106
107 return true;
108 }
109 else {
110 logError("MapSaver: Could not open %s for writing", dest_file.c_str());
111 }
112 return false;
113 }
114
saveMap(std::string file,std::string tileset_definitions)115 bool MapSaver::saveMap(std::string file, std::string tileset_definitions)
116 {
117 dest_file = file;
118
119 return saveMap(tileset_definitions);
120 }
121
122
writeHeader(std::ofstream & map_file)123 void MapSaver::writeHeader(std::ofstream& map_file)
124 {
125 map_file << "[header]" << std::endl;
126 map_file << "width=" << map->w << std::endl;
127 map_file << "height=" << map->h << std::endl;
128 map_file << "tilewidth=" << "64" << std::endl;
129 map_file << "tileheight=" << "32" << std::endl;
130 map_file << "orientation=" << "isometric" << std::endl;
131 map_file << "music=" << map->music_filename << std::endl;
132 map_file << "tileset=" << map->getTileset() << std::endl;
133 map_file << "title=" << map->title << std::endl;
134 map_file << "hero_pos" << static_cast<int>(map->hero_pos.x) << "," << static_cast<int>(map->hero_pos.y) << std::endl;
135
136 map_file << std::endl;
137 }
138
writeTilesets(std::ofstream & map_file,std::string tileset_definitions)139 void MapSaver::writeTilesets(std::ofstream& map_file, std::string tileset_definitions)
140 {
141 map_file << "[tilesets]" << std::endl;
142
143 map_file << tileset_definitions << std::endl;
144
145 map_file << std::endl;
146 }
147
148
writeLayers(std::ofstream & map_file)149 void MapSaver::writeLayers(std::ofstream& map_file)
150 {
151 for (unsigned short i = 0; i < map->layernames.size(); i++)
152 {
153 map_file << "[layer]" << std::endl;
154
155 map_file << "type=" << map->layernames[i] << std::endl;
156 map_file << "data=" << std::endl;
157
158 std::string layer = "";
159 for (int line = 0; line < map->h; line++)
160 {
161 std::stringstream map_row;
162 for (int tile = 0; tile < map->w; tile++)
163 {
164 map_row << map->layers[i][tile][line] << ",";
165 }
166 layer += map_row.str();
167 layer += '\n';
168 }
169 layer.erase(layer.end()-2, layer.end());
170 layer += '\n';
171
172 map_file << layer << std::endl;
173 }
174 }
175
176
writeEnemies(std::ofstream & map_file)177 void MapSaver::writeEnemies(std::ofstream& map_file)
178 {
179 std::queue<Map_Group> group = map->enemy_groups;
180
181 while (!group.empty())
182 {
183 map_file << "[enemy]" << std::endl;
184
185 if (group.front().type == "")
186 {
187 map_file << "type=enemy" << std::endl;
188 }
189 else
190 {
191 map_file << "type=" << group.front().type << std::endl;
192 }
193
194 map_file << "location=" << group.front().pos.x << "," << group.front().pos.y << "," << group.front().area.x << "," << group.front().area.y << std::endl;
195
196 map_file << "category=" << group.front().category << std::endl;
197
198 if (group.front().levelmin != 0 || group.front().levelmax != 0)
199 {
200 map_file << "level=" << group.front().levelmin << "," << group.front().levelmax << std::endl;
201 }
202
203 if (group.front().numbermin != 1 || group.front().numbermax != 1)
204 {
205 map_file << "number=" << group.front().numbermin << "," << group.front().numbermax << std::endl;
206 }
207
208 if (group.front().chance != 1.0f)
209 {
210 map_file << "chance=" << group.front().chance*100 << std::endl;
211 }
212
213 if (group.front().direction != -1)
214 {
215 map_file << "direction=" << group.front().direction << std::endl;
216 }
217
218 if (!group.front().waypoints.empty() && group.front().wander_radius == 0)
219 {
220 map_file << "waypoints=";
221 std::queue<FPoint> points = group.front().waypoints;
222 while (!points.empty())
223 {
224 map_file << points.front().x - 0.5f << "," << points.front().y - 0.5f;
225 points.pop();
226 if (!points.empty())
227 {
228 map_file << ";";
229 }
230 }
231 map_file << std::endl;
232 }
233
234 if ((group.front().wander_radius != 4 && group.front().waypoints.empty()))
235 {
236 map_file << "wander_radius=" << group.front().wander_radius << std::endl;
237 }
238
239 for (unsigned i = 0; i < group.front().requires_status.size(); i++)
240 {
241 map_file << "requires_status=" << group.front().requires_status[i] << std::endl;
242 }
243
244 for (unsigned i = 0; i < group.front().requires_status.size(); i++)
245 {
246 map_file << "requires_not_status=" << group.front().requires_not_status[i] << std::endl;
247 }
248
249 map_file << std::endl;
250 group.pop();
251 }
252 }
253
254
writeNPCs(std::ofstream & map_file)255 void MapSaver::writeNPCs(std::ofstream& map_file)
256 {
257 std::queue<Map_NPC> npcs = map->npcs;
258
259 while (!npcs.empty())
260 {
261 map_file << "[npc]" << std::endl;
262
263 if (npcs.front().type == "")
264 {
265 map_file << "type=npc" << std::endl;
266 }
267 else
268 {
269 map_file << "type=" << npcs.front().type << std::endl;
270 }
271
272 map_file << "location=" << npcs.front().pos.x - 0.5f << "," << npcs.front().pos.y - 0.5f << ",1,1" << std::endl;
273 map_file << "filename=" << npcs.front.id << std::endl;
274
275 for (unsigned j = 0; j < npcs.front().requires_status.size(); j++)
276 {
277 map_file << "requires_status=" << npcs.front().requires_status[j] << std::endl;
278 }
279 for (unsigned j = 0; j < npcs.front().requires_not_status.size(); j++)
280 {
281 map_file << "requires_not_status=" << npcs.front().requires_not_status[j] << std::endl;
282 }
283
284 map_file << std::endl;
285
286 npcs.pop();
287 }
288 }
289
writeEvents(std::ofstream & map_file)290 void MapSaver::writeEvents(std::ofstream& map_file)
291 {
292 for (unsigned i = 0; i < map->events.size(); i++)
293 {
294 map_file << "[event]" << std::endl;
295
296 if (map->events[i].type == "")
297 {
298 map_file << "type=event" << std::endl;
299 }
300 else
301 {
302 map_file << "type=" << map->events[i].type << std::endl;
303 }
304
305 Rect location = map->events[i].location;
306 map_file << "location=" << location.x << "," << location.y << "," << location.w << "," << location.h << std::endl;
307
308 if (map->events[i].activate_type == EVENT_ON_TRIGGER)
309 {
310 map_file << "activate=on_trigger" << std::endl;
311 }
312 else if (map->events[i].activate_type == EVENT_ON_MAPEXIT)
313 {
314 map_file << "activate=on_mapexit" << std::endl;
315 }
316 else if (map->events[i].activate_type == EVENT_ON_LEAVE)
317 {
318 map_file << "activate=on_leave" << std::endl;
319 }
320 else if (map->events[i].activate_type == EVENT_ON_LOAD)
321 {
322 map_file << "activate=on_load" << std::endl;
323 }
324 else if (map->events[i].activate_type == EVENT_ON_CLEAR)
325 {
326 map_file << "activate=on_clear" << std::endl;
327 }
328
329 Rect hotspot = map->events[i].hotspot;
330 if (hotspot.x == location.x && hotspot.y == location.y && hotspot.w == location.w && hotspot.h == location.h)
331 {
332 map_file << "hotspot=" << "location" << std::endl;
333 }
334 else if (hotspot.x != 0 && hotspot.y != 0 && hotspot.w != 0 && hotspot.h != 0)
335 {
336 map_file << "hotspot=" << hotspot.x << "," << hotspot.y << "," << hotspot.w << "," << hotspot.h << std::endl;
337 }
338
339 if (map->events[i].cooldown != 0)
340 {
341 std::string suffix = "ms";
342 int value = static_cast<int>(1000.f * map->events[i].cooldown / MAX_FRAMES_PER_SEC);
343 if (value % 1000 == 0)
344 {
345 value = map->events[i].cooldown / MAX_FRAMES_PER_SEC;
346 suffix = "s";
347 }
348 map_file << "cooldown=" << value << suffix << std::endl;
349 }
350
351 Rect reachable_from = map->events[i].reachable_from;
352 if (reachable_from.x != 0 && reachable_from.y != 0 && reachable_from.w != 0 && reachable_from.h != 0)
353 {
354 map_file << "reachable_from=" << reachable_from.x << "," << reachable_from.y << "," << reachable_from.w << "," << reachable_from.h << std::endl;
355 }
356 writeEventComponents(map_file, i);
357
358 map_file << std::endl;
359 }
360 }
361
writeEventComponents(std::ofstream & map_file,int eventID)362 void MapSaver::writeEventComponents(std::ofstream &map_file, int eventID)
363 {
364 std::vector<Event_Component> components = map->events[eventID].components;
365 for (unsigned i = 0; i < components.size(); i++)
366 {
367 Event_Component e = components[i];
368
369 if (e.type > 0 && e.type < EC_COUNT)
370 {
371 map_file << EVENT_COMPONENT_NAME[e.type] << "=";
372 }
373 else
374 {
375 continue;
376 }
377
378 if (e.type == EC::TOOLTIP) {
379 map_file << e.s << std::endl;
380 }
381 else if (e.type == EC::POWER_PATH) {
382 map_file << e.x << "," << e.y << ",";
383 if (e.s == "hero")
384 {
385 map_file << e.s << std::endl;
386 }
387 else
388 {
389 map_file << e.a << "," << e.b << std::endl;
390 }
391 }
392 else if (e.type == EC::POWER_DAMAGE) {
393 map_file << e.a << "," << e.b << std::endl;
394 }
395 else if (e.type == EC::INTERMAP) {
396 map_file << e.s << "," << e.x << "," << e.y << std::endl;
397 }
398 else if (e.type == EC::INTRAMAP) {
399 map_file << e.x << "," << e.y << std::endl;
400 }
401 else if (e.type == EC::MAPMOD) {
402 map_file << e.s << "," << e.x << "," << e.y << "," << e.z;
403
404 while (i+1 < components.size() && components[i+1].type == EC::MAPMOD)
405 {
406 i++;
407 e = components[i];
408 map_file << ";" << e.s << "," << e.x << "," << e.y << "," << e.z;
409 }
410 map_file << std::endl;
411 }
412 else if (e.type == EC::SOUNDFX) {
413 map_file << e.s;
414 if (e.x != -1 && e.y != -1)
415 {
416 map_file << "," << e.x << "," << e.y;
417 }
418 map_file << std::endl;
419 }
420 else if (e.type == EC::LOOT) {
421
422 std::stringstream chance;
423
424 if (e.z == 0)
425 chance << "fixed";
426 else
427 chance << e.z;
428
429 map_file << e.s << "," << chance.str() << "," << e.a << "," << e.b;
430
431 while (i+1 < components.size() && components[i+1].type == EC::LOOT)
432 {
433 i++;
434 e = components[i];
435
436 if (e.z == 0)
437 chance << "fixed";
438 else
439 chance << e.z;
440
441 map_file << ";" << e.s << "," << chance.str() << "," << e.a << "," << e.b;
442 }
443 map_file << std::endl;
444 // UNIMPLEMENTED
445 // Loot tables not supported
446 }
447 else if (e.type == EC::LOOT_COUNT) {
448 // UNIMPLEMENTED
449 // Loot count not supported
450 map_file << std::endl;
451 }
452 else if (e.type == EC::MSG) {
453 map_file << e.s << std::endl;
454 }
455 else if (e.type == EC::SHAKYCAM) {
456 std::string suffix = "ms";
457 int value = static_cast<int>(1000.f * e.x / MAX_FRAMES_PER_SEC);
458 if (value % 1000 == 0)
459 {
460 value = e.x / MAX_FRAMES_PER_SEC;
461 suffix = "s";
462 }
463 map_file << value << suffix << std::endl;
464 }
465 else if (e.type == EC::REQUIRES_STATUS) {
466 map_file << e.s;
467
468 while (i+1 < components.size() && components[i+1].type == EC::REQUIRES_STATUS)
469 {
470 i++;
471 e = components[i];
472 map_file << ";" << e.s;
473 }
474 map_file << std::endl;
475 }
476 else if (e.type == EC::REQUIRES_NOT_STATUS) {
477 map_file << e.s;
478
479 while (i+1 < components.size() && components[i+1].type == EC::REQUIRES_NOT_STATUS)
480 {
481 i++;
482 e = components[i];
483 map_file << ";" << e.s;
484 }
485 map_file << std::endl;
486 }
487 else if (e.type == EC::REQUIRES_LEVEL) {
488 map_file << e.x << std::endl;
489 }
490 else if (e.type == EC::REQUIRES_NOT_LEVEL) {
491 map_file << e.x << std::endl;
492 }
493 else if (e.type == EC::REQUIRES_CURRENCY) {
494 map_file << e.x << std::endl;
495 }
496 else if (e.type == EC::REQUIRES_NOT_CURRENCY) {
497 map_file << e.x << std::endl;
498 }
499 else if (e.type == EC::REQUIRES_ITEM) {
500 map_file << e.x;
501
502 while (i+1 < components.size() && components[i+1].type == EC::REQUIRES_ITEM)
503 {
504 i++;
505 e = components[i];
506 map_file << "," << e.x;
507 }
508 map_file << std::endl;
509 }
510 else if (e.type == EC::REQUIRES_NOT_ITEM) {
511 map_file << e.x;
512
513 while (i+1 < components.size() && components[i+1].type == EC::REQUIRES_NOT_ITEM)
514 {
515 i++;
516 e = components[i];
517 map_file << "," << e.x;
518 }
519 map_file << std::endl;
520 }
521 else if (e.type == EC::REQUIRES_CLASS) {
522 map_file << e.s << std::endl;
523 }
524 else if (e.type == EC::REQUIRES_NOT_CLASS) {
525 map_file << e.s << std::endl;
526 }
527 else if (e.type == EC::SET_STATUS) {
528 map_file << e.s;
529
530 while (i+1 < components.size() && components[i+1].type == EC::SET_STATUS)
531 {
532 i++;
533 e = components[i];
534 map_file << "," << e.s;
535 }
536 map_file << std::endl;
537 }
538 else if (e.type == EC::UNSET_STATUS) {
539 map_file << e.s;
540
541 while (i+1 < components.size() && components[i+1].type == EC::UNSET_STATUS)
542 {
543 i++;
544 e = components[i];
545 map_file << "," << e.s;
546 }
547 map_file << std::endl;
548 }
549 else if (e.type == EC::REMOVE_CURRENCY) {
550 map_file << e.x << std::endl;
551 }
552 else if (e.type == EC::REMOVE_ITEM) {
553 map_file << e.x;
554
555 while (i+1 < components.size() && components[i+1].type == EC::REMOVE_ITEM)
556 {
557 i++;
558 e = components[i];
559 map_file << "," << e.x;
560 }
561 map_file << std::endl;
562 }
563 else if (e.type == EC::REWARD_XP) {
564 map_file << e.x << std::endl;
565 }
566 else if (e.type == EC::REWARD_CURRENCY) {
567 map_file << e.x << std::endl;
568 }
569 else if (e.type == EC::REWARD_ITEM) {
570 map_file << e.x << ",";
571 map_file << e.y << std::endl;
572 }
573 else if (e.type == EC::RESTORE) {
574 map_file << e.s << std::endl;
575 }
576 else if (e.type == EC::POWER) {
577 map_file << e.x << std::endl;
578 }
579 else if (e.type == EC::SPAWN) {
580 map_file << e.s << "," << e.x << "," << e.y;
581
582 while (i+1 < components.size() && components[i+1].type == EC::SPAWN)
583 {
584 i++;
585 e = components[i];
586 map_file << ";" << e.s << "," << e.x << "," << e.y;
587 }
588 map_file << std::endl;
589 }
590 else if (e.type == EC::STASH) {
591 map_file << e.s << std::endl;
592 }
593 else if (e.type == EC::NPC) {
594 map_file << e.s << std::endl;
595 }
596 else if (e.type == EC::MUSIC) {
597 map_file << e.s << std::endl;
598 }
599 else if (e.type == EC::CUTSCENE) {
600 map_file << e.s << std::endl;
601 }
602 else if (e.type == EC::REPEAT) {
603 map_file << e.s << std::endl;
604 }
605 else if (e.type == EC::SAVE_GAME) {
606 map_file << e.s << std::endl;
607 }
608 else if (e.type == EC::BOOK) {
609 map_file << e.s << std::endl;
610 }
611 else if (e.type == EC::SCRIPT) {
612 map_file << e.s << std::endl;
613 }
614 else if (e.type == EC::CHANCE_EXEC) {
615 map_file << e.x << std::endl;
616 }
617 else if (e.type == EC::RESPEC) {
618 if (e.x == 3)
619 map_file << "xp";
620 else if (e.x == 2)
621 map_file << "stats";
622 else if (e.x == 1)
623 map_file << "powers";
624
625 map_file << "," << e.y << endl;
626 }
627 }
628
629 #endif //FLARE_MAP_SAVER
630
631