1 /*
2  *
3  *   Copyright (c) 1994, 2002, 2003 Johannes Prix
4  *   Copyright (c) 1994, 2002 Reinhard Prix
5  *   Copyright (c) 2004-2010 Arthur Huillet
6  *
7  *
8  *  This file is part of Freedroid
9  *
10  *  Freedroid is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  Freedroid is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with Freedroid; see the file COPYING. If not, write to the
22  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23  *  MA  02111-1307  USA
24  *
25  */
26 
27 /**
28  * This file contains (all?) map-related functions, which also includes
29  * loading of decks and whole ships, starting the lifts and consoles if
30  * close to the paradroid, refreshes as well as determining the map brick
31  * that contains specified coordinates are done in this file.
32  */
33 
34 #define _map_c
35 
36 #include "system.h"
37 
38 #include "defs.h"
39 #include "struct.h"
40 #include "proto.h"
41 #include "global.h"
42 
43 #include "lvledit/lvledit_actions.h"
44 #include "lvledit/lvledit_display.h"
45 #include "map.h"
46 
47 #define 	TELEPORT_PAIR_STRING	"teleport pair:"
48 
49 void GetThisLevelsDroids(char *section_pointer);
50 
51 /*
52  * Initialize a map tile with default values.
53  */
init_map_tile(struct map_tile * tile)54 void init_map_tile(struct map_tile* tile)
55 {
56 	int i;
57 	tile->floor_values[0] = ISO_FLOOR_SAND;
58 	for (i = 1; i < MAX_FLOOR_LAYERS; i++)
59 		tile->floor_values[i] = ISO_FLOOR_EMPTY;
60 	dynarray_init(&tile->glued_obstacles, 0, sizeof(int));
61 	INIT_LIST_HEAD(&tile->volatile_obstacles);
62 	tile->timestamp = 0;
63 }
64 
65 /**
66  * This function will make all blood obstacles vanish, all dead bots come
67  * back to life, and get all bots return to a wandering state.
68  */
respawn_level(int level_num)69 void respawn_level(int level_num)
70 {
71 	enemy *erot, *nerot;
72 
73 	int wp_num = curShip.AllLevels[level_num]->waypoints.size;
74 	char wp_used[wp_num]; // is a waypoint already used ?
75 	memset(wp_used, 0, wp_num);
76 
77 	// First we remove all the volatile obstacles...
78 	//
79 	remove_volatile_obstacles(level_num);
80 
81 	// Now we can give new life to dead bots...
82 	//
83 	BROWSE_DEAD_BOTS_SAFE(erot, nerot) {
84 		if (erot->pos.z != level_num || Droidmap[erot->type].is_human || !erot->will_respawn)
85 			continue;
86 		/* Move the bot to the alive list */
87 		list_move(&(erot->global_list), &alive_bots_head);
88 		/* Reinsert it into the current level list */
89 		list_add(&(erot->level_list), &level_bots_head[level_num]);
90 	}
91 
92 	// Finally, we reset the runtime attributes of the bots, place them
93 	// on a waypoint, and ask them to start wandering...
94 	//
95 	BROWSE_LEVEL_BOTS(erot, level_num) {
96 
97 		// Unconditional reset of the 'transient state' attributes
98 		enemy_reset(erot);
99 
100 		// Conditional reset of some 'global state' attributes
101 		if (erot->has_been_taken_over) {
102 			erot->faction = FACTION_BOTS;
103 			erot->has_been_taken_over = FALSE;
104 			erot->CompletelyFixed = FALSE;
105 			erot->follow_tux = FALSE;
106 		}
107 
108 		erot->has_greeted_influencer = FALSE;
109 
110 		if (wp_num) {
111 			// Re-place the bots onto the waypoint system
112 			if (!erot->SpecialForce) {
113 				// Standard bots are randomly placed on one waypoint
114 				int wp = teleport_to_random_waypoint(erot, curShip.AllLevels[level_num], wp_used);
115 				wp_used[wp] = 1;
116 				erot->homewaypoint = erot->lastwaypoint;
117 				erot->combat_state = SELECT_NEW_WAYPOINT;
118 				erot->state_timeout = 0.0;
119 			} else {
120 				if (erot->homewaypoint == -1) {
121 					// If a special force droid has not yet been integrated onto
122 					// the waypoint system, place it near its current position.
123 					int wp = teleport_to_closest_waypoint(erot);
124 					wp_used[wp] = 1;
125 					erot->homewaypoint = erot->lastwaypoint;
126 					erot->combat_state = SELECT_NEW_WAYPOINT;
127 					erot->state_timeout = 0.0;
128 				} else {
129 					// Consider that the nextwaypoint of a special force droid
130 					// is occupied, so that a standard bot will not be placed here
131 					if (erot->nextwaypoint != -1)
132 						wp_used[erot->nextwaypoint] = 1;
133 				}
134 			}
135 		} else {
136 			error_message(__FUNCTION__, "There is no waypoint on level %d - unable to place random bots.",
137 					PLEASE_INFORM, level_num);
138 		}
139 	}
140 }
141 
142 /**
143  * \brief Get the center coordinates of a given map label.
144  * In case of fail, a fatal error is thrown.
145  * \param map_label The name of map label to resolve.
146  * \return The gps center of the map label.
147  */
get_map_label_center(const char * label)148 gps get_map_label_center(const char *label)
149 {
150 	struct map_label *m;
151 	gps position = {0., 0., -1};
152 	int i;
153 
154 	for (i = 0; i < curShip.num_levels; i++) {
155 		if (!level_exists(i))
156 			continue;
157 
158 		m = get_map_label(curShip.AllLevels[i], label);
159 		if (m) {
160 			position.x = m->pos.x + 0.5;
161 			position.y = m->pos.y + 0.5;
162 			position.z = i;
163 			return position;
164 		}
165 	}
166 
167 	error_message(__FUNCTION__, "\
168 Resolving map label   \"%s\"   failed on the entire world!\n\
169 This is a severe error in the game data of FreedroidRPG.", PLEASE_INFORM, label);
170 
171 	return position;
172 };
173 
174 /**
175  * Next we extract the level interface data from the human-readable data
176  * into the level struct, but WITHOUT destroying or damaging the
177  * human-readable data in the process!
178  */
decode_interfaces(level * loadlevel,char * DataPointer)179 static void decode_interfaces(level *loadlevel, char *DataPointer)
180 {
181 	char *TempSectionPointer;
182 	char PreservedLetter;
183 
184 	// We look for the beginning and end of the map statement section
185 	TempSectionPointer = LocateStringInData(DataPointer, MAP_BEGIN_STRING);
186 
187 	// We add a terminator at the end, but ONLY TEMPORARY.  The damage will be restored later!
188 	PreservedLetter = TempSectionPointer[0];
189 	TempSectionPointer[0] = 0;
190 
191 	ReadValueFromString(DataPointer, "jump target north: ", "%d", &(loadlevel->jump_target_north), TempSectionPointer);
192 	ReadValueFromString(DataPointer, "jump target south: ", "%d", &(loadlevel->jump_target_south), TempSectionPointer);
193 	ReadValueFromString(DataPointer, "jump target east: ", "%d", &(loadlevel->jump_target_east), TempSectionPointer);
194 	ReadValueFromString(DataPointer, "jump target west: ", "%d", &(loadlevel->jump_target_west), TempSectionPointer);
195 
196 	TempSectionPointer[0] = PreservedLetter;
197 
198 }
199 
decode_dimensions(level * loadlevel,char * DataPointer)200 static void decode_dimensions(level *loadlevel, char *DataPointer)
201 {
202 
203 	int off = 0;
204 
205 	/* Read levelnumber */
206 	char *fp = DataPointer;
207 	fp += strlen(LEVEL_HEADER_LEVELNUMBER);
208 	while (*(fp + off) != '\n')
209 		off++;
210 	fp[off] = 0;
211 	loadlevel->levelnum = atoi(fp);
212 	fp[off] = '\n';
213 	fp += off + 1;
214 	off = 0;
215 
216 	/* Read xlen */
217 	fp += strlen("xlen of this level:");
218 	while (*(fp + off) != '\n')
219 		off++;
220 	fp[off] = 0;
221 	loadlevel->xlen = atoi(fp);
222 	fp[off] = '\n';
223 	fp += off + 1;
224 	off = 0;
225 
226 	/* Read ylen */
227 	fp += strlen("ylen of this level:");
228 	while (*(fp + off) != '\n')
229 		off++;
230 	fp[off] = 0;
231 	loadlevel->ylen = atoi(fp);
232 	fp[off] = '\n';
233 	fp += off + 1;
234 	off = 0;
235 
236 	/* Read floor_layers */
237 	fp += strlen("floor layers:");
238 	while (*(fp + off) != '\n')
239 		off++;
240 	fp[off] = 0;
241 	loadlevel->floor_layers = atoi(fp);
242 	fp[off] = '\n';
243 	fp += off + 1;
244 	off = 0;
245 
246 	/* Read lrb */
247 	fp += strlen("light radius bonus of this level:");
248 	while (*(fp + off) != '\n')
249 		off++;
250 	fp[off] = 0;
251 	loadlevel->light_bonus = atoi(fp);
252 	fp[off] = '\n';
253 	fp += off + 1;
254 	off = 0;
255 
256 	fp += strlen("minimal light on this level:");
257 	while (*(fp + off) != '\n')
258 		off++;
259 	fp[off] = 0;
260 	loadlevel->minimum_light_value = atoi(fp);
261 	fp[off] = '\n';
262 	fp += off + 1;
263 	off = 0;
264 
265 	fp += strlen("infinite_running_on_this_level:");
266 	while (*(fp + off) != '\n')
267 		off++;
268 	fp[off] = 0;
269 	loadlevel->infinite_running_on_this_level = atoi(fp);
270 	fp[off] = '\n';
271 	fp += off + 1;
272 	off = 0;
273 
274 	fp += strlen("random dungeon:");
275 	while (*(fp + off) != '\n')
276 		off++;
277 	fp[off] = 0;
278 	loadlevel->random_dungeon = atoi(fp);
279 	fp[off] = '\n';
280 	fp += off + 1;
281 	off = 0;
282 
283 	if (!strncmp(fp, TELEPORT_PAIR_STRING, strlen(TELEPORT_PAIR_STRING))) {
284 		fp += strlen(TELEPORT_PAIR_STRING);
285 		while (*(fp + off) != '\n')
286 			off++;
287 		fp[off] = 0;
288 		loadlevel->teleport_pair = atoi(fp);
289 		fp[off] = '\n';
290 		fp += off + 1;
291 		off = 0;
292 	} else {
293 		loadlevel->teleport_pair = 0;
294 	}
295 
296 	if (!strncmp(fp, "dungeon generated:", 18)) {
297 		fp += strlen("dungeon generated:");
298 		while (*(fp + off) != '\n')
299 			off++;
300 		fp[off] = 0;
301 		loadlevel->dungeon_generated = atoi(fp);
302 		fp[off] = '\n';
303 		fp += off + 1;
304 		off = 0;
305 	} else {
306 		loadlevel->dungeon_generated = 0;
307 	}
308 
309 	if (!strncmp(fp, "environmental flags:", 20)) {
310 		fp += strlen("environmental flags:");
311 		while (*(fp + off) != '\n')
312 			off++;
313 		fp[off] = 0;
314 		loadlevel->flags = atoi(fp);
315 		fp[off] = '\n';
316 		fp += off + 1;
317 		off = 0;
318 	} else {
319 		loadlevel->flags = 0;
320 	}
321 
322 	if (!strncmp(fp, "item drop class:", 16)) {
323 		fp += strlen("item drop class:");
324 		while (*(fp + off) != '\n')
325 			off++;
326 		fp[off] = 0;
327 		loadlevel->drop_class = atoi(fp);
328 		fp[off] = '\n';
329 		// Note: to be commented out if an other decoding part is added after this one
330 		// fp += off + 1;
331 		// off = 0;
332 	} else {
333 		loadlevel->drop_class = -1;
334 	}
335 
336 	// Note: if an other decoding part is added, do not forget to comment out the 2 lines
337 	// above...
338 
339 	if (loadlevel->ylen >= MAX_MAP_LINES) {
340 		error_message(__FUNCTION__, "\
341 A map/level in FreedroidRPG which was supposed to load has more map lines than allowed\n\
342 for a map/level as by the constant MAX_MAP_LINES in defs.h.\n\
343 Sorry, but unless this constant is raised, FreedroidRPG will refuse to load this map.", PLEASE_INFORM | IS_FATAL);
344 	}
345 }
346 
decode_random_droids(level * loadlevel,char * data)347 static void decode_random_droids(level *loadlevel, char *data)
348 {
349 	char *search_ptr;
350 	char *end_ptr;
351 
352 #define DROIDS_NUMBER_INDICATION_STRING "number of random droids: "
353 #define ALLOWED_TYPE_INDICATION_STRING "random droid types: "
354 
355 	// Read the number of random droids for this level
356 	end_ptr = strstr(data, ALLOWED_TYPE_INDICATION_STRING);
357 	ReadValueFromString(data, DROIDS_NUMBER_INDICATION_STRING, "%d", &loadlevel->random_droids.nr, end_ptr);
358 
359 	if (loadlevel->random_droids.nr <= 0)
360 		return;
361 
362 	// Now we read in the type(s) of random droids for this level
363 	data = strstr(data, ALLOWED_TYPE_INDICATION_STRING);
364 
365 	search_ptr = ReadAndMallocStringFromDataOptional(data, ALLOWED_TYPE_INDICATION_STRING, "\n");
366 	if (search_ptr) {
367 		char *droid_type_ptr = search_ptr;
368 		while (*droid_type_ptr) {
369 			while (*droid_type_ptr && isspace(*droid_type_ptr)) {
370 				droid_type_ptr++;
371 			}
372 			int droid_type_length = 0;
373 			char *ptr = droid_type_ptr;
374 			while (isalnum(*ptr)) {
375 				ptr++;
376 				droid_type_length++;
377 			}
378 			if (!droid_type_length)
379 				break;
380 
381 			char type_indication_string[droid_type_length + 1];
382 			strncpy(type_indication_string, droid_type_ptr, droid_type_length);
383 			type_indication_string[droid_type_length] = 0;
384 
385 			int droid_type = get_droid_type(type_indication_string);
386 
387 			loadlevel->random_droids.types[loadlevel->random_droids.types_size++] = droid_type;
388 
389 			droid_type_ptr += droid_type_length;
390 			if (*droid_type_ptr)
391 				droid_type_ptr++; //skip the comma
392 		}
393 		free(search_ptr);
394 	}
395 }
396 
decode_header(level * loadlevel,char * data)397 static int decode_header(level *loadlevel, char *data)
398 {
399 	data = strstr(data, LEVEL_HEADER_LEVELNUMBER);
400 	if (!data)
401 		return 1;
402 
403 	decode_interfaces(loadlevel, data);
404 	decode_dimensions(loadlevel, data);
405 	decode_random_droids(loadlevel, data);
406 
407 	// Read the levelname.
408 	loadlevel->Levelname = ReadAndMallocStringFromData(data, LEVEL_NAME_STRING, "\"");
409 
410 	loadlevel->Background_Song_Name = ReadAndMallocStringFromData(data, BACKGROUND_SONG_NAME_STRING, "\n");
411 
412 	return 0;
413 }
414 
415 /**
416  * Next we extract the human readable obstacle data into the level struct
417  * WITHOUT destroying or damaging the human-readable data in the process!
418  * This is an improved parser that is not quite readable but very performant.
419  */
decode_obstacles(level * loadlevel,char * DataPointer)420 static char *decode_obstacles(level *loadlevel, char *DataPointer)
421 {
422 	int i;
423 	char *curfield = NULL;
424 	char *curfieldend = NULL;
425 	char *obstacle_SectionBegin;
426 	float x, y;
427 	int type;
428 
429 	// First we initialize the obstacles with 'empty' information
430 	//
431 	for (i = 0; i < MAX_OBSTACLES_ON_MAP; i++) {
432 		loadlevel->obstacle_list[i].type = -1;
433 		loadlevel->obstacle_list[i].pos.x = -1;
434 		loadlevel->obstacle_list[i].pos.y = -1;
435 		loadlevel->obstacle_list[i].pos.z = loadlevel->levelnum;
436 		loadlevel->obstacle_list[i].timestamp = 0;
437 		loadlevel->obstacle_list[i].frame_index = 0;
438 	}
439 
440 	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
441 		return DataPointer;
442 
443 	// Now we look for the beginning and end of the obstacle section
444 	//
445 	obstacle_SectionBegin = LocateStringInData(DataPointer, OBSTACLE_DATA_BEGIN_STRING) + strlen(OBSTACLE_DATA_BEGIN_STRING) + 1;
446 
447 	// Now we decode all the obstacle information
448 	//
449 	curfield = obstacle_SectionBegin;
450 	while (*curfield != '/') {
451 		//structure of obstacle entry is :      // t59 x2.50 y63.50 l-1 d-1
452 		//we read the type
453 		curfield++;
454 		curfieldend = curfield;
455 		while ((*curfieldend) != ' ')
456 			curfieldend++;
457 		(*curfieldend) = 0;
458 		type = atoi(curfield);
459 		(*curfieldend) = ' ';
460 
461 		//we read the X position
462 		curfield = curfieldend + 2;
463 		curfieldend += 2;
464 		while ((*curfieldend) != ' ')
465 			curfieldend++;
466 		(*curfieldend) = 0;
467 		x = atof(curfield);
468 		(*curfieldend) = ' ';
469 
470 		//Y position
471 		curfield = curfieldend + 2;
472 		curfieldend += 2;
473 		while ((*curfieldend) != ' ')
474 			curfieldend++;
475 		(*curfieldend) = 0;
476 		y = atof(curfield);
477 		(*curfieldend) = ' ';
478 
479 		while ((*curfield) != '\n')
480 			curfield++;
481 		curfield++;
482 
483 		// Even invalid obstacles are loaded. They can not be removed at this
484 		// point, or else obstacle extensions will point to the wrong obstacles.
485 		// decode_level(), our callee, will take care of them.
486 		add_obstacle_nocheck(loadlevel, x, y, type);
487 	}
488 
489 	return curfield;
490 }
491 
492 /**
493  * Next we extract the map labels of this level WITHOUT destroying
494  * or damaging the data in the process!
495  */
decode_map_labels(level * loadlevel,char * data)496 static char *decode_map_labels(level *loadlevel, char *data)
497 {
498 	char *label_name;
499 	int i, x, y;
500 
501 	// Initialize map labels
502 	dynarray_init(&loadlevel->map_labels, 10, sizeof(struct map_label));
503 
504 	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
505 		return data;
506 
507 	// Now we look for the beginning and end of the map labels section
508 	char *map_label_begin = LocateStringInData(data, MAP_LABEL_BEGIN_STRING) + strlen(MAP_LABEL_BEGIN_STRING) + 1;
509 	char *map_label_end = LocateStringInData(map_label_begin, MAP_LABEL_END_STRING);
510 	*map_label_end = '\0';
511 
512 	// Get the number of map labels in this level
513 	int nb_map_labels_in_level = CountStringOccurences(map_label_begin, LABEL_ITSELF_ANNOUNCE_STRING);
514 	DebugPrintf(1, "\nNumber of map labels found in this level : %d.", nb_map_labels_in_level);
515 
516 	// Now we decode all the map label information
517 	for (i = 0; i < nb_map_labels_in_level ; i++) {
518 		if (i)
519 			map_label_begin = strstr(map_label_begin + 1, X_POSITION_OF_LABEL_STRING);
520 
521 		// Get the position of the map label
522 		ReadValueFromString(map_label_begin, X_POSITION_OF_LABEL_STRING, "%d", &x, map_label_end);
523 		ReadValueFromString(map_label_begin, Y_POSITION_OF_LABEL_STRING, "%d", &y, map_label_end);
524 
525 		// Get the name of the map label
526 		label_name = ReadAndMallocStringFromData(map_label_begin, LABEL_ITSELF_ANNOUNCE_STRING, "\"");
527 
528 		// Add the map label on the level
529 		add_map_label(loadlevel, x, y, label_name);
530 
531 		DebugPrintf(1, "\npos.x=%d pos.y=%d label_name=\"%s\"", x, y, label_name);
532 	}
533 
534 	*map_label_end = MAP_LABEL_END_STRING[0];
535 	return map_label_end;
536 }
537 
ReadInOneItem(char * ItemPointer,char * ItemsSectionEnd,item * TargetItem)538 static void ReadInOneItem(char *ItemPointer, char *ItemsSectionEnd, item *TargetItem)
539 {
540 	init_item(TargetItem);
541 
542 	char *item_id = ReadAndMallocStringFromData(ItemPointer, ITEM_ID_STRING, "\"");
543 	TargetItem->type = get_item_type_by_id(item_id);
544 	free(item_id);
545 
546 	ReadValueFromString(ItemPointer, ITEM_POS_X_STRING, "%f", &(TargetItem->pos.x), ItemsSectionEnd);
547 	ReadValueFromString(ItemPointer, ITEM_POS_Y_STRING, "%f", &(TargetItem->pos.y), ItemsSectionEnd);
548 	ReadValueFromStringWithDefault(ItemPointer, ITEM_ARMOR_CLASS_BASE_STRING, "%d", "0", &(TargetItem->armor_class), ItemsSectionEnd);
549 	ReadValueFromString(ItemPointer, ITEM_MAX_DURABILITY_STRING, "%d", &(TargetItem->max_durability), ItemsSectionEnd);
550 	ReadValueFromString(ItemPointer, ITEM_CUR_DURABILITY_STRING, "%f", &(TargetItem->current_durability), ItemsSectionEnd);
551 	ReadValueFromString(ItemPointer, ITEM_AMMO_CLIP_STRING, "%d", &(TargetItem->ammo_clip), ItemsSectionEnd);
552 	ReadValueFromString(ItemPointer, ITEM_MULTIPLICITY_STRING, "%d", &(TargetItem->multiplicity), ItemsSectionEnd);
553 
554 	// Read the socket data of the item and calculate bonuses using it.
555 	int i;
556 	int socket_count;
557 	ReadValueFromStringWithDefault(ItemPointer, ITEM_SOCKETS_SIZE_STRING, "%d", "0", &socket_count, ItemsSectionEnd);
558 	for (i = 0; i < socket_count; i++) {
559 		char type_string[32];
560 		char addon_string[32];
561 		struct upgrade_socket socket;
562 		sprintf(type_string, "%s%d=", ITEM_SOCKET_TYPE_STRING, i);
563 		sprintf(addon_string, "%s%d=", ITEM_SOCKET_ADDON_STRING, i);
564 		ReadValueFromString(ItemPointer, type_string, "%d", &socket.type, ItemsSectionEnd);
565 		socket.addon = ReadAndMallocStringFromDataOptional(ItemPointer, addon_string, "\"");
566 		create_upgrade_socket(TargetItem, socket.type, socket.addon);
567 		free(socket.addon);
568 	}
569 	calculate_item_bonuses(TargetItem);
570 
571 	DebugPrintf(1, "\nPosX=%f PosY=%f Item=%d", TargetItem->pos.x, TargetItem->pos.y, TargetItem->type);
572 
573 }
574 
decode_extension_chest(char * ext,void ** data)575 static char *decode_extension_chest(char *ext, void **data)
576 {
577 	struct dynarray *chest = dynarray_alloc(1, sizeof(item));
578 	char *item_str, *item_end;
579 
580 	item_str = ext;
581 
582 	while (*item_str != '}') {
583 		// Find end of this item (beginning of next item)
584 		item_end = item_str;
585 		while (*item_end != '\n')
586 			item_end++;
587 		while (isspace(*item_end))
588 			item_end++;
589 
590 		// Read the item on this line
591 		item new_item;
592 		ReadInOneItem(item_str, item_end, &new_item);
593 
594 		// Add the item to the dynarray
595 		dynarray_add(chest, &new_item, sizeof(item));
596 
597 		// Move to the next item definition
598 		item_str = item_end;
599 	}
600 
601 
602 	*data = chest;
603 	return item_str;
604 }
605 
decode_extension_label(char * ext,void ** data)606 static char *decode_extension_label(char *ext, void **data)
607 {
608 	char *begin = NULL;
609 	char *end = NULL;
610 
611 	if (*ext != '\"') {
612 		// For compatibility with old levels.dat and savegames, parse an
613 		// unquoted string.
614 		// TODO: to be removed in the future, when unquoted strings are no
615 		// more used by anybody...
616 		begin = ext;
617 		end = begin;
618 		while (*end != '\n')
619 			end++;
620 	} else {
621 		begin = ext + 1;
622 		end = begin;
623 		while (*end != '\"' && *end != '\n')
624 			end++;
625 	}
626 
627 	*end = '\0';
628 	*data = strdup(begin);
629 	*end = '\n';
630 
631 	while (*end != '}')
632 		end++;
633 
634 	return end;
635 }
636 
decode_extension_dialog(char * ext,void ** data)637 static char *decode_extension_dialog(char *ext, void **data)
638 {
639 	// Same format than label extension.
640 	return decode_extension_label(ext, data);
641 }
642 
decode_extension_signmessage(char * ext,void ** data)643 static char *decode_extension_signmessage(char *ext, void **data)
644 {
645 	char *begin = NULL;
646 	char *end = NULL;
647 
648 	if (*ext != '_' && *ext != '\"') {
649 		// For compatibility with old levels.dat and savegames, parse an
650 		// unquoted string.
651 		// TODO: to be removed in the future, when unquoted strings are no
652 		// more used by anybody...
653 		begin = ext;
654 		end = begin;
655 		while (*end != '\n')
656 			end++;
657 	} else {
658 		if (*ext == '\"')
659 			begin = ext + 1;
660 		else
661 			begin = ext + 2;
662 		end = begin;
663 		while (*end != '\"' && *end != '\n')
664 			end++;
665 	}
666 
667 	*end = '\0';
668 	*data = strdup(begin);
669 	*end = '\n';
670 
671 	while (*end != '}')
672 		end++;
673 
674 	return end;
675 }
676 
decode_obstacle_extensions(level * loadlevel,char * data)677 static char *decode_obstacle_extensions(level *loadlevel, char *data)
678 {
679 	dynarray_init(&loadlevel->obstacle_extensions, 10, sizeof(struct obstacle_extension));
680 
681 	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
682 		return data;
683 
684 	char *ext_begin = LocateStringInData(data, OBSTACLE_EXTENSIONS_BEGIN_STRING);
685 	char *ext_end = LocateStringInData(ext_begin, OBSTACLE_EXTENSIONS_END_STRING);
686 	*ext_end = '\0';
687 
688 	while (1) {
689 		// Look for the next extension
690 		ext_begin = strstr(ext_begin, "idx=");
691 		if (!ext_begin)
692 			break;
693 
694 		// Read extension information
695 		int index;
696 		int type;
697 		void *ext_data = NULL;
698 		sscanf(ext_begin, "idx=%d type=%d", &index, &type);
699 
700 		// Move to the extension data definition
701 		ext_begin = strstr(ext_begin, "data={\n");
702 		while (*ext_begin != '\n')
703 			ext_begin++;
704 		while (isspace(*ext_begin))
705 			ext_begin++;
706 
707 		// Read the extension data
708 		switch (type) {
709 			case OBSTACLE_EXTENSION_CHEST_ITEMS:
710 				ext_begin = decode_extension_chest(ext_begin, &ext_data);
711 				break;
712 			case OBSTACLE_EXTENSION_LABEL:
713 				ext_begin = decode_extension_label(ext_begin, &ext_data);
714 				break;
715 			case OBSTACLE_EXTENSION_DIALOGFILE:
716 			{
717 				// Old levels.dat and savegames use this type for
718 				// dialog extensions as well as for sign messages extensions.
719 				// New 'format' uses two different values.
720 				// For compatibility, at least during some time, we accept
721 				// to read sign message extension here, but we convert it to
722 				// its actual type.
723 				// The actual use of an obstacle extension, and thus its actual type,
724 				// is defined by the 'action' set to the obstacle (see action.c).
725 				// TODO: To be removed in the future
726 				obstacle *obs = &(loadlevel->obstacle_list[index]);
727 				obstacle_spec *spec = get_obstacle_spec(obs->type);
728 
729 				if (spec->action && strcmp(spec->action, "sign")) {
730 					// This is really a dialog extension
731 					ext_begin = decode_extension_dialog(ext_begin, &ext_data);
732 					break;
733 				}
734 				// This is a sign message extension
735 				// We change the extension's type, and continue within the next
736 				// switch case
737 				type = OBSTACLE_EXTENSION_SIGNMESSAGE;
738 			}
739 			/* no break */
740 			case OBSTACLE_EXTENSION_SIGNMESSAGE:
741 				ext_begin = decode_extension_signmessage(ext_begin, &ext_data);
742 				break;
743 		}
744 
745 		// Add the obstacle extension on the level
746 		add_obstacle_extension(loadlevel, &(loadlevel->obstacle_list[index]), type, ext_data);
747 	}
748 
749 	*ext_end = OBSTACLE_EXTENSIONS_END_STRING[0];
750 	return ext_end;
751 }
752 
decode_item_section(level * loadlevel,char * data)753 static char *decode_item_section(level *loadlevel, char *data)
754 {
755 	int i;
756 	char Preserved_Letter;
757 	int NumberOfItemsInThisLevel;
758 	char *ItemPointer;
759 	char *ItemsSectionBegin;
760 	char *ItemsSectionEnd;
761 
762 	// First we initialize the items arrays with 'empty' information
763 	//
764 	for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
765 		init_item(&loadlevel->ItemList[i]);
766 	}
767 
768 	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
769 		return data;
770 
771 	// We look for the beginning and end of the items section
772 	ItemsSectionBegin = LocateStringInData(data, ITEMS_SECTION_BEGIN_STRING);
773 	ItemsSectionEnd = LocateStringInData(ItemsSectionBegin, ITEMS_SECTION_END_STRING);
774 
775 	// We add a terminator at the end of the items section, but ONLY TEMPORARY.
776 	// The damage will be restored later!
777 	Preserved_Letter = ItemsSectionEnd[0];
778 	ItemsSectionEnd[0] = 0;
779 	NumberOfItemsInThisLevel = CountStringOccurences(ItemsSectionBegin, ITEM_ID_STRING);
780 	DebugPrintf(1, "\nNumber of items found in this level : %d.", NumberOfItemsInThisLevel);
781 
782 	// Now we decode all the item information
783 	ItemPointer = ItemsSectionBegin;
784 	char *NextItemPointer;
785 	for (i = 0; i < NumberOfItemsInThisLevel; i++) {
786 		if ((ItemPointer = strstr(ItemPointer + 1, ITEM_ID_STRING))) {
787 			NextItemPointer = strstr(ItemPointer + 1, ITEM_ID_STRING);
788 			if (NextItemPointer)
789 				NextItemPointer[0] = 0;
790 			ReadInOneItem(ItemPointer, ItemsSectionEnd, &(loadlevel->ItemList[i]));
791 			loadlevel->ItemList[i].pos.z = loadlevel->levelnum;
792 			if (NextItemPointer)
793 				NextItemPointer[0] = ITEM_ID_STRING[0];
794 		}
795 	}
796 
797 	// Now we repair the damage done to the loaded level data
798 	ItemsSectionEnd[0] = Preserved_Letter;
799 	return ItemsSectionEnd;
800 }
801 
decode_map(level * loadlevel,char * data)802 static char *decode_map(level *loadlevel, char *data)
803 {
804 	char *map_begin, *map_end;
805 	char *this_line;
806 	int i;
807 
808 	if ((map_begin = strstr(data, MAP_BEGIN_STRING)) == NULL)
809 		return NULL;
810 	map_begin += strlen(MAP_BEGIN_STRING) + 1;
811 
812 	if ((map_end = strstr(data, MAP_END_STRING)) == NULL)
813 		return NULL;
814 
815 	/* now scan the map */
816 	unsigned int curlinepos = 0;
817 	this_line = (char *)MyMalloc(4096);
818 
819 	/* read MapData */
820 	for (i = 0; i < loadlevel->ylen; i++) {
821 		int col;
822 		int layer;
823 		map_tile *Buffer;
824 		int tmp;
825 
826 		/* Select the next line */
827 		unsigned int nlpos = 0;
828 		memset(this_line, 0, 4096);
829 		while (map_begin[curlinepos + nlpos] != '\n')
830 			nlpos++;
831 		memcpy(this_line, map_begin + curlinepos, nlpos);
832 		this_line[nlpos] = '\0';
833 		nlpos++;
834 
835 		/* Decode it */
836 		Buffer = MyMalloc((loadlevel->xlen + 10) * sizeof(map_tile));
837 		for (col = 0; col < loadlevel->xlen; col++) {
838 			// Make sure that all floor layers are always initialized properly.
839 			init_map_tile(&Buffer[col]);
840 
841 			for (layer = 0; layer < loadlevel->floor_layers; layer++) {
842 				tmp = strtol(this_line + 4 * (loadlevel->floor_layers * col + layer), NULL, 10);
843 				Buffer[col].floor_values[layer] = (Uint16) tmp;
844 			}
845 		}
846 
847 		// Now the old text pointer can be replaced with a pointer to the
848 		// correctly assembled struct...
849 		//
850 		loadlevel->map[i] = Buffer;
851 
852 		curlinepos += nlpos;
853 	}
854 
855 	free(this_line);
856 	return map_end;
857 }
858 
decode_waypoints(level * loadlevel,char * data)859 static char *decode_waypoints(level *loadlevel, char *data)
860 {
861 	char *wp_begin, *wp_end;
862 	char *this_line;
863 	int nr, x, y, wp_rnd;
864 	char *pos;
865 
866 	// Initialize waypoints
867 	dynarray_init(&loadlevel->waypoints, 2, sizeof(struct waypoint));
868 
869 	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
870 		return data;
871 
872 	// Find the beginning and end of the waypoint list
873 	if ((wp_begin = strstr(data, WP_BEGIN_STRING)) == NULL)
874 		return NULL;
875 	wp_begin += strlen(WP_BEGIN_STRING) + 1;
876 
877 	if ((wp_end = strstr(data, WP_END_STRING)) == NULL)
878 		return NULL;
879 
880 	int curlinepos = 0;
881 	this_line = (char *)MyMalloc(4096);
882 
883 	while (1) {
884 		/* Select the next line */
885 		short int nlpos = 0;
886 		memset(this_line, 0, 4096);
887 		while (wp_begin[curlinepos + nlpos] != '\n')
888 			nlpos++;
889 		memcpy(this_line, wp_begin + curlinepos, nlpos);
890 		this_line[nlpos] = '\0';
891 		nlpos++;
892 
893 		curlinepos += nlpos;
894 
895 		if (!strncmp(this_line, wp_end, strlen(WP_END_STRING))) {
896 			break;
897 		}
898 		wp_rnd = 0;
899 		sscanf(this_line, "Nr.=%d \t x=%d \t y=%d   rnd=%d", &nr, &x, &y, &wp_rnd);
900 
901 		// Create a new waypoint
902 		waypoint new_wp;
903 		new_wp.x = x;
904 		new_wp.y = y;
905 		new_wp.suppress_random_spawn = wp_rnd;
906 
907 		// Initalize the connections of the new waypoint
908 		dynarray_init(&new_wp.connections, 2, sizeof(int));
909 
910 		pos = strstr(this_line, CONNECTION_STRING);
911 		if (pos == NULL) {
912 			fprintf(stderr, "Unable to find connection string. line is %s, level %i\n", this_line,
913 				loadlevel->levelnum);
914 		}
915 		pos += strlen(CONNECTION_STRING);	// skip connection-string
916 		pos += strspn(pos, WHITE_SPACE);	// skip initial whitespace
917 
918 		while (1) {
919 			if (*pos == '\0')
920 				break;
921 			int connection;
922 			int res = sscanf(pos, "%d", &connection);
923 			if ((connection == -1) || (res == 0) || (res == EOF))
924 				break;
925 
926 			// Add the connection on this waypoint
927 			dynarray_add(&new_wp.connections, &connection, sizeof(int));
928 
929 			pos += strcspn(pos, WHITE_SPACE);	// skip last token
930 			pos += strspn(pos, WHITE_SPACE);	// skip initial whitespace for next one
931 		}
932 
933 		// Add the waypoint on the level
934 		dynarray_add(&loadlevel->waypoints, &new_wp, sizeof(struct waypoint));
935 	}
936 
937 	free(this_line);
938 	return wp_end;
939 }
940 
941 /**
942  * The smash_obstacle function uses this function as a subfunction to
943  * check for exploding obstacles glued to one specific map square.  Of
944  * course also the player number (or -1 in case of no check/bullet hit)
945  * must be supplied so as to be able to suppress hits through walls or
946  * the like.
947  */
smash_obstacles_only_on_tile(float x,float y,int lvl,int map_x,int map_y)948 static int smash_obstacles_only_on_tile(float x, float y, int lvl, int map_x, int map_y)
949 {
950 	Level BoxLevel = curShip.AllLevels[lvl];
951 	int i;
952 	int target_idx;
953 	obstacle *target_obstacle;
954 	int smashed_something = FALSE;
955 	moderately_finepoint blast_start_pos;
956 
957 	// First some security checks against touching the outsides of the map...
958 	//
959 	if (!pos_inside_level(map_x, map_y, BoxLevel))
960 		return (FALSE);
961 
962 	// We check all the obstacles on this square if they are maybe destructible
963 	// and if they are, we destruct them, haha
964 	//
965 	for (i = 0; i < BoxLevel->map[map_y][map_x].glued_obstacles.size; i++) {
966 		// First we see if there is something glued to this map tile at all.
967 		//
968 		target_idx = ((int *)(BoxLevel->map[map_y][map_x].glued_obstacles.arr))[i];
969 
970 		target_obstacle = &(BoxLevel->obstacle_list[target_idx]);
971 
972 		obstacle_spec *obstacle_spec = get_obstacle_spec(target_obstacle->type);
973 		if (!(obstacle_spec->flags & IS_SMASHABLE))
974 			continue;
975 
976 		// Now we check if the item really was close enough to the strike target.
977 		// A range of 0.5 should do.
978 		//
979 		if (fabsf(x - target_obstacle->pos.x) > 0.4)
980 			continue;
981 		if (fabsf(y - target_obstacle->pos.y) > 0.4)
982 			continue;
983 
984 		colldet_filter filter = FlyableExceptIdPassFilter;
985 		filter.data = &target_idx;
986 		gps smash_pos = { x, y, lvl };
987 		gps vsmash_pos;
988 		update_virtual_position(&vsmash_pos, &smash_pos, Me.pos.z);
989 		if (vsmash_pos.x == -1)
990 			continue;
991 		if (!DirectLineColldet(vsmash_pos.x, vsmash_pos.y, Me.pos.x, Me.pos.y, Me.pos.z, &filter)) {
992 			continue;
993 		}
994 
995 		DebugPrintf(1, "\nObject smashed at: (%f/%f) by hit/explosion at (%f/%f).",
996 			    target_obstacle->pos.x, target_obstacle->pos.y, x, y);
997 
998 		smashed_something = TRUE;
999 
1000 		// Since the obstacle is destroyed, we start a blast at it's position.
1001 		// But here a WARNING WARNING WARNING! is due!  We must not start the
1002 		// blast before the obstacle is removed, because the blast will again
1003 		// cause this very obstacle removal function, so we need to be careful
1004 		// so as not to incite endless recursion.  We memorize the position for
1005 		// later, then delete the obstacle, then we start the blast.
1006 		//
1007 		blast_start_pos.x = target_obstacle->pos.x;
1008 		blast_start_pos.y = target_obstacle->pos.y;
1009 
1010 		int obstacle_drops_treasure
1011 			= obstacle_spec->flags & DROPS_RANDOM_TREASURE;
1012 
1013 		// Let the automap know that we've updated things
1014 		update_obstacle_automap(lvl, target_obstacle);
1015 
1016 		// Now we really smash the obstacle, i.e. we can set it's type to the debris that has
1017 		// been configured for this obstacle type.  In if there is nothing configured (i.e. -1 set)
1018 		// then we'll just delete the obstacle in question entirely.  For this we got a standard function to
1019 		// safely do it and not make some errors into the glue structure or obstacles lists...
1020 		//
1021 		if (obstacle_spec->result_type_after_smashing_once == (-1)) {
1022 			del_obstacle(target_obstacle);
1023 		} else {
1024 			target_obstacle->type = obstacle_spec->result_type_after_smashing_once;
1025 		}
1026 
1027 		// Drop items after destroying the obstacle, in order to avoid collisions
1028 		if (obstacle_drops_treasure)
1029 			DropRandomItem(lvl, target_obstacle->pos.x, target_obstacle->pos.y, BoxLevel->drop_class, FALSE);
1030 
1031 		// Now that the obstacle is removed AND ONLY NOW that the obstacle is
1032 		// removed, we may start a blast at this position.  Otherwise we would
1033 		// run into trouble, see the warning further above.
1034 		StartBlast(blast_start_pos.x, blast_start_pos.y,
1035 				lvl, obstacle_spec->blast_type, 0.0, FACTION_SELF, obstacle_spec->smashed_sound);
1036 	}
1037 
1038 	return smashed_something;
1039 }
1040 
1041 /**
1042  * When a destructible type of obstacle gets hit, e.g. by a blast
1043  * exploding on the tile or a melee hit on the same floor tile, then some
1044  * of the obstacles (like barrel or crates) might explode, sometimes
1045  * leaving some treasure behind.
1046  *
1047  */
smash_obstacle(float x,float y,int lvl)1048 int smash_obstacle(float x, float y, int lvl)
1049 {
1050 	int map_x, map_y;
1051 	int smash_x, smash_y;
1052 	int smashed_something = FALSE;
1053 
1054 	map_x = (int)rintf(x);
1055 	map_y = (int)rintf(y);
1056 
1057 	for (smash_y = map_y - 1; smash_y < map_y + 2; smash_y++) {
1058 		for (smash_x = map_x - 1; smash_x < map_x + 2; smash_x++) {
1059 			if (smash_obstacles_only_on_tile(x, y, lvl, smash_x, smash_y))
1060 				smashed_something = TRUE;
1061 		}
1062 	}
1063 
1064 	return (smashed_something);
1065 
1066 }				// int smash_obstacle ( float x , float y );
1067 
1068 /**
1069  * This function returns the map brick code of the tile that occupies the
1070  * given position in the given layer.
1071  * Floor layers are indexed from 0 to lvl->floor_layers - 1. The lowest
1072  * floor layer is #0. Every map has at least one layer.
1073  */
get_map_brick(level * lvl,float x,float y,int layer)1074 Uint16 get_map_brick(level *lvl, float x, float y, int layer)
1075 {
1076 	Uint16 BrickWanted;
1077 	int RoundX, RoundY;
1078 
1079 	gps vpos = { x, y, lvl->levelnum };
1080 	gps rpos;
1081 	if (!resolve_virtual_position(&rpos, &vpos)) {
1082 		return ISO_FLOOR_EMPTY;
1083 	}
1084 	RoundX = (int)rintf(rpos.x);
1085 	RoundY = (int)rintf(rpos.y);
1086 
1087 	BrickWanted = curShip.AllLevels[rpos.z]->map[RoundY][RoundX].floor_values[layer];
1088 
1089 	if (BrickWanted >= underlay_floor_tiles.size) {
1090 		if (BrickWanted < MAX_UNDERLAY_FLOOR_TILES || (BrickWanted - MAX_UNDERLAY_FLOOR_TILES) >= overlay_floor_tiles.size) {
1091 			error_message(__FUNCTION__, "Level %d at %d %d in %d layer uses an unknown floor tile: %d.", PLEASE_INFORM,
1092 				lvl->levelnum, RoundX, RoundY, layer, BrickWanted);
1093 			return ISO_FLOOR_EMPTY;
1094 		}
1095 	}
1096 
1097 	return BrickWanted;
1098 }
1099 
1100 /**
1101  * This functions reads the specification for a level
1102  * taken from the ship file.
1103  *
1104  * @return pointer to the level
1105  * @param text buffer containing level description
1106  */
decode_level(char ** buffer)1107 static level *decode_level(char **buffer)
1108 {
1109 	level *loadlevel;
1110 	char *data = *buffer;
1111 
1112 	loadlevel = (level *)MyMalloc(sizeof(level));
1113 
1114 	if (decode_header(loadlevel, data)) {
1115 		error_message(__FUNCTION__, "Unable to decode level header!", PLEASE_INFORM | IS_FATAL);
1116 	}
1117 
1118 	// The order of sections in the file has to match this.
1119 	data = decode_map(loadlevel, data);
1120 	if (!data) {
1121 		error_message(__FUNCTION__, "Unable to decode the map for level %d", PLEASE_INFORM | IS_FATAL, loadlevel->levelnum);
1122 	}
1123 	data = decode_obstacles(loadlevel, data);
1124 	data = decode_map_labels(loadlevel, data);
1125 	data = decode_item_section(loadlevel, data);
1126 	data = decode_obstacle_extensions(loadlevel, data);
1127 	data = decode_waypoints(loadlevel, data);
1128 
1129 	// Due to currently unknown bugs, some obstacles can be located on invalid
1130 	// positions. If we are starting the lvleditor, we will warn the user but
1131 	// keep those obstacles to help investigating the bugs. Otherwise, those
1132 	// obstacles and their associated extension are silently removed.
1133 	int i;
1134 	int need_defrag = FALSE;
1135 	struct auto_string *error_msg = alloc_autostr(256);
1136 
1137 	for (i = 0; i < MAX_OBSTACLES_ON_MAP; i++) {
1138 		struct obstacle *obs = &loadlevel->obstacle_list[i];
1139 		if (obs->type == -1)
1140 			continue;
1141 		if (!pos_inside_level(obs->pos.x, obs->pos.y, loadlevel)) {
1142 			if (game_root_mode == ROOT_IS_LVLEDIT && game_status == INSIDE_LVLEDITOR) {
1143 				autostr_append(error_msg, "Invalid obstacle (%s) position on level %d: t%d x%3.2f y%3.2f\n",
1144 						((char **)get_obstacle_spec(obs->type)->filenames.arr)[0],
1145 						loadlevel->levelnum, obs->type, obs->pos.x, obs->pos.y);
1146 				alert_once_window(ONCE_PER_GAME,
1147 						_("-- WARNING --\n"
1148 						  "An obstacle with invalid coords was loaded onto a map.\n"
1149 						  "We keep it to help finding the bug.\n"
1150 						  "See the report in your terminal console."));
1151 			} else if (game_root_mode != ROOT_IS_LVLEDIT) {
1152 				unglue_obstacle(loadlevel, obs);
1153 				obs->type = -1;
1154 				del_obstacle_extensions(loadlevel, obs);
1155 				need_defrag = TRUE;
1156 			}
1157 		}
1158 	}
1159 	if (error_msg->length != 0) {
1160 		// Remove the last carriage return
1161 		error_msg->value[error_msg->length - 1] = '\0';
1162 		error_message(__FUNCTION__,	"%s", NO_REPORT, error_msg->value);
1163 	}
1164 	free_autostr(error_msg);
1165 	if (need_defrag)
1166 		defrag_obstacle_array(loadlevel);
1167 
1168 	// Point the buffer to the end of this level, so the next level can be read
1169 	*buffer = data;
1170 	return loadlevel;
1171 }
1172 
1173 
1174 /**
1175  * Call the random dungeon generator on this level  if this level is marked
1176  * as being randomly generated and if we are not in the "leveleditor" mode
1177  * in which case random dungeons must not be considered as generated (so that
1178  * they will be exported as being non-generated random levels).
1179  */
generate_dungeon_if_needed(level * l)1180 static void generate_dungeon_if_needed(level *l)
1181 {
1182 	if (!l->random_dungeon || l->dungeon_generated) {
1183 		return;
1184 	}
1185 
1186 	// Generate random dungeon now
1187 	set_dungeon_output(l);
1188 	generate_dungeon(l->xlen, l->ylen, l->random_dungeon, l->teleport_pair);
1189 	l->dungeon_generated = 1;
1190 }
1191 
free_ship_level(level * lvl)1192 void free_ship_level(level *lvl)
1193 {
1194 	int row = 0;
1195 	int col = 0;
1196 
1197 	// Map tiles
1198 	for (row = 0; row < lvl->ylen; row++) {
1199 		if (lvl->map[row]) {
1200 			for (col = 0; col < lvl->xlen; col++) {
1201 				dynarray_free(&lvl->map[row][col].glued_obstacles);
1202 			}
1203 
1204 			free(lvl->map[row]);
1205 			lvl->map[row] = NULL;
1206 		}
1207 	}
1208 
1209 	// Level strings
1210 	if (lvl->Levelname) {
1211 		free(lvl->Levelname);
1212 		lvl->Levelname = NULL;
1213 	}
1214 
1215 	if (lvl->Background_Song_Name) {
1216 		free(lvl->Background_Song_Name);
1217 		lvl->Background_Song_Name = NULL;
1218 	}
1219 
1220 	// Waypoints
1221 	int w;
1222 	for (w = 0; w < lvl->waypoints.size; w++) {
1223 		struct waypoint *wpts = lvl->waypoints.arr;
1224 		dynarray_free(&wpts[w].connections);
1225 	}
1226 
1227 	dynarray_free(&lvl->waypoints);
1228 
1229 	// Obstacle extensions
1230 	free_obstacle_extensions(lvl);
1231 
1232 	// Map labels
1233 	free_map_labels(lvl);
1234 
1235 	// Random droids
1236 	lvl->random_droids.types_size = 0;
1237 
1238 	// Items
1239 	for (w = 0; w < MAX_ITEMS_PER_LEVEL; w++) {
1240 		if (lvl->ItemList[w].type != -1) {
1241 			delete_upgrade_sockets(&(lvl->ItemList[w]));
1242 		}
1243 	}
1244 
1245 	free(lvl);
1246 }
1247 
free_current_ship()1248 void free_current_ship()
1249 {
1250 	struct level *lvl;
1251 	BROWSE_LEVELS(lvl) {
1252 		int lvlnum = lvl->levelnum;
1253 		free_ship_level(lvl);
1254 		curShip.AllLevels[lvlnum] = NULL;
1255 	}
1256 	curShip.num_levels = 0;
1257 }
1258 
1259 /**
1260  * This function loads the data for a whole ship
1261  * Possible return values are : OK and ERR
1262  */
LoadShip(char * filename,int compressed)1263 int LoadShip(char *filename, int compressed)
1264 {
1265 	char *ShipData = NULL;
1266 	FILE *ShipFile;
1267 
1268 #define END_OF_SHIP_DATA_STRING "*** End of Ship Data ***"
1269 
1270 	// Free existing level data
1271 	free_current_ship();
1272 
1273 	// Read the whole ship-data to memory
1274 	//
1275 	ShipFile = fopen(filename, "rb");
1276 	if (!ShipFile) {
1277 		error_message(__FUNCTION__, "Unable to open ship file %s: %s.", PLEASE_INFORM | IS_FATAL, filename, strerror(errno));
1278 	}
1279 
1280 	if (compressed) {
1281 		if (inflate_stream(ShipFile, (unsigned char **)&ShipData, NULL)) {
1282 			error_message(__FUNCTION__, "Unable to decompress ship file %s.", PLEASE_INFORM | IS_FATAL, filename);
1283 		}
1284 	} else {
1285 		int length = FS_filelength(ShipFile);
1286 		ShipData = malloc(length + 1);
1287 		if (!fread(ShipData, length, 1, ShipFile))
1288 			error_message(__FUNCTION__, "Reading ship file %s failed with fread().", PLEASE_INFORM | IS_FATAL, filename);
1289 		ShipData[length] = 0;
1290 	}
1291 
1292 	fclose(ShipFile);
1293 
1294 	// Read each level
1295 	int done = 0;
1296 	char *pos = ShipData;
1297 	while (!done) {
1298 		level *this_level = decode_level(&pos);
1299 		int this_levelnum = this_level->levelnum;
1300 
1301 		if (this_levelnum >= MAX_LEVELS)
1302 			error_message(__FUNCTION__, "One levelnumber in savegame (%d) is bigger than the maximum allowed (%d).",
1303 					PLEASE_INFORM | IS_FATAL, this_levelnum, MAX_LEVELS - 1);
1304 		if (level_exists(this_levelnum))
1305 			error_message(__FUNCTION__, "Two levels with same levelnumber (%d) found in the savegame.",
1306 					PLEASE_INFORM | IS_FATAL, this_levelnum);
1307 
1308 		curShip.AllLevels[this_levelnum] = this_level;
1309 		curShip.num_levels = this_levelnum + 1;
1310 
1311 		generate_dungeon_if_needed(this_level);
1312 
1313 		// Move to the level termination marker
1314 		pos = strstr(pos, LEVEL_END_STRING);
1315 		pos += strlen(LEVEL_END_STRING) + 1;
1316 
1317 		// Check if there is another level
1318 		if (!strstr(pos, LEVEL_HEADER_LEVELNUMBER)) {
1319 			done = 1;
1320 		}
1321 	}
1322 
1323 	// Now that all the information has been copied, we can free the loaded data
1324 	// again.
1325 	//
1326 	free(ShipData);
1327 
1328 	// Compute the gps transform acceleration data
1329 	gps_transform_map_dirty_flag = TRUE;
1330 	gps_transform_map_init();
1331 
1332 	return OK;
1333 
1334 };				// int LoadShip ( ... )
1335 
1336 /**
1337  * This should write the obstacle information in human-readable form into
1338  * a buffer.
1339  */
encode_obstacles_of_this_level(struct auto_string * shipstr,struct level * lvl)1340 static void encode_obstacles_of_this_level(struct auto_string *shipstr, struct level *lvl)
1341 {
1342 	int i;
1343 	struct auto_string *error_msg = alloc_autostr(256);
1344 
1345 	defrag_obstacle_array(lvl);
1346 
1347 	autostr_append(shipstr, "%s\n", OBSTACLE_DATA_BEGIN_STRING);
1348 
1349 	for (i = 0; i < MAX_OBSTACLES_ON_MAP; i++) {
1350 		if (lvl->obstacle_list[i].type == (-1))
1351 			continue;
1352 
1353 		// Invalid obstacles are saved, but with a warning message unless we
1354 		// are playtesting, to enable further inspection of the bug.
1355 		// Anyhow, invalid obstacles are not loaded.
1356 		if (!pos_inside_level(lvl->obstacle_list[i].pos.x, lvl->obstacle_list[i].pos.y, lvl)) {
1357 			if (game_root_mode != ROOT_IS_LVLEDIT) {
1358 				error_once_message(ONCE_PER_GAME, __FUNCTION__,
1359 					"Some obstacles with an invalid position were found"
1360 					" while saving them. See the individual reports below.", PLEASE_INFORM);
1361 			}
1362 			if (game_root_mode != ROOT_IS_LVLEDIT || game_status == INSIDE_LVLEDITOR) {
1363 				autostr_append(error_msg, "Invalid obstacle (%s) position on level %d: t%d x%3.2f y%3.2f\n",
1364 						((char **)get_obstacle_spec(lvl->obstacle_list[i].type)->filenames.arr)[0],
1365 						lvl->levelnum, lvl->obstacle_list[i].type, lvl->obstacle_list[i].pos.x, lvl->obstacle_list[i].pos.y);
1366 			}
1367 			// If we are inside the lvleditor, also display an alert
1368 			if (game_root_mode == ROOT_IS_LVLEDIT && game_status == INSIDE_LVLEDITOR) {
1369 				alert_once_window(ONCE_PER_GAME,
1370 						_("-- WARNING --\n"
1371 						  "An obstacle with invalid coords is saved to a map.\n"
1372 						  "We accept to save it, for further inspection of the bug.\n"
1373 						  "See the report in your terminal console."));
1374 			}
1375 		}
1376 		autostr_append(shipstr, "%s%d %s%3.2f %s%3.2f\n", OBSTACLE_TYPE_STRING, lvl->obstacle_list[i].type,
1377 				OBSTACLE_X_POSITION_STRING, lvl->obstacle_list[i].pos.x, OBSTACLE_Y_POSITION_STRING,
1378 				lvl->obstacle_list[i].pos.y);
1379 	}
1380 
1381 	autostr_append(shipstr, "%s\n", OBSTACLE_DATA_END_STRING);
1382 
1383 	if (error_msg->length != 0) {
1384 		// Remove the last carriage return
1385 		error_msg->value[error_msg->length - 1] = '\0';
1386 		error_message(__FUNCTION__,	"%s", NO_REPORT, error_msg->value);
1387 	}
1388 	free_autostr(error_msg);
1389 }
1390 
encode_map_labels(struct auto_string * shipstr,level * lvl)1391 static void encode_map_labels(struct auto_string *shipstr, level *lvl)
1392 {
1393 	int i;
1394 	struct map_label *map_label;
1395 
1396 	autostr_append(shipstr, "%s\n", MAP_LABEL_BEGIN_STRING);
1397 
1398 	for (i = 0; i < lvl->map_labels.size; i++) {
1399 		// Get the map label
1400 		map_label = &ACCESS_MAP_LABEL(lvl->map_labels, i);
1401 
1402 		// Encode map label
1403 		autostr_append(shipstr, "%s%d %s%d %s%s\"\n", X_POSITION_OF_LABEL_STRING, map_label->pos.x, Y_POSITION_OF_LABEL_STRING,
1404 				            map_label->pos.y, LABEL_ITSELF_ANNOUNCE_STRING, map_label->label_name);
1405 	}
1406 
1407 	autostr_append(shipstr, "%s\n", MAP_LABEL_END_STRING);
1408 }
1409 
1410 /**
1411  *
1412  *
1413  */
WriteOutOneItem(struct auto_string * shipstr,item * ItemToWriteOut)1414 static void WriteOutOneItem(struct auto_string *shipstr, item *ItemToWriteOut)
1415 {
1416 
1417 	autostr_append(shipstr, "%s%s\" %s%f %s%f ", ITEM_ID_STRING, ItemMap[ItemToWriteOut->type].id,
1418 			ITEM_POS_X_STRING, ItemToWriteOut->pos.x, ITEM_POS_Y_STRING, ItemToWriteOut->pos.y);
1419 
1420 	if (ItemToWriteOut->armor_class) {
1421 		autostr_append(shipstr, "%s%d ", ITEM_ARMOR_CLASS_BASE_STRING, ItemToWriteOut->armor_class);
1422 	}
1423 
1424 	autostr_append(shipstr, "%s%d %s%f %s%d %s%d",
1425 			ITEM_MAX_DURABILITY_STRING, ItemToWriteOut->max_durability,
1426 			ITEM_CUR_DURABILITY_STRING, ItemToWriteOut->current_durability,
1427 			ITEM_AMMO_CLIP_STRING, ItemToWriteOut->ammo_clip,
1428 			ITEM_MULTIPLICITY_STRING, ItemToWriteOut->multiplicity);
1429 
1430 	// Write the sockets of the item. The bonuses can be reconstructed from
1431 	// these easily so we don't need to write them at all.
1432 	if (ItemToWriteOut->upgrade_sockets.size) {
1433 		int i;
1434 		autostr_append(shipstr, "%s%d ", ITEM_SOCKETS_SIZE_STRING, ItemToWriteOut->upgrade_sockets.size);
1435 		for (i = 0; i < ItemToWriteOut->upgrade_sockets.size; i++) {
1436 			struct upgrade_socket *socket = &ItemToWriteOut->upgrade_sockets.arr[i];
1437 			autostr_append(shipstr, "%s%d=%d ", ITEM_SOCKET_TYPE_STRING, i, socket->type);
1438 			if (socket->addon) {
1439 				autostr_append(shipstr, "%s%d=%s\" ", ITEM_SOCKET_ADDON_STRING, i, socket->addon);
1440 			}
1441 		}
1442 	}
1443 
1444 	autostr_append(shipstr, "\n");
1445 }
1446 
EncodeItemSectionOfThisLevel(struct auto_string * shipstr,level * Lev)1447 static void EncodeItemSectionOfThisLevel(struct auto_string *shipstr, level *Lev)
1448 {
1449 	int i;
1450 
1451 	autostr_append(shipstr, "%s\n", ITEMS_SECTION_BEGIN_STRING);
1452 
1453 	// Now we write out the bulk of items infos
1454 	//
1455 	for (i = 0; i < MAX_ITEMS_PER_LEVEL; i++) {
1456 		if (Lev->ItemList[i].type == (-1))
1457 			continue;
1458 
1459 		WriteOutOneItem(shipstr, &(Lev->ItemList[i]));
1460 
1461 	}
1462 
1463 	autostr_append(shipstr, "%s\n", ITEMS_SECTION_END_STRING);
1464 }
1465 
encode_extension_chest(struct auto_string * shipstr,struct obstacle_extension * ext)1466 static void encode_extension_chest(struct auto_string *shipstr, struct obstacle_extension *ext)
1467 {
1468 	int i;
1469 	struct dynarray *da = ext->data;
1470 
1471 	for (i = 0; i < da->size; i++) {
1472 		item *it = &((item *)da->arr)[i];
1473 		if (it->type == -1)
1474 			continue;
1475 
1476 		autostr_append(shipstr, "\t");
1477 		WriteOutOneItem(shipstr, it);
1478 	}
1479 }
1480 
encode_extension_label(struct auto_string * shipstr,struct obstacle_extension * ext)1481 static void encode_extension_label(struct auto_string *shipstr, struct obstacle_extension *ext)
1482 {
1483 	const char *label = ext->data;
1484 
1485 	autostr_append(shipstr, "\t\"%s\"\n", label);
1486 }
1487 
encode_extension_dialog(struct auto_string * shipstr,struct obstacle_extension * ext)1488 static void encode_extension_dialog(struct auto_string *shipstr, struct obstacle_extension *ext)
1489 {
1490 	const char *label = ext->data;
1491 
1492 	autostr_append(shipstr, "\t\"%s\"\n", label);
1493 }
1494 
encode_extension_signmessage(struct auto_string * shipstr,struct obstacle_extension * ext)1495 static void encode_extension_signmessage(struct auto_string *shipstr, struct obstacle_extension *ext)
1496 {
1497 	const char *label = ext->data;
1498 
1499 	// Sign messages can be localized. Use gettext marker.
1500 	autostr_append(shipstr, "\t_\"%s\"\n", label);
1501 }
1502 
encode_obstacle_extensions(struct auto_string * shipstr,level * l)1503 static void encode_obstacle_extensions(struct auto_string *shipstr, level *l)
1504 {
1505 	int i;
1506 	autostr_append(shipstr, "%s\n", OBSTACLE_EXTENSIONS_BEGIN_STRING);
1507 	for (i = 0; i < l->obstacle_extensions.size; i++) {
1508 		struct obstacle_extension *ext = &ACCESS_OBSTACLE_EXTENSION(l->obstacle_extensions, i);
1509 
1510 		if (ext->type == 0)
1511 			continue;
1512 
1513 		autostr_append(shipstr, "idx=%d type=%d data={\n", get_obstacle_index(l, ext->obs), ext->type);
1514 
1515 		switch ((enum obstacle_extension_type)(ext->type)) {
1516 			case OBSTACLE_EXTENSION_CHEST_ITEMS:
1517 				encode_extension_chest(shipstr, ext);
1518 				break;
1519 			case OBSTACLE_EXTENSION_LABEL:
1520 				encode_extension_label(shipstr, ext);
1521 				break;
1522 			case OBSTACLE_EXTENSION_DIALOGFILE:
1523 				encode_extension_dialog(shipstr, ext);
1524 				break;
1525 			case OBSTACLE_EXTENSION_SIGNMESSAGE:
1526 				encode_extension_signmessage(shipstr, ext);
1527 				break;
1528 		}
1529 
1530 		autostr_append(shipstr, "}\n");
1531 	}
1532 	autostr_append(shipstr, "%s\n", OBSTACLE_EXTENSIONS_END_STRING);
1533 }
1534 
encode_waypoints(struct auto_string * shipstr,level * lvl)1535 static void encode_waypoints(struct auto_string *shipstr, level *lvl)
1536 {
1537 	waypoint *wpts = lvl->waypoints.arr;
1538 	int *connections;
1539 	int i, j;
1540 
1541 	autostr_append(shipstr, "%s\n", WP_BEGIN_STRING);
1542 
1543 	for (i = 0; i < lvl->waypoints.size; i++) {
1544 		// Encode the waypoint
1545 		autostr_append(shipstr, "Nr.=%3d x=%4d y=%4d rnd=%1d\t %s", i, wpts[i].x, wpts[i].y, wpts[i].suppress_random_spawn, CONNECTION_STRING);
1546 
1547 		// Get the connections of the waypoint
1548 		connections = wpts[i].connections.arr;
1549 
1550 		for (j = 0; j < wpts[i].connections.size; j++) {
1551 			// Check connected waypoint validity
1552 			int connected_waypoint = connections[j];
1553 			if (connected_waypoint < 0 || connected_waypoint >= lvl->waypoints.size) {
1554 				error_message(__FUNCTION__, "A connection to an invalid waypoint (#%d) was found while encoding level #%d\n."
1555 				              "We discard it.", PLEASE_INFORM, connected_waypoint, lvl->levelnum);
1556 				continue;
1557 			}
1558 			// Encode the connection of the waypoint
1559 			autostr_append(shipstr, " %3d", connected_waypoint);
1560 		}
1561 
1562 		autostr_append(shipstr, "\n");
1563 	}
1564 }
1565 
1566 /**
1567  * This function translates map data into human readable map code, that
1568  * can later be written to the map file on disk.
1569  */
TranslateToHumanReadable(struct auto_string * str,map_tile * MapInfo,int LineLength,int layers)1570 static void TranslateToHumanReadable(struct auto_string *str, map_tile * MapInfo, int LineLength, int layers)
1571 {
1572 	int col;
1573 	int layer;
1574 
1575 	for (col = 0; col < LineLength; col++) {
1576 		for (layer = 0; layer < layers; layer++)
1577 			autostr_append(str, "%3d ", MapInfo[col].floor_values[layer]);
1578 	}
1579 
1580 	autostr_append(str, "\n");
1581 }
1582 
1583 /**
1584  * This function generates savable text out of the current level data
1585  *
1586  * If reset_random_levels is TRUE, then the random levels are saved
1587  * "un-generated" (typical usage: levels.dat).
1588  *
1589  */
encode_level_for_saving(struct auto_string * shipstr,level * lvl,int reset_random_levels)1590 static void encode_level_for_saving(struct auto_string *shipstr, level *lvl, int reset_random_levels)
1591 {
1592 	int i;
1593 	int xlen = lvl->xlen, ylen = lvl->ylen;
1594 
1595 	// Write level header
1596 	autostr_append(shipstr, "%s %d\n\
1597 xlen of this level: %d\n\
1598 ylen of this level: %d\n\
1599 floor layers: %d\n\
1600 light radius bonus of this level: %d\n\
1601 minimal light on this level: %d\n\
1602 infinite_running_on_this_level: %d\n\
1603 random dungeon: %d\n\
1604 teleport pair: %d\n\
1605 dungeon generated: %d\n\
1606 environmental flags: %d\n\
1607 item drop class: %d\n\
1608 jump target north: %d\n\
1609 jump target south: %d\n\
1610 jump target east: %d\n\
1611 jump target west: %d\n", LEVEL_HEADER_LEVELNUMBER, lvl->levelnum,
1612 		lvl->xlen, lvl->ylen, lvl->floor_layers,
1613 		lvl->light_bonus, lvl->minimum_light_value,
1614 		lvl->infinite_running_on_this_level,
1615 		lvl->random_dungeon,
1616 		lvl->teleport_pair,
1617 		(reset_random_levels && lvl->random_dungeon) ? 0 : lvl->dungeon_generated,
1618 		lvl->flags,
1619 		lvl->drop_class,
1620 		lvl->jump_target_north,
1621 		lvl->jump_target_south,
1622 		lvl->jump_target_east,
1623 		lvl->jump_target_west);
1624 
1625 	autostr_append(shipstr, "number of random droids: %d\n", lvl->random_droids.nr);
1626 	autostr_append(shipstr, "random droid types: ");
1627 
1628 	for (i = 0; i < lvl->random_droids.types_size; i++) {
1629 		if (i)
1630 			autostr_append(shipstr, ", ");
1631 		autostr_append(shipstr, "%s", Droidmap[lvl->random_droids.types[i]].droidname);
1632 	}
1633 
1634 	autostr_append(shipstr, "\n%s%s\"\n%s%s\n", LEVEL_NAME_STRING, lvl->Levelname,
1635 			BACKGROUND_SONG_NAME_STRING, lvl->Background_Song_Name);
1636 
1637 	autostr_append(shipstr, "%s\n", MAP_BEGIN_STRING);
1638 
1639 	// Now in the loop each line of map data should be saved as a whole
1640 	for (i = 0; i < ylen; i++) {
1641 		if (!(reset_random_levels && lvl->random_dungeon)) {
1642 			TranslateToHumanReadable(shipstr, lvl->map[i], xlen, lvl->floor_layers);
1643 		} else {
1644 			int j = xlen;
1645 			while (j--) {
1646 				autostr_append(shipstr, "  0 ");
1647 			}
1648 			autostr_append(shipstr, "\n");
1649 		}
1650 	}
1651 
1652 	autostr_append(shipstr, "%s\n", MAP_END_STRING);
1653 
1654 	if (!(reset_random_levels && lvl->random_dungeon)) {
1655 		encode_obstacles_of_this_level(shipstr, lvl);
1656 
1657 		encode_map_labels(shipstr, lvl);
1658 
1659 		EncodeItemSectionOfThisLevel(shipstr, lvl);
1660 
1661 		encode_obstacle_extensions(shipstr, lvl);
1662 
1663 		encode_waypoints(shipstr, lvl);
1664 	}
1665 
1666 	autostr_append(shipstr, "%s\n----------------------------------------------------------------------\n",
1667 			LEVEL_END_STRING);
1668 }
1669 
1670 /**
1671  * This function should save a whole ship to disk to the given filename.
1672  * It is not only used by the level editor, but also by the function that
1673  * saves whole games.
1674  *
1675  * If reset_random_levels is TRUE, then the random levels are saved
1676  * "un-generated" (typical usage: levels.dat).
1677  * @return 0 if OK, 1 on error
1678  */
SaveShip(const char * filename,int reset_random_levels,int compress)1679 int SaveShip(const char *filename, int reset_random_levels, int compress)
1680 {
1681 	int i;
1682 	FILE *ShipFile = NULL;
1683 	struct auto_string *shipstr;
1684 
1685 	// Open the ship file
1686 	if ((ShipFile = fopen(filename, "wb")) == NULL) {
1687 		error_message(__FUNCTION__, "Error opening ship file %s for writing.", NO_REPORT, filename);
1688 		return ERR;
1689 	}
1690 
1691 	shipstr	= alloc_autostr(1048576);
1692 	autostr_printf(shipstr, "\n");
1693 
1694 	// Save all the levels
1695 	for (i = 0; i < curShip.num_levels; i++) {
1696 		if (level_exists(i)) {
1697 			encode_level_for_saving(shipstr, curShip.AllLevels[i], reset_random_levels);
1698 		}
1699 	}
1700 
1701 	autostr_append(shipstr, "%s\n\n", END_OF_SHIP_DATA_STRING);
1702 
1703 	if (compress) {
1704 		deflate_to_stream((unsigned char *)shipstr->value, shipstr->length, ShipFile);
1705 	}	else {
1706 		if (fwrite((unsigned char *)shipstr->value, shipstr->length, 1, ShipFile) != 1) {
1707 			error_message(__FUNCTION__, "Error writing ship file %s.", PLEASE_INFORM, filename);
1708 			fclose(ShipFile);
1709 			free_autostr(shipstr);
1710 			return ERR;
1711 		}
1712 	}
1713 
1714 	if (fclose(ShipFile) == EOF) {
1715 		error_message(__FUNCTION__, "Closing of ship file failed!", PLEASE_INFORM);
1716 		free_autostr(shipstr);
1717 		return ERR;
1718 	}
1719 
1720 	free_autostr(shipstr);
1721 	return OK;
1722 }
1723 
save_special_forces(const char * filename)1724 int save_special_forces(const char *filename)
1725 {
1726 	FILE *s_forces_file = NULL;
1727 	struct auto_string *s_forces_str;
1728 	level *lvl;
1729 	int i;
1730 
1731 	if ((s_forces_file = fopen(filename, "wb")) == NULL) {
1732 		error_message(__FUNCTION__, "Error opening Special Forces file %s for writing.", NO_REPORT, filename);
1733 		return ERR;
1734 	}
1735 
1736 	s_forces_str = alloc_autostr(64);
1737 
1738 	for (i = 0; i < curShip.num_levels; i++) {
1739 		if (!level_exists(i))
1740 			continue;
1741 
1742 		lvl = curShip.AllLevels[i];
1743 		autostr_append(s_forces_str, "** Beginning of new Level **\n");
1744 		autostr_append(s_forces_str, "Level=%d\n\n",  lvl->levelnum);
1745 
1746 		enemy *en;
1747 
1748 		list_for_each_entry_reverse(en, &level_bots_head[lvl->levelnum], level_list) {
1749 			if (!en->SpecialForce)
1750 				continue;
1751 
1752 			autostr_append(s_forces_str, "T=%s: ", Droidmap[en->type].droidname);
1753 			autostr_append(s_forces_str, "PosX=%d PosY=%d ", (int)en->pos.x, (int)en->pos.y);
1754 			autostr_append(s_forces_str, "Faction=\"%s\" ", get_faction_from_id(en->faction));
1755 			autostr_append(s_forces_str, "UseDialog=\"%s\" ", en->dialog_section_name);
1756 
1757 			autostr_append(s_forces_str, "ShortLabel=_\"%s\" ", en->short_description_text);
1758 			autostr_append(s_forces_str, "Marker=%d ", en->marker);
1759 			autostr_append(s_forces_str, "RushTux=%d ", en->will_rush_tux);
1760 
1761 			autostr_append(s_forces_str, "Fixed=%hi ", en->CompletelyFixed);
1762 			autostr_append(s_forces_str, "DropItemId=\"%s\" ",
1763 						(en->on_death_drop_item_code == -1) ? "none" : ItemMap[en->on_death_drop_item_code].id);
1764 			autostr_append(s_forces_str, "MaxDistanceToHome=%hd", en->max_distance_to_home);
1765 			if (en->sensor_id != Droidmap[en->type].sensor_id) // Only save sensor if it's not the default.
1766 				autostr_append(s_forces_str, " UseSensor=\"%s\"", get_sensor_name_by_id(en->sensor_id));
1767 			autostr_append(s_forces_str, "\n");
1768 		}
1769 
1770 		autostr_append(s_forces_str, "** End of this levels Special Forces data **\n");
1771 		autostr_append(s_forces_str, "---------------------------------------------------------\n");
1772 	}
1773 
1774 	autostr_append(s_forces_str, "*** End of Droid Data ***\n");
1775 
1776 	if (fwrite((unsigned char *)s_forces_str->value, s_forces_str->length, 1, s_forces_file) != 1) {
1777 		error_message(__FUNCTION__, "Error writing SpecialForces file %s.", PLEASE_INFORM, filename);
1778 		fclose(s_forces_file);
1779 		goto out;
1780 	}
1781 
1782 	if (fclose(s_forces_file) == EOF) {
1783 		error_message(__FUNCTION__, "Closing of Special Forces file failed!", PLEASE_INFORM);
1784 		goto out;
1785 	}
1786 
1787 out:	free_autostr(s_forces_str);
1788 	return OK;
1789 }
1790 
1791 /**
1792  * This function is used to calculate the number of the droids on the
1793  * ship, which is a global variable.
1794  */
CountNumberOfDroidsOnShip(void)1795 void CountNumberOfDroidsOnShip(void)
1796 {
1797 	Number_Of_Droids_On_Ship = 0;
1798 
1799 	enemy *erot;
1800 	BROWSE_ALIVE_BOTS(erot) {
1801 		Number_Of_Droids_On_Ship++;
1802 	}
1803 
1804 	BROWSE_DEAD_BOTS(erot) {
1805 		Number_Of_Droids_On_Ship++;
1806 	}
1807 
1808 };				// void CountNumberOfDroidsOnShip ( void )
1809 
1810 /* -----------------------------------------------------------------
1811  * This function initializes all enemies, which means that enemies are
1812  * filled in into the enemy list according to the enemies types that
1813  * are to be found on each deck.
1814  * ----------------------------------------------------------------- */
GetCrew(char * filename)1815 int GetCrew(char *filename)
1816 {
1817 	char fpath[PATH_MAX];
1818 	char *MainDroidsFilePointer;
1819 	char *DroidSectionPointer;
1820 	char *EndOfThisDroidSectionPointer;
1821 
1822 #define START_OF_DROID_DATA_STRING "*** Beginning of Droid Data ***"
1823 #define END_OF_DROID_DATA_STRING "*** End of Droid Data ***"
1824 #define DROIDS_LEVEL_DESCRIPTION_START_STRING "** Beginning of new Level **"
1825 #define DROIDS_LEVEL_DESCRIPTION_END_STRING "** End of this levels Special Forces data **"
1826 
1827 	//Now its time to start decoding the droids file.
1828 	//For that, we must get it into memory first.
1829 	//The procedure is the same as with LoadShip
1830 	//
1831 	find_file(filename, MAP_DIR, fpath, PLEASE_INFORM | IS_FATAL);
1832 
1833 	MainDroidsFilePointer = ReadAndMallocAndTerminateFile(fpath, END_OF_DROID_DATA_STRING);
1834 
1835 	// The Droid crew file for this map is now completely read into memory
1836 	// It's now time to decode the file and to fill the array of enemys with
1837 	// new droids of the given types.
1838 	//
1839 	enemy_reset_fabric();
1840 
1841 	DroidSectionPointer = MainDroidsFilePointer;
1842 	while ((DroidSectionPointer = strstr(DroidSectionPointer, DROIDS_LEVEL_DESCRIPTION_START_STRING)) != NULL) {
1843 		DroidSectionPointer += strlen(DROIDS_LEVEL_DESCRIPTION_START_STRING);
1844 		DebugPrintf(1, "\nFound another levels droids description starting point entry!");
1845 		EndOfThisDroidSectionPointer = strstr(DroidSectionPointer, DROIDS_LEVEL_DESCRIPTION_END_STRING);
1846 		if (EndOfThisDroidSectionPointer == NULL) {
1847 			error_message(__FUNCTION__, "Unterminated droid section encountered!", PLEASE_INFORM | IS_FATAL);
1848 		}
1849 		// EndOfThisDroidSectionPointer[0]=0;
1850 		GetThisLevelsDroids(DroidSectionPointer);
1851 		DroidSectionPointer = EndOfThisDroidSectionPointer + 2;	// Move past the inserted String terminator
1852 	}
1853 
1854 	free(MainDroidsFilePointer);
1855 	return (OK);
1856 
1857 };				// int GetCrew ( ... )
1858 
1859 /**
1860  *
1861  *
1862  */
GetThisLevelsSpecialForces(char * search_pointer,int our_level_number,char * lvl_end_location)1863 static void GetThisLevelsSpecialForces(char *search_pointer, int our_level_number, char *lvl_end_location)
1864 {
1865 	int droid_type;
1866 #define SPECIAL_FORCE_INDICATION_STRING "T="
1867 
1868 	while ((search_pointer = strstr(search_pointer, SPECIAL_FORCE_INDICATION_STRING)) != NULL) {
1869 		char *special_droid = ReadAndMallocStringFromData(search_pointer, SPECIAL_FORCE_INDICATION_STRING, "\n");
1870 		char *special_droid_end = special_droid + strlen(special_droid);
1871 		search_pointer += strlen(SPECIAL_FORCE_INDICATION_STRING);
1872 		//identify what model of droid to display:
1873 		char *ptr = special_droid;
1874 		int droid_type_length = 0;
1875 		while (isalnum(*ptr)) {
1876 			ptr++;
1877 			droid_type_length++;
1878 		}
1879 		char type_indication_string[droid_type_length + 1];
1880 		strncpy(type_indication_string, special_droid, droid_type_length);
1881 		type_indication_string[droid_type_length] = 0;
1882 
1883 		droid_type = get_droid_type(type_indication_string);
1884 
1885 		// Create a new enemy, and initialize its 'identity' and 'global state'
1886 		// (the enemy will be fully initialized by respawn_level())
1887 
1888 
1889 		enemy *newen = enemy_new(droid_type);
1890 
1891 
1892 		newen->SpecialForce = TRUE;
1893 
1894 
1895 		char *tmp_sensor_ID = ReadAndMallocStringFromDataOptional(special_droid, "UseSensor=\"","\"");
1896 		if (!tmp_sensor_ID)
1897 			newen->sensor_id=Droidmap[newen->type].sensor_id;	//Not declared? In this case, use default.
1898 		else
1899 			newen->sensor_id=get_sensor_id_by_name(tmp_sensor_ID);	// Otherwise, use the specified sensor!
1900 		free(tmp_sensor_ID);
1901 
1902 		ReadValueFromStringWithDefault(special_droid, "Fixed=", "%hd", "0", &(newen->CompletelyFixed), special_droid_end);
1903 		ReadValueFromStringWithDefault(special_droid, "Marker=", "%d", "0000", &(newen->marker), special_droid_end);
1904 		ReadValueFromStringWithDefault(special_droid, "MaxDistanceToHome=", "%hd", "0", &(newen->max_distance_to_home),
1905 					       special_droid_end);
1906 
1907 		char *faction = ReadAndMallocStringFromData(special_droid, "Faction=\"", "\"");
1908 		newen->faction = get_faction_id(faction);
1909 		free(faction);
1910 
1911 		char *x, *y;
1912 		x = ReadAndMallocStringFromData(special_droid, "PosX=", " ");
1913 		y = ReadAndMallocStringFromData(special_droid, "PosY=", " ");
1914 
1915 		newen->pos.x = strtof(x, NULL);
1916 		newen->pos.y = strtof(y, NULL);
1917 		newen->pos.z = our_level_number;
1918 		free(x);
1919 		free(y);
1920 
1921 		ReadValueFromStringWithDefault(special_droid, "RushTux=", "%hu", "0", &(newen->will_rush_tux), special_droid_end);
1922 
1923 		newen->dialog_section_name = ReadAndMallocStringFromData(special_droid, "UseDialog=\"", "\"");
1924 		npc_get(newen->dialog_section_name); // Check that we have a valid dialog.
1925 
1926 		if (newen->short_description_text)
1927 			free(newen->short_description_text);
1928 
1929 		newen->short_description_text = ReadAndMallocStringFromDataOptional(special_droid, "ShortLabel=_\"", "\"");
1930 		if (!newen->short_description_text) {
1931 			newen->short_description_text = ReadAndMallocStringFromData(special_droid, "ShortLabel=\"", "\"");
1932 		}
1933 
1934 		char *death_drop;
1935 		death_drop = ReadAndMallocStringFromData(special_droid, "DropItemId=\"", "\"");
1936 		if (strcmp(death_drop, "none")) {
1937 			newen->on_death_drop_item_code = get_item_type_by_id(death_drop);
1938 		} else {
1939 			newen->on_death_drop_item_code = -1;
1940 		}
1941 		free(death_drop);
1942 		free(special_droid);
1943 		enemy_insert_into_lists(newen, TRUE);
1944 	}
1945 
1946 };
1947 
1948 /**
1949  * This function receives a pointer to the already read in crew section
1950  * in a already read in droids file and decodes all the contents of that
1951  * droid section to fill the AllEnemys array with droid types according
1952  * to the specifications made in the file.
1953  */
GetThisLevelsDroids(char * section_pointer)1954 void GetThisLevelsDroids(char *section_pointer)
1955 {
1956 	int our_level_number;
1957 	char *search_ptr;
1958 	char *lvl_end_location;
1959 	int random_droids;
1960 	int *allowed_type_list;
1961 	level *lvl;
1962 
1963 #define DROIDS_LEVEL_INDICATION_STRING "Level="
1964 #define DROIDS_LEVEL_END_INDICATION_STRING "** End of this levels Special Forces data **"
1965 
1966 	lvl_end_location = LocateStringInData(section_pointer, DROIDS_LEVEL_END_INDICATION_STRING);
1967 	lvl_end_location[0] = 0;
1968 
1969 	// Now we read in the level number for this level
1970 	ReadValueFromString(section_pointer, DROIDS_LEVEL_INDICATION_STRING, "%d", &our_level_number, lvl_end_location);
1971 
1972 	lvl = curShip.AllLevels[our_level_number];
1973 
1974 	// At this point, the List "allowed_type_list" has been filled with the NUMBERS of
1975 	// the allowed types.  The number of different allowed types found is also available.
1976 	// That means that now we can add the appropriate droid types into the list of existing
1977 	// droids in that mission.
1978 
1979 	random_droids = lvl->random_droids.nr;
1980 	allowed_type_list = lvl->random_droids.types;
1981 
1982 	while (random_droids--) {
1983 		// Create a new enemy, and initialize its 'identity' and 'global state'
1984 		// (the enemy will be fully initialized by respawn_level())
1985 		enemy *newen = enemy_new(allowed_type_list[MyRandom(lvl->random_droids.types_size - 1)]);
1986 		newen->pos.x = newen->pos.y = -1;
1987 		newen->pos.z = our_level_number;
1988 		newen->on_death_drop_item_code = -1;
1989 		newen->dialog_section_name = strdup("AfterTakeover");
1990 		newen->faction = FACTION_BOTS;
1991 
1992 		enemy_insert_into_lists(newen, TRUE);
1993 	}			// while (enemy-limit of this level not reached)
1994 
1995 	search_ptr = section_pointer;
1996 	GetThisLevelsSpecialForces(search_ptr, our_level_number, lvl_end_location);
1997 
1998 	// End bot's initialization, and put them onto a waypoint.
1999 	respawn_level(our_level_number);
2000 };
2001 
2002 /**
2003  * This function determines whether a given object on x/y is visible to
2004  * the 001 or not (due to some walls or something in between
2005  *
2006  * Return values are TRUE or FALSE accordingly
2007  *
2008  */
IsVisible(gps * objpos)2009 int IsVisible(gps *objpos)
2010 {
2011 
2012 	// For the purpose of visibility checking, we might as well exclude objects
2013 	// that are too far away to ever be visible and thereby save some checks of
2014 	// longer lines on the map, that wouldn't be necessary or helpful anyway.
2015 	//
2016 	if ((fabsf(Me.pos.x - objpos->x) > FLOOR_TILES_VISIBLE_AROUND_TUX) ||
2017 	    (fabsf(Me.pos.y - objpos->y) > FLOOR_TILES_VISIBLE_AROUND_TUX))
2018 		return (FALSE);
2019 
2020 	// So if the object in question is close enough to be visible, we'll do the
2021 	// actual check and see if the line of sight is free or blocked, a rather
2022 	// time-consuming and often re-iterated process.  (Maybe some do-it-every-
2023 	// -10th-frame-only code could be added here later... and in the meantime
2024 	// old values could be used from a stored flag?!
2025 	//
2026 	return (DirectLineColldet(objpos->x, objpos->y, Me.pos.x, Me.pos.y, objpos->z, &VisiblePassFilter));
2027 
2028 };				// int IsVisible( Point objpos )
2029 
2030 /**
2031  *
2032  *
2033  */
translate_pixel_to_map_location(float axis_x,float axis_y,int give_x)2034 inline float translate_pixel_to_map_location(float axis_x, float axis_y, int give_x)
2035 {
2036 
2037 	// NOTE:  This function does not expect absolute screen coordinates but rather coordinates relative
2038 	// to the center of the screen.
2039 	//
2040 	// That's also why it's 'axis' rather than 'pos' or 'point'.
2041 	//
2042 	// That is because mouse clicks can best be analyzed this way.
2043 	//
2044 
2045 	if (give_x) {
2046 		return (Me.pos.x + (axis_x / ((float)iso_floor_tile_width)) + (axis_y / ((float)iso_floor_tile_height)));
2047 	} else {
2048 		return (Me.pos.y - (axis_x / ((float)iso_floor_tile_width)) + (axis_y / ((float)iso_floor_tile_height)));
2049 	}
2050 
2051 };				// int translate_pixel_to_map_location ( int axis_x , int axis_y , int give_x )
2052 
2053 /**
2054  *
2055  *
2056  */
translate_pixel_to_zoomed_map_location(float axis_x,float axis_y,int give_x)2057 float translate_pixel_to_zoomed_map_location(float axis_x, float axis_y, int give_x)
2058 {
2059 	float zf = lvledit_zoomfact();
2060 	if (give_x) {
2061 		return (Me.pos.x + (zf * axis_x / ((float)iso_floor_tile_width)) + (zf * axis_y / ((float)iso_floor_tile_height)));
2062 		// return ( ( axis_x / ISO_WIDTH ) + ( axis_y / ISO_HEIGHT ) ) ;
2063 	} else {
2064 		return (Me.pos.y - (zf * axis_x / ((float)iso_floor_tile_width)) + (zf * axis_y / ((float)iso_floor_tile_height)));
2065 		// return ( - ( axis_x / ISO_WIDTH ) + ( axis_y / ISO_HEIGHT ) ) ;
2066 	}
2067 
2068 };				// int translate_pixel_to_zoomed_map_location ( int axis_x , int axis_y , int give_x )
2069 
2070 /**
2071  *
2072  *
2073  */
translate_point_to_map_location(float axis_x,float axis_y,int zoom_is_on)2074 moderately_finepoint translate_point_to_map_location(float axis_x, float axis_y, int zoom_is_on)
2075 {
2076 	moderately_finepoint position;
2077 	if (zoom_is_on) {
2078 		position.x = translate_pixel_to_zoomed_map_location(axis_x, axis_y, TRUE);
2079 		position.y = translate_pixel_to_zoomed_map_location(axis_x, axis_y, FALSE);
2080 	} else {
2081 		position.x = translate_pixel_to_map_location(axis_x, axis_y, TRUE);
2082 		position.y = translate_pixel_to_map_location(axis_x, axis_y, FALSE);
2083 	}
2084 	return position;
2085 }
2086 
2087 /**
2088  * This function translates a given map point to screen coordinates.
2089  *
2090  * @param x_map_pos X position on map
2091  * @param y_map_pos Y position on map
2092  * @param x_res	pointer to the int that will hold the x position on screen
2093  * @param y_res pointer to the y position on screen
2094  * @param zoom_factor zoom factor in use
2095  *
2096  */
translate_map_point_to_screen_pixel_func(float x_map_pos,float y_map_pos,int * x_res,int * y_res)2097 void translate_map_point_to_screen_pixel_func(float x_map_pos, float y_map_pos, int *x_res, int *y_res)
2098 {
2099 	float zoom_factor = 1.0;
2100 
2101 	/* XXX should not check for leveleditor here! */
2102 	if (game_status == INSIDE_LVLEDITOR && GameConfig.zoom_is_on) {
2103 		zoom_factor = lvledit_zoomfact_inv();
2104 	}
2105 #define R ceilf
2106 #define factX iso_floor_tile_width*0.5*zoom_factor
2107 #define factY iso_floor_tile_height*0.5*zoom_factor
2108 	if (x_res != NULL) {
2109 		//obstacles oscillent *x_res = UserCenter_x + R( (x_map_pos - Me.pos.x) * factX) + R((Me . pos . y - y_map_pos) * factX);
2110 		//murs tilent pas -- en fait si
2111 		*x_res = UserCenter_x + R(x_map_pos * factX) - R(y_map_pos * factX) + R(Me.pos.y * factX) - R(factX * Me.pos.x);
2112 		//murs tilent pas ET tux oscille *x_res = UserCenter_x + R( x_map_pos * factX) - R(y_map_pos * factX) + R((Me.pos.y - Me.pos.x) * factX);
2113 		//original "devtrack" - murs tilent pas *x_res = ( UserCenter_x + R ( ( x_map_pos - y_map_pos )  * factX  ) + R ( ( Me . pos . y - Me . pos . x ) * factX ) );
2114 
2115 	}
2116 	if (y_res != NULL) {
2117 		//*y_res = UserCenter_y + R( (x_map_pos - Me.pos.x)* factY ) + R((y_map_pos - Me . pos . y)* factY);
2118 		*y_res = UserCenter_y + R(x_map_pos * factY) + R(y_map_pos * factY) - R(Me.pos.x * factY) - R(factY * Me.pos.y);
2119 		//*y_res = UserCenter_y + R( x_map_pos * factY ) + R(y_map_pos * factY) - R((Me.pos.x + Me.pos.y) * factY);
2120 		//*y_res=( UserCenter_y + R ( ( x_map_pos + y_map_pos )  * factY ) - R( (  Me . pos . x + Me . pos . y ) * factY ));
2121 	}
2122 #undef R
2123 #undef factX
2124 #undef factY
2125 }
2126 
2127 #undef _map_c
2128