map.c 68.5 KB
Newer Older
1
/*
ahuillet's avatar
 
ahuillet committed
2
3
4
 *
 *   Copyright (c) 1994, 2002, 2003 Johannes Prix
 *   Copyright (c) 1994, 2002 Reinhard Prix
5
 *   Copyright (c) 2004-2010 Arthur Huillet
ahuillet's avatar
 
ahuillet committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *
 *
 *  This file is part of Freedroid
 *
 *  Freedroid is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Freedroid is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Freedroid; see the file COPYING. If not, write to the 
 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
 *  MA  02111-1307  USA
 *
 */

27
/**
ahuillet's avatar
 
ahuillet committed
28
29
30
31
 * This file contains (all?) map-related functions, which also includes 
 * loading of decks and whole ships, starting the lifts and consoles if 
 * close to the paradroid, refreshes as well as determining the map brick 
 * that contains specified coordinates are done in this file.
32
 */
ahuillet's avatar
 
ahuillet committed
33

34
#define _map_c 1
ahuillet's avatar
 
ahuillet committed
35
36
37
38
39
40
41
42

#include "system.h"

#include "defs.h"
#include "struct.h"
#include "proto.h"
#include "global.h"

43
#include "lvledit/lvledit_actions.h"
44
#include "lvledit/lvledit_display.h"
ahuillet's avatar
 
ahuillet committed
45
46
#include "map.h"

47
48
#define 	TELEPORT_PAIR_STRING	"teleport pair:"

49
void GetThisLevelsDroids(char *section_pointer);
ahuillet's avatar
 
ahuillet committed
50

51
52
53
54
55
56
57
58
59
60
/*
 * Initialize a map tile with default values.
 */
void init_map_tile(struct map_tile* tile)
{
	int i;
	tile->floor_values[0] = ISO_FLOOR_SAND;
	for (i = 1; i < MAX_FLOOR_LAYERS; i++)
		tile->floor_values[i] = ISO_FLOOR_EMPTY;
	dynarray_init(&tile->glued_obstacles, 0, sizeof(int));
61
62
	tile->volatile_obstacles = (list_head_t*)MyMalloc(sizeof(list_head_t));
	INIT_LIST_HEAD(tile->volatile_obstacles);
63
	tile->timestamp = 0;
64
65
}

66
/**
67
68
 * This function will make all blood obstacles vanish, all dead bots come
 * back to life, and get all bots return to a wandering state.
69
 */
70
void respawn_level(int level_num)
ahuillet's avatar
 
ahuillet committed
71
{
72
73
	enemy *erot, *nerot;

74
	int wp_num = curShip.AllLevels[level_num]->waypoints.size;
75
76
77
78
79
80
	// wp_num can be zero, so we can not use a static array
	char *wp_used = NULL; // is a waypoint already used ?
	if (wp_num > 0) {
		wp_used = (char *)MyMalloc(wp_num * sizeof(char));
		memset(wp_used, 0, wp_num * sizeof(char));
	}
81

82
	// First we remove all the volatile obstacles...
83
	//
84
	remove_volatile_obstacles(level_num);
85

86
	// Now we can give new life to dead bots...
87
	//
88
	BROWSE_DEAD_BOTS_SAFE(erot, nerot) {
89
		if (erot->pos.z != level_num || Droidmap[erot->type].is_human || !erot->will_respawn)
90
91
			continue;
		/* Move the bot to the alive list */
92
93
94
		list_move(&(erot->global_list), &alive_bots_head);
		/* Reinsert it into the current level list */
		list_add(&(erot->level_list), &level_bots_head[level_num]);
95
96
	}

97
98
99
100
	// Finally, we reset the runtime attributes of the bots, place them
	// on a waypoint, and ask them to start wandering...
	//
	BROWSE_LEVEL_BOTS(erot, level_num) {
101

102
103
		// Unconditional reset of the 'transient state' attributes
		enemy_reset(erot);
104

105
106
		// Conditional reset of some 'global state' attributes
		if (erot->has_been_taken_over) {
107
			erot->faction = FACTION_BOTS;
108
			erot->has_been_taken_over = FALSE;
109
110
			erot->CompletelyFixed = FALSE;
			erot->follow_tux = FALSE;
111
		}
112
113
		
		erot->has_greeted_influencer = FALSE;
114

115
		if (wp_num > 0) {
116
117
118
119
			// Re-place the bots onto the waypoint system
			if (!erot->SpecialForce) {
				// Standard bots are randomly placed on one waypoint
				int wp = teleport_to_random_waypoint(erot, curShip.AllLevels[level_num], wp_used);
120
121
122
123
124
				wp_used[wp] = 1;
				erot->homewaypoint = erot->lastwaypoint;
				erot->combat_state = SELECT_NEW_WAYPOINT;
				erot->state_timeout = 0.0;
			} else {
125
126
127
128
129
130
131
132
133
134
135
136
137
138
				if (erot->homewaypoint == -1) {
					// If a special force droid has not yet been integrated onto
					// the waypoint system, place it near its current position.
					int wp = teleport_to_closest_waypoint(erot);
					wp_used[wp] = 1;
					erot->homewaypoint = erot->lastwaypoint;
					erot->combat_state = SELECT_NEW_WAYPOINT;
					erot->state_timeout = 0.0;
				} else {
					// Consider that the nextwaypoint of a special force droid
					// is occupied, so that a standard bot will not be placed here
					if (erot->nextwaypoint != -1)
						wp_used[erot->nextwaypoint] = 1;
				}
139
			}
140
		} else {
fluzz's avatar
fluzz committed
141
142
			error_message(__FUNCTION__, "There is no waypoint on level %d - unable to place random bots.",
					PLEASE_INFORM, level_num);
143
144
		}
	}
145
146

	free(wp_used);
147
}
ahuillet's avatar
 
ahuillet committed
148

149
/**
150
151
152
153
 * \brief Get the center coordinates of a given map label.
 * In case of fail, a fatal error is thrown.
 * \param map_label The name of map label to resolve.
 * \return The gps center of the map label.
154
 */
155
gps get_map_label_center(const char *label)
ahuillet's avatar
 
ahuillet committed
156
{
157
158
	struct map_label *m;
	gps position = {0., 0., -1};
159
160
161
	int i;

	for (i = 0; i < curShip.num_levels; i++) {
162
		if (!level_exists(i))
163
164
			continue;

165
		m = get_map_label(curShip.AllLevels[i], label);
166
		if (m) {
167
168
169
170
			position.x = m->pos.x + 0.5;
			position.y = m->pos.y + 0.5;
			position.z = i;
			return position;
171
		}
172
173
	}

fluzz's avatar
fluzz committed
174
	error_message(__FUNCTION__, "\
175
Resolving map label   \"%s\"   failed on the entire world!\n\
176
This is a severe error in the game data of FreedroidRPG.", PLEASE_INFORM, label);
177

178
	return position;
179
};
ahuillet's avatar
 
ahuillet committed
180

181
/**
ahuillet's avatar
 
ahuillet committed
182
183
184
 * Next we extract the level interface data from the human-readable data 
 * into the level struct, but WITHOUT destroying or damaging the 
 * human-readable data in the process!
185
 */
186
static void decode_interfaces(level *loadlevel, char *DataPointer)
ahuillet's avatar
 
ahuillet committed
187
{
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
	char *TempSectionPointer;
	char PreservedLetter;

	// We look for the beginning and end of the map statement section
	TempSectionPointer = LocateStringInData(DataPointer, MAP_BEGIN_STRING);

	// We add a terminator at the end, but ONLY TEMPORARY.  The damage will be restored later!
	PreservedLetter = TempSectionPointer[0];
	TempSectionPointer[0] = 0;

	ReadValueFromString(DataPointer, "jump target north: ", "%d", &(loadlevel->jump_target_north), TempSectionPointer);
	ReadValueFromString(DataPointer, "jump target south: ", "%d", &(loadlevel->jump_target_south), TempSectionPointer);
	ReadValueFromString(DataPointer, "jump target east: ", "%d", &(loadlevel->jump_target_east), TempSectionPointer);
	ReadValueFromString(DataPointer, "jump target west: ", "%d", &(loadlevel->jump_target_west), TempSectionPointer);

	TempSectionPointer[0] = PreservedLetter;

205
}
ahuillet's avatar
 
ahuillet committed
206

207
static void decode_dimensions(level *loadlevel, char *DataPointer)
ahuillet's avatar
 
ahuillet committed
208
{
209
210
211
212

	int off = 0;

	/* Read levelnumber */
213
214
	char *fp = DataPointer;
	fp += strlen(LEVEL_HEADER_LEVELNUMBER);
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->levelnum = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

	/* Read xlen */
	fp += strlen("xlen of this level:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->xlen = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

	/* Read ylen */
	fp += strlen("ylen of this level:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->ylen = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

243
244
245
246
247
248
249
250
251
252
	/* Read floor_layers */
	fp += strlen("floor layers:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->floor_layers = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

253
254
255
256
257
	/* Read lrb */
	fp += strlen("light radius bonus of this level:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
258
	loadlevel->light_bonus = atoi(fp);
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

	fp += strlen("minimal light on this level:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->minimum_light_value = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

	fp += strlen("infinite_running_on_this_level:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->infinite_running_on_this_level = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

	fp += strlen("random dungeon:");
	while (*(fp + off) != '\n')
		off++;
	fp[off] = 0;
	loadlevel->random_dungeon = atoi(fp);
	fp[off] = '\n';
	fp += off + 1;
	off = 0;

290
291
292
293
294
295
296
297
298
299
300
301
302
	if (!strncmp(fp, TELEPORT_PAIR_STRING, strlen(TELEPORT_PAIR_STRING))) {
		fp += strlen(TELEPORT_PAIR_STRING);
		while (*(fp + off) != '\n')
			off++;
		fp[off] = 0;
		loadlevel->teleport_pair = atoi(fp);
		fp[off] = '\n';
		fp += off + 1;
		off = 0;
	} else {
		loadlevel->teleport_pair = 0;
	}

fluzz's avatar
fluzz committed
303
304
305
306
307
308
309
310
	if (!strncmp(fp, "dungeon generated:", 18)) {
		fp += strlen("dungeon generated:");
		while (*(fp + off) != '\n')
			off++;
		fp[off] = 0;
		loadlevel->dungeon_generated = atoi(fp);
		fp[off] = '\n';
		fp += off + 1;
311
		off = 0;
fluzz's avatar
fluzz committed
312
	} else {
313
		loadlevel->dungeon_generated = 0;
fluzz's avatar
fluzz committed
314
315
	}

316
317
318
319
320
321
322
323
	if (!strncmp(fp, "environmental flags:", 20)) {
		fp += strlen("environmental flags:");
		while (*(fp + off) != '\n')
			off++;
		fp[off] = 0;
		loadlevel->flags = atoi(fp);
		fp[off] = '\n';
		fp += off + 1;
324
		off = 0;
325
326
327
328
	} else {
		loadlevel->flags = 0;
	}

329
	if (!strncmp(fp, "item drop class:", 16)) {
330
		fp += strlen("item drop class:");
Xenux's avatar
Xenux committed
331
332
333
334
335
		while (*(fp + off) != '\n')
			off++;
		fp[off] = 0;
		loadlevel->drop_class = atoi(fp);
		fp[off] = '\n';
336
		// Note: to be commented out if another decoding part is added after this one
337
338
		// fp += off + 1;
		// off = 0;
Xenux's avatar
Xenux committed
339
	} else {
340
		loadlevel->drop_class = -1;
Xenux's avatar
Xenux committed
341
342
	}

343
	// Note: if another decoding part is added, do not forget to comment out the 2 lines
344
345
	// above...

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
	if (loadlevel->xlen < 10) {
		error_message(__FUNCTION__,
		              "A map/level line has not enough tiles (%d). The savegame is probably corrupted.\n"
		              "Sorry, but FreedroidRPG will refuse to load this map.",
		              PLEASE_INFORM | IS_FATAL, loadlevel->xlen);
	}

	if (loadlevel->xlen >= MAX_MAP_LINES) {
		error_message(__FUNCTION__,
		              "A map/level line has more tiles (%d) than allowed by the constant MAX_MAP_LINES in defs.h.\n"
		              "Sorry, but unless this constant is raised, FreedroidRPG will refuse to load this map.",
		              PLEASE_INFORM | IS_FATAL, loadlevel->xlen);
	}

	if (loadlevel->ylen < 10) {
		error_message(__FUNCTION__,
	                  "A map/level has not enough map lines (%d). The savegame is probably corrupted.\n"
	                  "Sorry, but FreedroidRPG will refuse to load this map.",
		              PLEASE_INFORM | IS_FATAL, loadlevel->ylen);
	}

367
	if (loadlevel->ylen >= MAX_MAP_LINES) {
368
369
370
371
		error_message(__FUNCTION__,
		              "A map/level has more map lines (%d) than allowed by the constant MAX_MAP_LINES in defs.h.\n"
		              "Sorry, but unless this constant is raised, FreedroidRPG will refuse to load this map.",
		              PLEASE_INFORM | IS_FATAL, loadlevel->ylen);
372
	}
373
374
}

375
376
377
static void decode_random_droids(level *loadlevel, char *data)
{
	char *search_ptr;
378
	char *end_ptr;
379
380
381
382
383
384
385
386

#define DROIDS_NUMBER_INDICATION_STRING "number of random droids: "
#define ALLOWED_TYPE_INDICATION_STRING "random droid types: "

	// Read the number of random droids for this level
	end_ptr = strstr(data, ALLOWED_TYPE_INDICATION_STRING);
	ReadValueFromString(data, DROIDS_NUMBER_INDICATION_STRING, "%d", &loadlevel->random_droids.nr, end_ptr);

387
388
389
	if (loadlevel->random_droids.nr <= 0)
		return;

Gregory's avatar
Gregory committed
390
	// Now we read in the type(s) of random droids for this level
391
392
393
	data = strstr(data, ALLOWED_TYPE_INDICATION_STRING);

	search_ptr = ReadAndMallocStringFromDataOptional(data, ALLOWED_TYPE_INDICATION_STRING, "\n");
394
	if (search_ptr) {
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
		char *droid_type_ptr = search_ptr;
		while (*droid_type_ptr) {
			while (*droid_type_ptr && isspace(*droid_type_ptr)) {
				droid_type_ptr++;
			}
			int droid_type_length = 0;
			char *ptr = droid_type_ptr;
			while (isalnum(*ptr)) {
				ptr++;
				droid_type_length++;
			}
			if (!droid_type_length)
				break;

			char type_indication_string[droid_type_length + 1];
			strncpy(type_indication_string, droid_type_ptr, droid_type_length);
			type_indication_string[droid_type_length] = 0;

			int droid_type = get_droid_type(type_indication_string);

			loadlevel->random_droids.types[loadlevel->random_droids.types_size++] = droid_type;

			droid_type_ptr += droid_type_length;
			if (*droid_type_ptr)
				droid_type_ptr++; //skip the comma
420
421
		}
		free(search_ptr);
422
423
424
	}
}

425
426
static int decode_header(level *loadlevel, char *data)
{
427
	data = strstr(data, LEVEL_HEADER_LEVELNUMBER);
428
429
430
431
432
	if (!data)
		return 1;

	decode_interfaces(loadlevel, data);
	decode_dimensions(loadlevel, data);
433
434
	decode_random_droids(loadlevel, data);

435
	// Read the levelname.
fluzz's avatar
fluzz committed
436
	loadlevel->Levelname = ReadAndMallocStringFromData(data, LEVEL_NAME_STRING, "\"");
437
438

	loadlevel->Background_Song_Name = ReadAndMallocStringFromData(data, BACKGROUND_SONG_NAME_STRING, "\n");
ahuillet's avatar
 
ahuillet committed
439

440
441
	return 0;
}
ahuillet's avatar
 
ahuillet committed
442

443
/**
ahuillet's avatar
 
ahuillet committed
444
445
446
 * Next we extract the human readable obstacle data into the level struct
 * WITHOUT destroying or damaging the human-readable data in the process!
 * This is an improved parser that is not quite readable but very performant.
447
 */
448
static char *decode_obstacles(level *load_level, char *data_pointer)
ahuillet's avatar
 
ahuillet committed
449
{
450
	// First we initialize the obstacles with 'empty' information
451
452

	int i;
453
	for (i = 0; i < MAX_OBSTACLES_ON_MAP; i++) {
454
455
456
457
458
459
		load_level->obstacle_list[i].type = -1;
		load_level->obstacle_list[i].pos.x = -1;
		load_level->obstacle_list[i].pos.y = -1;
		load_level->obstacle_list[i].pos.z = load_level->levelnum;
		load_level->obstacle_list[i].timestamp = 0;
		load_level->obstacle_list[i].frame_index = 0;
460
461
	}

462
463
	if (load_level->random_dungeon && !load_level->dungeon_generated)
		return data_pointer;
464

465
	// Now we look for the beginning and end of the obstacle section
466
467

	char *obstacle_SectionBegin = LocateStringInData(data_pointer, OBSTACLE_DATA_BEGIN_STRING) + strlen(OBSTACLE_DATA_BEGIN_STRING) + 1;
468
469

	// Now we decode all the obstacle information
470
471

	char *curfield = obstacle_SectionBegin;
472
473
474
475
	while (*curfield != '/') {
		//structure of obstacle entry is :      // t59 x2.50 y63.50 l-1 d-1 
		//we read the type
		curfield++;
476
		char *curfieldend = curfield;
477
478
479
		while ((*curfieldend) != ' ')
			curfieldend++;
		(*curfieldend) = 0;
480
		int type = atoi(curfield);
481
		(*curfieldend) = ' ';
482
483
484
485
486
487
488

		//we read the X position
		curfield = curfieldend + 2;
		curfieldend += 2;
		while ((*curfieldend) != ' ')
			curfieldend++;
		(*curfieldend) = 0;
489
		float x = atof(curfield);
490
		(*curfieldend) = ' ';
491
492
493
494
495
496
497

		//Y position
		curfield = curfieldend + 2;
		curfieldend += 2;
		while ((*curfieldend) != ' ')
			curfieldend++;
		(*curfieldend) = 0;
498
		float y = atof(curfield);
499
500
501
502
503
		(*curfieldend) = ' ';

		while ((*curfield) != '\n')
			curfield++;
		curfield++;
504

505
506
507
		// Even invalid obstacles are loaded. They can not be removed at this
		// point, or else obstacle extensions will point to the wrong obstacles.
		// decode_level(), our callee, will take care of them.
508
		add_obstacle_nocheck(load_level, x, y, type);
509
510
	}

511
	return curfield;
512
}
ahuillet's avatar
 
ahuillet committed
513

514
/**
ahuillet's avatar
 
ahuillet committed
515
516
 * Next we extract the map labels of this level WITHOUT destroying
 * or damaging the data in the process!
517
 */
518
static char *decode_map_labels(level *load_level, char *data)
ahuillet's avatar
 
ahuillet committed
519
{
520
	// Initialize map labels
521
	dynarray_init(&load_level->map_labels, 10, sizeof(struct map_label));
522

523
	if (load_level->random_dungeon && !load_level->dungeon_generated)
524
		return data;
525

526
	// Now we look for the beginning and end of the map labels section
527
528
529
	char *map_label_begin = LocateStringInData(data, MAP_LABEL_BEGIN_STRING) + strlen(MAP_LABEL_BEGIN_STRING) + 1;
	char *map_label_end = LocateStringInData(map_label_begin, MAP_LABEL_END_STRING);
	*map_label_end = '\0';
530

531
532
533
	// Get the number of map labels in this level
	int nb_map_labels_in_level = CountStringOccurences(map_label_begin, LABEL_ITSELF_ANNOUNCE_STRING);
	DebugPrintf(1, "\nNumber of map labels found in this level : %d.", nb_map_labels_in_level);
534
535

	// Now we decode all the map label information
536
	int i;
537
	for (i = 0; i < nb_map_labels_in_level ; i++) {
538
		if (i)
539
			map_label_begin = strstr(map_label_begin + 1, X_POSITION_OF_LABEL_STRING);
540

541
		// Get the position of the map label
542
		int x, y;
543
544
545
546
		ReadValueFromString(map_label_begin, X_POSITION_OF_LABEL_STRING, "%d", &x, map_label_end);
		ReadValueFromString(map_label_begin, Y_POSITION_OF_LABEL_STRING, "%d", &y, map_label_end);

		// Get the name of the map label
547
		char *label_name = ReadAndMallocStringFromData(map_label_begin, LABEL_ITSELF_ANNOUNCE_STRING, "\"");
548
549

		// Add the map label on the level
550
		add_map_label(load_level, x, y, label_name);
551
552

		DebugPrintf(1, "\npos.x=%d pos.y=%d label_name=\"%s\"", x, y, label_name);
553
554
	}

555
556
	*map_label_end = MAP_LABEL_END_STRING[0];
	return map_label_end;
557
}
ahuillet's avatar
 
ahuillet committed
558

559
static void ReadInOneItem(char *ItemPointer, char *ItemsSectionEnd, item *TargetItem)
ahuillet's avatar
 
ahuillet committed
560
{
561
	init_item(TargetItem);
562

563
564
565
	char *item_id = ReadAndMallocStringFromData(ItemPointer, ITEM_ID_STRING, "\"");
	TargetItem->type = get_item_type_by_id(item_id);
	free(item_id);
566
567
568

	ReadValueFromString(ItemPointer, ITEM_POS_X_STRING, "%f", &(TargetItem->pos.x), ItemsSectionEnd);
	ReadValueFromString(ItemPointer, ITEM_POS_Y_STRING, "%f", &(TargetItem->pos.y), ItemsSectionEnd);
569
	ReadValueFromStringWithDefault(ItemPointer, ITEM_ARMOR_CLASS_BASE_STRING, "%d", "0", &(TargetItem->armor_class), ItemsSectionEnd);
570
571
	ReadValueFromString(ItemPointer, ITEM_MAX_DURABILITY_STRING, "%d", &(TargetItem->max_durability), ItemsSectionEnd);
	ReadValueFromString(ItemPointer, ITEM_CUR_DURABILITY_STRING, "%f", &(TargetItem->current_durability), ItemsSectionEnd);
572
573
	ReadValueFromString(ItemPointer, ITEM_AMMO_CLIP_STRING, "%d", &(TargetItem->ammo_clip), ItemsSectionEnd);
	ReadValueFromString(ItemPointer, ITEM_MULTIPLICITY_STRING, "%d", &(TargetItem->multiplicity), ItemsSectionEnd);
574
	ReadValueFromStringWithDefault(ItemPointer, ITEM_QUALITY_STRING, "%d", "0", &(TargetItem->quality), ItemsSectionEnd);
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591

	// Read the socket data of the item and calculate bonuses using it.
	int i;
	int socket_count;
	ReadValueFromStringWithDefault(ItemPointer, ITEM_SOCKETS_SIZE_STRING, "%d", "0", &socket_count, ItemsSectionEnd);
	for (i = 0; i < socket_count; i++) {
		char type_string[32];
		char addon_string[32];
		struct upgrade_socket socket;
		sprintf(type_string, "%s%d=", ITEM_SOCKET_TYPE_STRING, i);
		sprintf(addon_string, "%s%d=", ITEM_SOCKET_ADDON_STRING, i);
		ReadValueFromString(ItemPointer, type_string, "%d", &socket.type, ItemsSectionEnd);
		socket.addon = ReadAndMallocStringFromDataOptional(ItemPointer, addon_string, "\"");
		create_upgrade_socket(TargetItem, socket.type, socket.addon);
		free(socket.addon);
	}
	calculate_item_bonuses(TargetItem);
592
593
594
595
596

	DebugPrintf(1, "\nPosX=%f PosY=%f Item=%d", TargetItem->pos.x, TargetItem->pos.y, TargetItem->type);

}

597
static char *decode_extension_chest(char *ext, void **data)
598
{
599
	struct dynarray *chest = dynarray_alloc(1, sizeof(item));
600
601
602
	char *item_str, *item_end;
	
	item_str = ext;
603

604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
	while (*item_str != '}') {
		// Find end of this item (beginning of next item)
		item_end = item_str;
		while (*item_end != '\n')
			item_end++;
		while (isspace(*item_end))
			item_end++;

		// Read the item on this line
		item new_item;
		ReadInOneItem(item_str, item_end, &new_item);

		// Add the item to the dynarray
		dynarray_add(chest, &new_item, sizeof(item));

		// Move to the next item definition
		item_str = item_end;
621
	}
622

623
624
625
626
627
628
629

	*data = chest;
	return item_str;
}

static char *decode_extension_label(char *ext, void **data)
{
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
	char *begin = NULL;
	char *end = NULL;

	if (*ext != '\"') {
		// For compatibility with old levels.dat and savegames, parse an
		// unquoted string.
		// TODO: to be removed in the future, when unquoted strings are no
		// more used by anybody...
		begin = ext;
		end = begin;
		while (*end != '\n')
			end++;
	} else {
		begin = ext + 1;
		end = begin;
		while (*end != '\"' && *end != '\n')
			end++;
	}
648
649

	*end = '\0';
650
	*data = strdup(begin);
651
652
653
654
655
656
657
658
659
660
	*end = '\n';

	while (*end != '}')
		end++;

	return end;
}

static char *decode_extension_dialog(char *ext, void **data)
{
661
	// Same format than label extension.
662
663
664
	return decode_extension_label(ext, data);
}

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
static char *decode_extension_signmessage(char *ext, void **data)
{
	char *begin = NULL;
	char *end = NULL;

	if (*ext != '_' && *ext != '\"') {
		// For compatibility with old levels.dat and savegames, parse an
		// unquoted string.
		// TODO: to be removed in the future, when unquoted strings are no
		// more used by anybody...
		begin = ext;
		end = begin;
		while (*end != '\n')
			end++;
	} else {
		if (*ext == '\"')
			begin = ext + 1;
		else
			begin = ext + 2;
		end = begin;
		while (*end != '\"' && *end != '\n')
			end++;
	}

	*end = '\0';
	*data = strdup(begin);
	*end = '\n';

	while (*end != '}')
		end++;

	return end;
}

699
700
701
702
static char *decode_obstacle_extensions(level *loadlevel, char *data)
{
	dynarray_init(&loadlevel->obstacle_extensions, 10, sizeof(struct obstacle_extension));

703
	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
704
		return data;
705

706
707
708
	char *ext_begin = LocateStringInData(data, OBSTACLE_EXTENSIONS_BEGIN_STRING);
	char *ext_end = LocateStringInData(ext_begin, OBSTACLE_EXTENSIONS_END_STRING);
	*ext_end = '\0';
709

710
711
712
713
714
	while (1) {
		// Look for the next extension
		ext_begin = strstr(ext_begin, "idx=");
		if (!ext_begin)
			break;
715

716
717
718
		// Read extension information
		int index;
		int type;
719
		void *ext_data = NULL;
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
		sscanf(ext_begin, "idx=%d type=%d", &index, &type);

		// Move to the extension data definition
		ext_begin = strstr(ext_begin, "data={\n");
		while (*ext_begin != '\n')
			ext_begin++;
		while (isspace(*ext_begin))
			ext_begin++;

		// Read the extension data
		switch (type) {
			case OBSTACLE_EXTENSION_CHEST_ITEMS:
				ext_begin = decode_extension_chest(ext_begin, &ext_data);
				break;
			case OBSTACLE_EXTENSION_LABEL:
				ext_begin = decode_extension_label(ext_begin, &ext_data);
				break;
			case OBSTACLE_EXTENSION_DIALOGFILE:
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
			{
				// Old levels.dat and savegames use this type for
				// dialog extensions as well as for sign messages extensions.
				// New 'format' uses two different values.
				// For compatibility, at least during some time, we accept
				// to read sign message extension here, but we convert it to
				// its actual type.
				// The actual use of an obstacle extension, and thus its actual type,
				// is defined by the 'action' set to the obstacle (see action.c).
				// TODO: To be removed in the future
				obstacle *obs = &(loadlevel->obstacle_list[index]);
				obstacle_spec *spec = get_obstacle_spec(obs->type);

				if (spec->action && strcmp(spec->action, "sign")) {
					// This is really a dialog extension
					ext_begin = decode_extension_dialog(ext_begin, &ext_data);
					break;
				}
				// This is a sign message extension
				// We change the extension's type, and continue within the next
				// switch case
				type = OBSTACLE_EXTENSION_SIGNMESSAGE;
			}
			/* no break */
			case OBSTACLE_EXTENSION_SIGNMESSAGE:
				ext_begin = decode_extension_signmessage(ext_begin, &ext_data);
764
				break;
765
		}
766
767

		// Add the obstacle extension on the level
768
		add_obstacle_extension(loadlevel, &(loadlevel->obstacle_list[index]), type, ext_data);
769
770
	}

771
772
	*ext_end = OBSTACLE_EXTENSIONS_END_STRING[0];
	return ext_end;
773
}
ahuillet's avatar
 
ahuillet committed
774

775
static char *decode_item_section(level *loadlevel, char *data)
ahuillet's avatar
 
ahuillet committed
776
{
777
	int i;
778
779
780
781
782
	char Preserved_Letter;
	int NumberOfItemsInThisLevel;
	char *ItemPointer;
	char *ItemsSectionBegin;
	char *ItemsSectionEnd;
783

784
	// First we initialize the items arrays with 'empty' information
785
	dynarray_init(&loadlevel->item_list, 10, sizeof(struct item));
786

787
	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
788
		return data;
789

790
	// We look for the beginning and end of the items section
791
792
	ItemsSectionBegin = LocateStringInData(data, ITEMS_SECTION_BEGIN_STRING);
	ItemsSectionEnd = LocateStringInData(ItemsSectionBegin, ITEMS_SECTION_END_STRING);
793

794
795
796
797
	// We add a terminator at the end of the items section, but ONLY TEMPORARY.  
	// The damage will be restored later!
	Preserved_Letter = ItemsSectionEnd[0];
	ItemsSectionEnd[0] = 0;
798
	NumberOfItemsInThisLevel = CountStringOccurences(ItemsSectionBegin, ITEM_ID_STRING);
799
	DebugPrintf(1, "\nNumber of items found in this level : %d.", NumberOfItemsInThisLevel);
800

801
	// Now we decode all the item information
802
	ItemPointer = ItemsSectionBegin;
803
	char *NextItemPointer;
804
	for (i = 0; i < NumberOfItemsInThisLevel; i++) {
805
806
		if ((ItemPointer = strstr(ItemPointer + 1, ITEM_ID_STRING))) {
			NextItemPointer = strstr(ItemPointer + 1, ITEM_ID_STRING);
807
808
			if (NextItemPointer)
				NextItemPointer[0] = 0;
809
810
811
812
			struct item new_item;
			ReadInOneItem(ItemPointer, ItemsSectionEnd, &new_item);
			new_item.pos.z = loadlevel->levelnum;
			dynarray_add(&loadlevel->item_list, &new_item, sizeof(struct item));
813
			if (NextItemPointer)
814
				NextItemPointer[0] = ITEM_ID_STRING[0];
815
		}
816
	}
817
818
819

	// Now we repair the damage done to the loaded level data
	ItemsSectionEnd[0] = Preserved_Letter;
820
	return ItemsSectionEnd;
821
822
}

823
static char *decode_map(level *loadlevel, char *data)
824
825
826
827
828
{
	char *map_begin, *map_end;
	int i;

	if ((map_begin = strstr(data, MAP_BEGIN_STRING)) == NULL)
829
		return NULL;
830
831
832
	map_begin += strlen(MAP_BEGIN_STRING) + 1;

	if ((map_end = strstr(data, MAP_END_STRING)) == NULL)
833
		return NULL;
834

835
836
837
838
839
840
841
	if (loadlevel->floor_layers > MAX_FLOOR_LAYERS) {
		error_message(__FUNCTION__,
		              "Too much layers on level %d: %d layers found for a max of %d.\n"
		              "We ignore the extra layers.",
				      PLEASE_INFORM, loadlevel->levelnum, loadlevel->floor_layers, MAX_FLOOR_LAYERS);
	}

842
	/* now scan the map */
843
	unsigned int curlinepos = 0;
844
845
846
847

	/* read MapData */
	for (i = 0; i < loadlevel->ylen; i++) {
		int col;
848
		int layer;
849
850
851
		map_tile *Buffer;
		int tmp;

852
		/* Get the next line */
853
		unsigned int nlpos = 0;
854
		while (map_begin[curlinepos + nlpos] != '\n' && (map_begin + curlinepos + nlpos < map_end))
855
			nlpos++;
856

857
		if (nlpos > MAX_MAP_LINE_LENGTH) { // Enough room for a width of 200 tiles on 10 layers
858
859
			error_message(__FUNCTION__,
			              "A very long line has been detected in a map data of the savegame.\n"
860
			              "Line length: %d chars - Max length: %d chars\n"
861
			              "That savegame is probably corrupted, we do not want to load it.",
862
						  PLEASE_INFORM | IS_FATAL, nlpos, MAX_MAP_LINE_LENGTH);
863
864
865
866
		}
		if (nlpos != (loadlevel->xlen * loadlevel->floor_layers * 4)) {
			error_message(__FUNCTION__,
			              "A line with a wrong length has been detected in a map data of the savegame.\n"
867
			              "Expected length: %d (due to xlen: %d and #layers: %d) - Actual length: %d chars.\n"
868
			              "That savegame is probably corrupted, we do not want to load it.",
869
						  PLEASE_INFORM | IS_FATAL, loadlevel->xlen * loadlevel->floor_layers * 4, loadlevel->xlen, loadlevel->floor_layers, nlpos);
870
871
872
		}

		char *this_line = (char *)MyMalloc(nlpos+1);
873
874
875
876
877
878
		memcpy(this_line, map_begin + curlinepos, nlpos);
		this_line[nlpos] = '\0';

		/* Decode it */
		Buffer = MyMalloc((loadlevel->xlen + 10) * sizeof(map_tile));
		for (col = 0; col < loadlevel->xlen; col++) {
879
880
881
			// Make sure that all floor layers are always initialized properly.
			init_map_tile(&Buffer[col]);

882
			for (layer = 0; (layer < loadlevel->floor_layers) && (layer < MAX_FLOOR_LAYERS); layer++) {
883
				tmp = strtol(this_line + 4 * (loadlevel->floor_layers * col + layer), NULL, 10);
884
885
886
887
888
889
890
891
				if (tmp >= underlay_floor_tiles.size) {
					if (tmp < MAX_UNDERLAY_FLOOR_TILES || (tmp - MAX_UNDERLAY_FLOOR_TILES) >= overlay_floor_tiles.size) {
						error_message(__FUNCTION__, "Level %d at (%d, %d) in layer #%d uses an unknown floor tile: %d.", PLEASE_INFORM,
								loadlevel->levelnum, col, i, layer, tmp);
						tmp = ISO_FLOOR_EMPTY;
					}
				}

892
893
				Buffer[col].floor_values[layer] = (Uint16) tmp;
			}
894
895
896
897
		}

		// Now the old text pointer can be replaced with a pointer to the
		// correctly assembled struct...
898

899
900
		loadlevel->map[i] = Buffer;

901
902
903
904
905
906
907
908
909
		free(this_line);
		curlinepos += nlpos+1;
		if ((map_begin + curlinepos >= map_end) && ((i+1) != loadlevel->ylen)) {
			error_message(__FUNCTION__,
			              "A map data of the savegame has not the right number of lines.\n"
			              "Expected ylen: %d lines - Actual: %d lines\n"
			              "That savegame is probably corrupted, we do not want to load it.",
						  PLEASE_INFORM | IS_FATAL, loadlevel->ylen, i+1);
		}
910
911
	}

912
	return map_end;
913
914
}

915
static char *decode_waypoints(level *loadlevel, char *data)
916
917
918
{
	char *wp_begin, *wp_end;

919
	// Initialize waypoints
920
	dynarray_init(&loadlevel->waypoints, 2, sizeof(struct waypoint));
921

922
923
924
	if (loadlevel->random_dungeon && !loadlevel->dungeon_generated)
		return data;

925
926
	// Find the beginning and end of the waypoint list
	if ((wp_begin = strstr(data, WP_BEGIN_STRING)) == NULL)
927
		return NULL;
928
929
930
	wp_begin += strlen(WP_BEGIN_STRING) + 1;

	if ((wp_end = strstr(data, WP_END_STRING)) == NULL)
931
		return NULL;
932

933
	int curlinepos = 0;
934
	
935
	while (1) {
936
937
		// Get the next line

938
		short int nlpos = 0;
939
		while (wp_begin[curlinepos + nlpos] != '\n' && (wp_begin + curlinepos + nlpos < wp_end))
940
			nlpos++;
941

942
		if (nlpos > MAX_WP_LINE_LENGTH) { // Enough room for a waypoint with 100 connections
943
944
			error_message(__FUNCTION__,
			              "A very long line has been detected in a waypoint config of the savegame.\n"
945
			              "Line length: %d chars - Max length: %d\n"
946
			              "That savegame is probably corrupted, we do not want to load it.",
947
			              IS_FATAL, nlpos, MAX_WP_LINE_LENGTH);
948
949
950
		}

		char *this_line = (char *)MyMalloc(nlpos+1);
951
952
953
		memcpy(this_line, wp_begin + curlinepos, nlpos);
		this_line[nlpos] = '\0';

954
955
956
		// Create a new waypoint

		int nr, x, y, wp_rnd;
957
958
		sscanf(this_line, "Nr.=%d \t x=%d \t y=%d   rnd=%d", &nr, &x, &y, &wp_rnd);

959
960
		waypoint new_wp = { .x = x, .y = y, .suppress_random_spawn = wp_rnd };
		dynarray_init(&new_wp.connections, 2, sizeof(int));
961

Rafael Fontenelle's avatar
Rafael Fontenelle committed
962
		// Initialize the connections of the new waypoint
963

964
		char *pos = strstr(this_line, CONNECTION_STRING);
965
		if (pos == NULL) {
966
967
968
969
970
971
972
973
			error_message(__FUNCTION__,
			              "Unable to find connection string, on line %s, level %i.\n"
			              "The data file seems to be corrupted.\n"
			              "We continue to load it, but you may encounter some errors while playing.",
						  PLEASE_INFORM, this_line, loadlevel->levelnum);
		} else {
			pos += strlen(CONNECTION_STRING);	// skip connection-string
			pos += strspn(pos, WHITE_SPACE);	// skip initial whitespace
974

975
976
977
978
979
			while (1) {
				if (*pos == '\0')
					break;
				int connection;
				int res = sscanf(pos, "%d", &connection);
980
				if ((connection == -1) || (res == '\0') || (res == EOF))
981
					break;
982

983
984
				// Add the connection on this waypoint
				dynarray_add(&new_wp.connections, &connection, sizeof(int));
985

986
987
988
				pos += strcspn(pos, WHITE_SPACE);	// skip last token
				pos += strspn(pos, WHITE_SPACE);	// skip initial whitespace for next one
			}
989
		}
990
991

		// Add the waypoint on the level
992

993
		dynarray_add(&loadlevel->waypoints, &new_wp, sizeof(struct waypoint));
994
995
996
997
998
999
1000

		// Prepare for next loop, unless we reached the end of the buffer

		free(this_line);
		curlinepos += nlpos + 1;
		if (wp_begin + curlinepos >= wp_end) {
			break;