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