1 #include "system.h"
2 
3 #include "defs.h"
4 #include "struct.h"
5 #include "../src/global.h"
6 #include "proto.h"
7 
8 #include "mapgen/mapgen.h"
9 #include "mapgen/themes.h"
10 
11 #include "lvledit/lvledit.h"
12 
13 #include "lvledit/lvledit_actions.h"
14 
15 #define		SET_PILLAR_PROB		70
16 
17 static struct mapgen_gamelevel map;
18 static level *target_level;
19 
20 struct roominfo *rooms;
21 int total_rooms = 0;
22 int max_rooms = 0;
23 
24 const struct { int enter, exit; } teleport_pairs[] = {
25 	{ ISO_TELEPORTER_1, ISO_TELEPORTER_1},	// enter: cloud, exit: cloud
26 	{ ISO_TELEPORTER_1, ISO_EXIT_5 },		// enter: cloud, exit: ladder to upstairs
27 	{ ISO_EXIT_3, ISO_TELEPORTER_1 },		// enter: ladder to downstairs, exit: cloud
28 	{ ISO_EXIT_3, ISO_EXIT_5}				// enter: ladder to downstairs, exit: ladder to upstairs
29 };
30 
new_level(int w,int h)31 static void new_level(int w, int h)
32 {
33 	int x, y;
34 	unsigned char *map_p;
35 
36 	map.w = w;
37 	map.h = h;
38 
39 	map.m = malloc(map.w * map.h * sizeof(unsigned char));
40 	map.r = malloc(map.w * map.h * sizeof(int));
41 	map_p = map.m;
42 
43 	for (y = 0; y < map.h; y++) {
44 		for (x = 0; x < map.w; x++) {
45 			*(map_p++) = TILE_EMPTY;
46 			map.r[y * map.w + x] = -1;
47 		}
48 	}
49 
50 	rooms = MyMalloc(100 * sizeof(struct roominfo));
51 	max_rooms = 100;
52 	total_rooms = 0;
53 }
54 
free_level()55 static void free_level()
56 {
57 	int i;
58 
59 	free(map.m);
60 	free(map.r);
61 
62 	for (i = 0; i < total_rooms; i++) {
63 		free(rooms[i].neighbors);
64 	}
65 
66 	free(rooms);
67 	total_rooms = 0;
68 	max_rooms = 0;
69 	rooms = NULL;
70 }
71 
set_dungeon_output(level * output)72 void set_dungeon_output(level * output)
73 {
74 	target_level = output;
75 }
76 
mapgen_add_obstacle(double x,double y,int type)77 void mapgen_add_obstacle(double x, double y, int type)
78 {
79 	add_obstacle(target_level, x, y, type);
80 }
81 
mapgen_set_floor(int x,int y,int type)82 void mapgen_set_floor(int x, int y, int type)
83 {
84 	target_level->map[y][x].floor_values[0] = type;
85 }
86 
split_wall(int w,int h,unsigned char * tiles)87 static void split_wall(int w, int h, unsigned char *tiles)
88 {
89 	int y, x;
90 	int room;
91 #define SET(X,Y,TILE) mapgen_put_tile(X, Y, TILE, room)
92 
93 	// Reduce the size of the rooms, not lying on the map's boundary.
94 	for (x = 0; x < total_rooms; x++) {
95 		if (rooms[x].x + rooms[x].w < w - 1 )
96 			rooms[x].w--;
97 		if (rooms[x].y + rooms[x].h < h - 1)
98 			rooms[x].h--;
99 	}
100 
101 	for (y = 1; y < h - 1; y++)
102 		for (x = 1; x < w - 1; x++) {
103 			room = mapgen_get_room(x, y);
104 			if (tiles[y * w + x] == TILE_WALL) {
105 				SET(x - 1, y    , TILE_WALL);
106 				SET(x    , y - 1, TILE_WALL);
107 				SET(x - 1, y - 1, TILE_WALL);
108 			}
109 		}
110 }
111 
112 // Randomly reduce size of rooms. We go alongside the
113 // room border and check whether we can shift the current wall
114 // tile closer to the center of rooms. Therefore the room size
115 // becomes smaller and free space between rooms increases.
reduce_room_space()116 static void reduce_room_space() {
117 	int x, y;
118 	int i, j;
119 	int point_x[4];
120 	int point_y[4];
121 	int count[4];
122 	const int point_dx[4] = { 1,  0, -1,  0};
123 	const int point_dy[4] = { 0,  1,  0, -1};
124 	const int check_dir[4] = { 3, 1, 2, 0 };
125 	for (i = 0; i < total_rooms; i++) {
126 		if (rooms[i].w < 4 || rooms[i].h < 4)
127 			continue;
128 
129 		// 4 start points, one per room corner
130 		point_x[0] = rooms[i].x;
131 		point_y[0] = rooms[i].y;
132 		count[0] = rooms[i].w;
133 
134 		point_x[1] = rooms[i].x + rooms[i].w - 1;
135 		point_y[1] = rooms[i].y;
136 		count[1] = rooms[i].h;
137 
138 		point_x[2] = rooms[i].x + rooms[i].w - 1;
139 		point_y[2] = rooms[i].y + rooms[i].h - 1;
140 		count[2] = rooms[i].w;
141 
142 		point_x[3] = rooms[i].x;
143 		point_y[3] = rooms[i].y + rooms[i].h - 1;
144 		count[3] = rooms[i].h;
145 
146 		for (j = 0; j < 4; j++) {
147 			if (MyRandom(100) > 40)
148 				continue;
149 
150 			x = point_x[j];
151 			y = point_y[j];
152 			if (mapgen_get_tile(x + point_dx[check_dir[j]], y + point_dy[check_dir[j]]) != TILE_WALL)
153 				continue;
154 
155 			switch(j) {
156 				case 0:
157 					rooms[i].y++;
158 					rooms[i].h--;
159 					break;
160 				case 1:
161 					rooms[i].w--;
162 					break;
163 				case 2:
164 					rooms[i].h--;
165 					break;
166 				case 3:
167 					rooms[i].x++;
168 					rooms[i].w--;
169 					break;
170 			}
171 			while (count[j]--) {
172 				mapgen_put_tile(x, y, TILE_WALL, -1);
173 				x +=  point_dx[j];
174 				y +=  point_dy[j];
175 			}
176 			// If the given room took part in fusion there may be additional tile ahead
177 			// that should be removed
178 			if (mapgen_get_tile(x, y) == TILE_PARTITION)
179 				mapgen_put_tile(x, y, TILE_WALL, -1);
180 		}
181 	}
182 }
183 
place_internal_door(int x,int y,enum connection_type dir)184 static void place_internal_door(int x, int y, enum connection_type dir)
185 {
186 	const int check_horz[] = { 0, -1 };
187 	const int check_vert[] = { -1, 0 };
188 	const float dx_horz[] = { 0.5, 0.5 };
189 	const float dy_horz[] = {   0,   1 };
190 	const float dx_vert[] = {   0,   1 };
191 	const float dy_vert[] = { 0.5, 0.5 };
192 	const int *check;
193 	const float *dx, *dy;
194 	int room, door;
195 
196 	if (dir == UP || dir == DOWN) {
197 		check = check_horz;
198 		dx = dx_horz;
199 		dy = dy_horz;
200 		door = ISO_H_DOOR_000_OPEN;
201 	} else {
202 		check = check_vert;
203 		dx = dx_vert;
204 		dy = dy_vert;
205 		door = ISO_V_DOOR_000_OPEN;
206 	}
207 	room = mapgen_get_room(x, y);
208 	mapgen_put_tile(x, y, TILE_FLOOR, room);
209 	if (mapgen_get_room(x + check[0], y + check[1]) != room)
210 		mapgen_add_obstacle(x + dx[0], y + dy[0], door);
211 	else
212 		mapgen_add_obstacle(x + dx[1], y + dy[1], door);
213 }
214 
place_doors()215 static void place_doors()
216 {
217 	int i, j, room;
218 	int x, y;
219 	int w;
220 	int x1, x2, y1, y2;
221 	enum connection_type dir;
222 	int id, id1, id2;
223 	int place_door;
224 
225 	for (i = 0; i < total_rooms; i++) {
226 		for (j = 0; j < rooms[i].num_doors; j++) {
227 			x = rooms[i].doors[j].x;
228 			y = rooms[i].doors[j].y;
229 			room = rooms[i].doors[j].room;
230 			w = 2;
231 			id1 = -1;
232 			id2 = -1;
233 			place_door = 1;
234 			// Place door only if both rooms have its side length greater then 2
235 			// If one of the rooms is a corridor then whole doorway should belong to it
236 			if (rooms[i].w < 3 || rooms[i].h < 3) {
237 				id1 = i;
238 				id2 = i;
239 				place_door = 0;
240 			}
241 			if (rooms[room].w < 3 || rooms[room].h < 3) {
242 				id1 = room;
243 				id2 = room;
244 				place_door = 0;
245 			}
246 
247 			if (rooms[i].x < x - 1 && x - 1 <= (rooms[i].x + rooms[i].w - 1)) {
248 				// Place horizontal wall
249 
250 				// Set single horizontal door if the wall is internal
251 				if (mapgen_get_tile(x, y) == TILE_PARTITION) {
252 					place_internal_door(x, y, UP);
253 					continue;
254 				}
255 
256 				// Shift to left by 1 due to split done
257 				x1 = x - w;
258 				x2 = x;
259 				if (y < rooms[i].y) {
260 					y1 = rooms[room].y + rooms[room].h;
261 					y2 = rooms[i].y;
262 					dir = UP;
263 					if (id1 == -1) {
264 						id1 = room;
265 						id2 = i;
266 					}
267 				} else {
268 					y1 = rooms[i].y + rooms[i].h;
269 					y2 = rooms[room].y;
270 					dir = DOWN;
271 					if (id1 == -1) {
272 						id1 = i;
273 						id2 = room;
274 					}
275 				}
276 				if (place_door)
277 					mapgen_add_obstacle(x1 + 0.5, (y1 + y2) / 2.0, ISO_DH_DOOR_000_OPEN);
278 
279 				if (MyRandom(100) < SET_PILLAR_PROB &&  y2 - y1 > 2) {
280 					if (rooms[i].x <= x - w - 1 && rooms[i].x + rooms[i].w > x &&
281 						rooms[room].x <= x - w - 1 && rooms[room].x + rooms[room].w > x) {
282 						mapgen_put_tile(x1-1, y1, TILE_FLOOR, id1);
283 						mapgen_add_obstacle(x1 - 0.5, y1 + 0.5, ISO_PILLAR_SHORT);
284 
285 						mapgen_put_tile(x1 + w, y1, TILE_FLOOR, id1);
286 						mapgen_add_obstacle(x1 + w + 0.5, y1 + 0.5, ISO_PILLAR_SHORT);
287 
288 						mapgen_put_tile(x1-1, y2 - 1, TILE_FLOOR, id2);
289 						mapgen_add_obstacle(x1 - 0.5, y2 - 0.5, ISO_PILLAR_SHORT);
290 
291 						mapgen_put_tile(x1 + w, y2 - 1, TILE_FLOOR, id2);
292 						mapgen_add_obstacle(x1 + w + 0.5, y2 - 0.5, ISO_PILLAR_SHORT);
293 					}
294 				}
295 			} else {
296 				// Place vertical wall
297 
298 				// Set signle vertical door if the wall is internal
299 				if (mapgen_get_tile(x, y) == TILE_PARTITION) {
300 					place_internal_door(x, y, LEFT);
301 					continue;
302 				}
303 				// Shift to top by 1 due to split done
304 				y1 = y - w;
305 				y2 = y;
306 				if (x < rooms[i].x) {
307 					x1 = rooms[room].x + rooms[room].w;
308 					x2 = rooms[i].x;
309 					dir = LEFT;
310 					if (id1 == -1) {
311 						id1 = room;
312 						id2 = i;
313 					}
314 				} else {
315 					x1 = rooms[i].x + rooms[i].w;
316 					x2 = rooms[room].x;
317 					dir = RIGHT;
318 					if (id1 == -1) {
319 						id1 = i;
320 						id2 = room;
321 					}
322 				}
323 				if (place_door)
324 					mapgen_add_obstacle((x1 + x2) / 2.0, y1 + 0.5, ISO_DV_DOOR_000_OPEN);
325 
326 				if (MyRandom(100) < SET_PILLAR_PROB &&  x2 - x1 > 2) {
327 					if (rooms[i].y <= y - w - 1 && rooms[i].y + rooms[i].h > y &&
328 						rooms[room].y <= y - w - 1 && rooms[room].y + rooms[room].h > y) {
329 						mapgen_put_tile(x1, y1 - 1, TILE_FLOOR, id1);
330 						mapgen_add_obstacle(x1 + 0.5, y1 - 0.5, ISO_PILLAR_SHORT);
331 
332 						mapgen_put_tile(x1, y1 + w, TILE_FLOOR, id2);
333 						mapgen_add_obstacle(x1 + 0.5, y1 + w + 0.5, ISO_PILLAR_SHORT);
334 
335 						mapgen_put_tile(x2 - 1, y1 - 1, TILE_FLOOR, id1);
336 						mapgen_add_obstacle(x2 - 0.5, y1 - 0.5, ISO_PILLAR_SHORT);
337 
338 						mapgen_put_tile(x2 - 1, y1 + w, TILE_FLOOR, id2);
339 						mapgen_add_obstacle(x2 - 0.5, y1 + w + 0.5, ISO_PILLAR_SHORT);
340 					}
341 				}
342 			}
343 
344 			for (y = y1; y < y2; y++) {
345 				for (x = x1; x < x2; x++) {
346 					// New tiles are divided between rooms equally
347 					if (dir == LEFT || dir == RIGHT)
348 						id = x < (x1 + x2) / 2 ? id1 : id2;
349 					else
350 						id = y < (y1 + y2) / 2 ? id1 : id2;
351 					mapgen_put_tile(x, y, TILE_FLOOR, id);
352 				}
353 			}
354 		}
355 	}
356 }
357 
358 // Turn the given room into corridor and return whether
359 // the transformation was successful
make_corridor(int room)360 static int make_corridor(int room)
361 {
362 #define	MAX_DOORS_IN_CORRIDOR	3
363 	struct doorinfo doors[3];
364 	int num_doors = 0;
365 	int i, j;
366 	int x1 = rooms[room].x;
367 	int y1 = rooms[room].y;
368 	int x2 = x1 + rooms[room].w - 1;
369 	int y2 = y1 + rooms[room].h - 1;
370 	int xmin = x2 + 1;
371 	int ymin = y2 + 1;
372 	int xmax = x1;
373 	int ymax = y1;
374 
375 	if (rooms[room].num_doors > MAX_DOORS_IN_CORRIDOR)
376 		return 0;
377 
378 
379 	for (i = 0; i < rooms[room].num_doors; i++) {
380 		// We don't connect internal door to the corridor so exit
381 		if (rooms[room].doors[i].internal)
382 			return 0;
383 		doors[num_doors++] = rooms[room].doors[i];
384 	}
385 	// Find the doors of other rooms leading to the given room
386 	for (i = 0; i < total_rooms; i++) {
387 		for (j = 0; j < rooms[i].num_doors; j++) {
388 			if (rooms[i].doors[j].room == room) {
389 				if (rooms[i].doors[j].internal)
390 					return 0;
391 				if (num_doors == MAX_DOORS_IN_CORRIDOR)
392 					return 0;
393 				doors[num_doors++] = rooms[i].doors[j];
394 			}
395 		}
396 	}
397 
398 	if (num_doors == 1)
399 		return 0;
400 
401 	// Calculate new room width and height
402 	for (i = 0; i < num_doors; i++) {
403 		if (x1 <= doors[i].x - 1 && doors[i].x - 1 < x2) {
404 			xmin = min(doors[i].x, xmin);
405 			xmax = max(doors[i].x + 2, xmax);
406 		} else {
407 			ymin = min(doors[i].y, ymin);
408 			ymax = max(doors[i].y + 2, ymax);
409 		}
410 	}
411 	// If there is not enoguh information to calculate room dimensions
412 	// we will suggest its position in the center
413 	if (xmin > xmax) {
414 		xmin = (x1 + x2) / 2 - 1;
415 		xmax = xmin + 1;
416 	}
417 	if (ymin > ymax) {
418 		ymin = (y1 + y2) / 2 - 1;
419 		ymax = ymin + 1;
420 	}
421 	// Remove old tiles and redraw room
422 	rooms[room].x = xmin - 2;
423 	rooms[room].y = ymin - 2;
424 	rooms[room].w = max(xmax - xmin, 2);
425 	rooms[room].h = max(ymax - ymin, 2);
426 	for (i = y1; i <= y2; i++) {
427 		for (j = x1; j <= x2; j++)
428 			mapgen_put_tile(j, i, TILE_WALL, -1);
429 	}
430 	mapgen_draw_room(room);
431 
432 	return 1;
433 }
434 
cmp_room_surface(const void * room1,const void * room2)435 static int cmp_room_surface(const void *room1, const void *room2)
436 {
437 	int r1 = *(int *)room1;
438 	int r2 = *(int *)room2;
439 
440 	int s1 = rooms[r1].w * rooms[r1].h;
441 	int s2 = rooms[r2].w * rooms[r2].h;
442 
443 	if (s1 == s2) {
444 		return 0;
445 	} else if (s1 < s2) {
446 		return 1;
447 	} else
448 		return -1;
449 }
450 
451 // Convert tile matrix to a set of obstacles and decorate rooms according to their themes,
452 // using mid_room as the center of dungeon
mapgen_convert(struct dungeon_info * di,int w,int h,unsigned char * tiles)453 void mapgen_convert(struct dungeon_info *di, int w, int h, unsigned char *tiles)
454 {
455 	int i;
456 	int idx[di->num_rooms];
457 	int tries = 30;
458 	int n = 7;
459 
460 	reduce_room_space();
461 	split_wall(w, h, tiles);
462 
463 	// Sort rooms by their surface
464 	for (i = 0; i < di->num_rooms; i++)
465 		idx[i] = i;
466 	qsort(idx, di->num_rooms, sizeof(int), cmp_room_surface);
467 
468 	i = 0;
469 	while(tries && n && (i < di->num_rooms)) {
470 		if (idx[i] != di->enter && idx[i] != di->exit) {
471 			if (make_corridor(idx[i]))
472 				n--;
473 			else
474 				tries--;
475 		}
476 		i++;
477 	}
478 
479 	place_doors();
480 
481 	qsort(idx, di->num_rooms, sizeof(int), cmp_room_surface);
482 
483 	mapgen_place_obstacles(di, w, h, tiles, idx);
484 }
485 
add_teleport(int telnum,int x,int y,int tpair)486 static void add_teleport(int telnum, int x, int y, int tpair)
487 {
488 	const int helpers[2][4] = {
489 		{ ISO_DROID_NEST_GREEN, ISO_DROID_NEST_GREEN, ISO_DROID_NEST_GREEN, ISO_DROID_NEST_GREEN },
490 		{ ISO_ENHANCER_RU, ISO_ENHANCER_LU, ISO_ENHANCER_RD, ISO_ENHANCER_LD }
491 	};
492 
493 	char *warp, *fromwarp;
494 	char tmp[500];
495 	int obs_type, helper;
496 
497 	sprintf(tmp, "%dtoX%d", target_level->levelnum, telnum);
498 	warp = strdup(tmp);
499 
500 	sprintf(tmp, "%dfromX%d", target_level->levelnum, telnum);
501 	fromwarp = strdup(tmp);
502 
503 	add_map_label(target_level, x, y, warp);
504 	add_map_label(target_level, x + 1, y, fromwarp);
505 
506 	// A label placed at (x, y) is actually positioned at (x+0.5, y+0.5).
507 	// That 0.5 translation is added to the other obstacles around the labels
508 	// to have a coherent position.
509 
510 	obs_type = telnum ? teleport_pairs[tpair].exit : teleport_pairs[tpair].enter;
511 	mapgen_add_obstacle(x + 0.5, y + 0.5, obs_type);
512 
513 	// Decorate room with teleport if the obstacle is the cloud
514 	if (obs_type == ISO_TELEPORTER_1) {
515 		helper = MyRandom(1);
516 		mapgen_add_obstacle(x + 1 + 0.5, y - 1 + 0.5, helpers[helper][0]);
517 		mapgen_add_obstacle(x - 1 + 0.5, y - 1 + 0.5, helpers[helper][1]);
518 		mapgen_add_obstacle(x + 1 + 0.5, y + 1 + 0.5, helpers[helper][2]);
519 		mapgen_add_obstacle(x - 1 + 0.5, y + 1 + 0.5, helpers[helper][3]);
520 	}
521 }
522 
mapgen_entry_at(struct roominfo * r,int tpair)523 void mapgen_entry_at(struct roominfo *r, int tpair)
524 {
525 	add_teleport(0, r->x + r->w / 2, r->y + r->h / 2, tpair);
526 }
527 
mapgen_exit_at(struct roominfo * r,int tpair)528 void mapgen_exit_at(struct roominfo *r, int tpair)
529 {
530 	add_teleport(1, r->x + r->w / 2, r->y + r->h / 2, tpair);
531 }
532 
mapgen_gift(struct roominfo * r)533 void mapgen_gift(struct roominfo *r)
534 {
535 	const int gifts[] = {
536 		ISO_E_CHEST2_CLOSED,
537 		ISO_W_CHEST2_CLOSED,
538 		ISO_S_CHEST2_CLOSED,
539 		ISO_N_CHEST2_CLOSED
540 	};
541 	const int dx[] = { -2, 1, 0, 0 };
542 	const int dy[] = { 0, 0, -2, 1 };
543 
544 	int pos = MyRandom(3);
545 
546 	struct {
547 		float x;
548 		float y;
549 	} positions[4] = {
550 		{
551 		r->x, r->y + r->h / 2}, {
552 		r->x + r->w - 1, r->y + r->h / 2}, {
553 		r->x + r->w / 2, r->y}, {
554 		r->x + r->w / 2, r->y + r->h - 1}
555 	};
556 
557 	if (mapgen_get_tile(positions[pos].x + dx[pos], positions[pos].y + dy[pos]) == TILE_WALL) {
558 		obstacle_spec *spec = get_obstacle_spec(gifts[pos]);
559 		positions[pos].x += spec->right_border;
560 		positions[pos].y += spec->lower_border;
561 		mapgen_add_obstacle(positions[pos].x, positions[pos].y, gifts[pos]);
562 	}
563 }
564 
mapgen_add_room(int x,int y,int w,int h)565 int mapgen_add_room(int x, int y, int w, int h)
566 {
567 	int newid = total_rooms;
568 
569 	if (total_rooms == max_rooms) {
570 		// Add 10 more slots
571 		max_rooms += 10;
572 		struct roominfo *buffer = realloc(rooms, max_rooms * sizeof(struct roominfo));
573 		if (!buffer) {
574 			error_message(__FUNCTION__, "Not enough memory to reallocate the 'rooms' datastruct (requested size: " SIZE_T_F ").", IS_FATAL, max_rooms * sizeof(struct roominfo));
575 			return -1;
576 		}
577 		rooms = buffer;
578 	}
579 
580 	total_rooms++;
581 
582 	// don't forget to reserve space for bounding walls
583 	rooms[newid].x = x;
584 	rooms[newid].y = y;
585 	rooms[newid].w = w;
586 	rooms[newid].h = h;
587 	rooms[newid].num_neighbors = 0;
588 	rooms[newid].max_neighbors = 8;
589 	rooms[newid].neighbors = MyMalloc(rooms[newid].max_neighbors * sizeof(int));
590 	rooms[newid].num_doors = 0;
591 
592 	return newid;
593 }
594 
mapgen_put_tile(int x,int y,unsigned char tile,int room)595 void mapgen_put_tile(int x, int y, unsigned char tile, int room)
596 {
597 	map.m[map.w * y + x] = tile;
598 	map.r[map.w * y + x] = room;
599 }
600 
mapgen_get_tile(int x,int y)601 unsigned char mapgen_get_tile(int x, int y)
602 {
603 	if (x < 0)
604 		return TILE_EMPTY;
605 	if (y < 0)
606 		return TILE_EMPTY;
607 	if (x >= map.w)
608 		return TILE_EMPTY;
609 	if (y >= map.h)
610 		return TILE_EMPTY;
611 
612 	return map.m[map.w * y + x];
613 }
614 
mapgen_get_room(int x,int y)615 int mapgen_get_room(int x, int y)
616 {
617 	if (x < 0)
618 		return -1;
619 	if (y < 0)
620 		return -1;
621 	if (x >= map.w)
622 		return -1;
623 	if (y >= map.h)
624 		return -1;
625 
626 	return map.r[map.w * y + x];
627 }
628 
mapgen_draw_room(int room_id)629 void mapgen_draw_room(int room_id)
630 {
631 	int place_x = rooms[room_id].x - 1;
632   	int	place_y = rooms[room_id].y - 1;
633 	int room_w = rooms[room_id].w + 1;
634 	int room_h = rooms[room_id].h + 1;
635 	int x, y, i;
636 
637 	// Corners
638 	mapgen_put_tile(place_x, place_y, TILE_WALL, -1);
639 	mapgen_put_tile(place_x + room_w, place_y, TILE_WALL, -1);
640 	mapgen_put_tile(place_x, place_y + room_h, TILE_WALL, -1);
641 	mapgen_put_tile(place_x + room_w, place_y + room_h, TILE_WALL, -1);
642 
643 	// Walls
644 	for (i = 1; i < room_w; i++) {
645 		mapgen_put_tile(place_x + i, place_y + room_h, TILE_WALL, -1);
646 		mapgen_put_tile(place_x + i, place_y, TILE_WALL, -1);
647 	}
648 	for (i = 1; i < room_h; i++) {
649 		mapgen_put_tile(place_x + room_w, place_y + i, TILE_WALL, -1);
650 		mapgen_put_tile(place_x, place_y + i, TILE_WALL, -1);
651 	}
652 
653 	// Floor
654 	for (y = 1; y < room_h; y++)
655 		for (x = 1; x < room_w; x++)
656 			mapgen_put_tile(place_x + x, place_y + y, TILE_FLOOR, room_id);
657 }
658 
659 // Check if the given cell is suitable for connections. Condition of success is that
660 // the current cell as well as 'offset' adjacent cells are free.
SuitableConnection(int x,int y,enum connection_type t,int offset)661 static int SuitableConnection(int x, int y, enum connection_type t, int offset)
662 {
663 	int i;
664 	const int dx[] = { -1, 1,  0, 0};
665 	const int dy[] = {  0, 0, -1, 1};
666 	if (mapgen_get_room(x, y) == -1)
667 		return 0;
668 	for (i = -offset; i <= offset; i++) {
669 		if (mapgen_get_tile(x + i * dx[t], y + i * dy[t]) != TILE_FLOOR)
670 			return 0;
671 	}
672 	return 1;
673 }
674 
675 /** Find the possible connections at each square on the border of the
676   given room.
677   Fill out the struct cplist_t array and return the number of possible
678   connections.
679   */
find_connection_points(int room_id,struct cplist_t cplist[100],int offset)680 int find_connection_points(int room_id, struct cplist_t cplist[100], int offset)
681 {
682 	// Find connection points
683 	int connect_points = 0;
684 	int i;
685 
686 	struct roominfo *r = &rooms[room_id];
687 
688 	for (i = offset; i < r->w - offset; i++) {
689 		if (SuitableConnection(r->x + i, r->y - 2, UP, offset)) {
690 			cplist[connect_points].x = r->x + i;
691 			cplist[connect_points].y = r->y - 1;
692 			cplist[connect_points].r = mapgen_get_room(r->x + i, r->y - 2);
693 			cplist[connect_points].t = UP;
694 			connect_points++;
695 		}
696 
697 		if (SuitableConnection(r->x + i, r->y + r->h + 1, DOWN, offset)) {
698 			cplist[connect_points].x = r->x + i;
699 			cplist[connect_points].y = r->y + r->h;
700 			cplist[connect_points].r = mapgen_get_room(r->x + i, r->y + r->h + 1);
701 			cplist[connect_points].t = DOWN;
702 			connect_points++;
703 		}
704 	}
705 	for (i = offset; i < r->h - offset; i++) {
706 		if (SuitableConnection(r->x - 2, r->y + i, LEFT, offset)) {
707 			cplist[connect_points].x = r->x - 1;
708 			cplist[connect_points].y = r->y + i;
709 			cplist[connect_points].r = mapgen_get_room(r->x - 2, r->y + i);
710 			cplist[connect_points].t = LEFT;
711 			connect_points++;
712 		}
713 
714 		if (SuitableConnection(r->x + r->w + 1, r->y + i, RIGHT, offset)) {
715 			cplist[connect_points].x = r->x + r->w;
716 			cplist[connect_points].y = r->y + i;
717 			cplist[connect_points].r = mapgen_get_room(r->x + r->w + 1, r->y + i);
718 			cplist[connect_points].t = RIGHT;
719 			connect_points++;
720 		}
721 	}
722 
723 	return connect_points;
724 }
725 
recursive_browse(int at,int parent,unsigned char * seen)726 static void recursive_browse(int at, int parent, unsigned char *seen)
727 {
728 	int i;
729 
730 	// If the room has already been seen, return immediately
731 	if (seen[at])
732 		return;
733 
734 	seen[at] = 1;
735 
736 	for (i = 0; i < rooms[at].num_neighbors; i++) {
737 		// Don't recurse into our parent
738 		if (rooms[at].neighbors[i] == parent)
739 			continue;
740 
741 		recursive_browse(rooms[at].neighbors[i], at, seen);
742 	}
743 }
744 
mapgen_is_connected(unsigned char * seen)745 int mapgen_is_connected(unsigned char *seen)
746 {
747 	int i;
748 	memset(seen, 0, total_rooms);
749 
750 	recursive_browse(0, 0, seen);
751 
752 	for (i = 0; i < total_rooms; i++) {
753 		if (seen[i] == 0) {
754 			return 0;
755 		}
756 	}
757 
758 	return 1;
759 }
760 
mapgen_are_connected(int room1,int room2)761 int mapgen_are_connected(int room1, int room2)
762 {
763 	int i;
764 	struct roominfo *r1 = &rooms[room1];
765 
766 	for (i = 0; i < r1->num_neighbors; i++) {
767 		if (r1->neighbors[i] == room2)
768 			return 1;
769 	}
770 
771 	return 0;
772 }
773 
mapgen_add_door(int x,int y,int from,int to)774 void mapgen_add_door(int x, int y, int from, int to)
775 {
776 	int num = rooms[from].num_doors;
777 	if (num == MAX_DOORS) {
778 			error_message(__FUNCTION__, "Maximal number of doors for a room exceeded", PLEASE_INFORM | IS_FATAL);
779 			return;
780 	}
781 	rooms[from].doors[num].x = x;
782 	rooms[from].doors[num].y = y;
783 	rooms[from].doors[num].room = to;
784 	rooms[from].doors[num].internal = 0;
785 	rooms[from].num_doors++;
786 }
787 
add_neighbor(struct roominfo * r,int neigh)788 static void add_neighbor(struct roominfo *r, int neigh)
789 {
790 	int newid = r->num_neighbors;
791 
792 	if (r->num_neighbors >= r->max_neighbors) {
793 		r->max_neighbors *= 2;
794 		r->neighbors = realloc(r->neighbors, r->max_neighbors * sizeof(int));
795 	}
796 
797 	r->num_neighbors++;
798 
799 	r->neighbors[newid] = neigh;
800 }
801 
MakeConnect(int x,int y,enum connection_type type)802 void MakeConnect(int x, int y, enum connection_type type)
803 {
804 	const int shift = 2;
805 	int wp_x, wp_y, wp_nx, wp_ny;
806 	int room_1, room_2;
807 
808 	wp_x = wp_nx = x;
809 	wp_y = wp_ny = y;
810 
811 	switch (type) {
812 		case UP:
813 			wp_ny = y - 1;
814 			wp_y = y + 1;
815 			break;
816 		case DOWN:
817 			wp_ny = y + 1;
818 			wp_y = y - 1;
819 			break;
820 		case LEFT:
821 			wp_nx = x - 1;
822 			wp_x = x + 1;
823 			break;
824 		case RIGHT:
825 			wp_nx = x + 1;
826 			wp_x = x - 1;
827 			break;
828 		default:
829 			error_message(__FUNCTION__, "Unknown connection type %d", PLEASE_INFORM | IS_FATAL, type);
830 			break;
831 
832 	}
833 
834 	room_1 = mapgen_get_room(wp_nx, wp_ny);
835 	room_2 = mapgen_get_room(wp_x, wp_y);
836 	mapgen_add_door(x, y, room_2, room_1);
837 	add_neighbor(&rooms[room_1], room_2);
838 	add_neighbor(&rooms[room_2], room_1);
839 
840 	// Make waypoint correction according to pending
841 	if (type == UP || type == DOWN) {
842 		wp_x -= shift;
843 		wp_nx -= shift;
844 	} else if (type == LEFT || type == RIGHT) {
845 		wp_y -= shift;
846 		wp_ny -= shift;
847 	}
848 	int wp1 = add_waypoint(target_level, wp_x, wp_y, 0);
849 	int wp2 = add_waypoint(target_level, wp_nx, wp_ny, 0);
850 
851 	action_toggle_waypoint_connection(target_level, wp1, wp2, 0, 0);
852 	action_toggle_waypoint_connection(target_level, wp2, wp1, 0, 0);
853 }
854 
find_waypoints(int x1,int y1,int x2,int y2,int * wps,int max)855 static int find_waypoints(int x1, int y1, int x2, int y2, int *wps, int max)
856 {
857 	waypoint *wpts = target_level->waypoints.arr;
858 	int total_wps = 0;
859 	int i;
860 
861 	for (i = 0; i < target_level->waypoints.size; i++) {
862 		if (wpts[i].x >= x1 && wpts[i].x < x2 && wpts[i].y >= y1 && wpts[i].y < y2) {
863 			wps[total_wps] = i;
864 			total_wps++;
865 
866 			if (total_wps == max)
867 				break;
868 		}
869 	}
870 
871 	return total_wps;
872 }
873 
connect_waypoints()874 static void connect_waypoints()
875 {
876 	int rn;
877 
878 	for (rn = 0; rn < total_rooms; rn++) {
879 		int wps[25];
880 		int max_wps = find_waypoints(rooms[rn].x - 1, rooms[rn].y - 1, rooms[rn].x + rooms[rn].w + 1, rooms[rn].y + rooms[rn].h + 1, wps, 25);
881 		int nbconn = max_wps;
882 
883 		if (max_wps == 1 || max_wps == 0)
884 			continue;
885 
886 		while (nbconn--) {
887 			int wp1 = nbconn;
888 			int wp2 = rand() % max_wps;
889 
890 			while (wp2 == wp1)
891 				wp2 = rand() % max_wps;
892 
893 			if (wp1 != wp2) {
894 				action_toggle_waypoint_connection(target_level, wps[wp1], wps[wp2], 0, 0);
895 				action_toggle_waypoint_connection(target_level, wps[wp2], wps[wp1], 0, 0);
896 			}
897 		}
898 	}
899 }
900 
place_waypoints()901 static void place_waypoints()
902 {
903 	int rn;
904 	int nb;
905 
906 	for (rn = 0; rn < total_rooms; rn++) {
907 		int func = sqrt(rooms[rn].w * rooms[rn].h);
908 
909 		nb = -1 + func / 3;
910 
911 		int retries = 15;
912 
913 		while ((nb--) > 0) {
914 			int newx = rooms[rn].x;
915 			int newy = rooms[rn].y;
916 			newx += MyRandom(rooms[rn].w - 1);
917 			newy += MyRandom(rooms[rn].h - 1);
918 
919 			colldet_filter my_filter = { WalkablePassFilterCallback, NULL, 0.8, NULL };
920 			if (!SinglePointColldet(newx + 0.5, newy + 0.5, target_level->levelnum, &my_filter)) {
921 				// If the randomly chosen position is not passable, retry... a certain number of times before giving up.
922 				if (retries-- > 0) {
923 					nb++;
924 				}
925 				continue;
926 			}
927 
928 			add_waypoint(target_level, newx, newy, 0);
929 		}
930 	}
931 }
932 
933 // The function computes eccentricity for each room and picks the one
934 // with the minimal eccentricity as the center. Also it fills 'distance'
935 // array with the distances from the 'entrance' in terms of rooms.
get_middle_room(int entrance,int * distance)936 static int get_middle_room(int entrance, int *distance)
937 {
938 	int i, j, k;
939 	int m;
940 	int dist[total_rooms][total_rooms];
941 	int eccentricity[total_rooms];
942 
943 	// Initialize distance between pairs of rooms with fake infinity
944 	for (i = 0; i < total_rooms; i++) {
945 		for (j = 0; j < total_rooms; j++)
946 			dist[i][j] = 99999;
947 		// Distance from a room to itself is 0
948 		dist[i][i] = 0;
949 		eccentricity[i] = 0;
950 	}
951 	for (i = 0; i < total_rooms; i++) {
952 		// Distance from a room to its neighbors is 1
953 		for (j = 0; j < rooms[i].num_neighbors; j++)
954 			dist[i][rooms[i].neighbors[j]] = 1;
955 	}
956 	// Calculate distance for each pair of rooms
957 	for (k = 0; k < total_rooms; k++) {
958 		for (i = 0; i < total_rooms; i++) {
959 			for (j = 0; j < total_rooms; j++)
960 				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
961 		}
962 	}
963 	// Eccentricity of a room is a maximum of the shortest distances
964 	// to all other rooms.
965 	for (i = 0; i < total_rooms; i++) {
966 		for (j = 0; j < total_rooms; j++)
967 			eccentricity[i] = max(eccentricity[i], dist[i][j]);
968 	}
969 	// Thus the central room is one with the minimal
970 	// eccentricity
971 	m = 0;
972 	for (i = 0; i < total_rooms; i++) {
973 		if (eccentricity[i] < eccentricity[m])
974 			m = i;
975 	}
976 	// Fill array of distances for a room with the entrance
977 	for (i = 0; i < total_rooms; i++)
978 		distance[i] = dist[entrance][i];
979 
980 	return m;
981 }
982 
generate_dungeon(int w,int h,int nbconnec,int tpair)983 int generate_dungeon(int w, int h, int nbconnec, int tpair)
984 {
985 	int i, j;
986 	int max, max_idx = 0;
987 	struct dungeon_info di;
988 
989 	new_level(w, h);
990 
991 	generate_dungeon_gram(w, h);
992 
993 	// Select entrance at random.
994 	int dist[total_rooms];
995 	int vis[total_rooms];
996 	int entrance = rand() % total_rooms;
997 	int mid_room = get_middle_room(entrance, dist);
998 
999 	mapgen_entry_at(&rooms[entrance], tpair);
1000 
1001 	memset(vis, 0, sizeof(int) * total_rooms);
1002 	// Choose N farthest rooms and place exits there
1003 	for (i = 0; i < nbconnec - 1; i++) {
1004 		max = dist[0];
1005 		max_idx = 0;
1006 		for (j = 1; j < total_rooms; j++) {
1007 			if (dist[j] > max && !vis[j]) {
1008 				max = dist[j];
1009 				max_idx = j;
1010 			}
1011 		}
1012 		mapgen_exit_at(&rooms[max_idx], tpair);
1013 		vis[max_idx] = 1;
1014 	}
1015 
1016 	di.enter = entrance;
1017 	di.exit = max_idx;
1018 	di.middle_room = mid_room;
1019 	di.num_rooms = total_rooms;
1020 	di.distance = dist;
1021 	mapgen_convert(&di, w, h, map.m);
1022 
1023 	// Place random waypoints
1024 	place_waypoints();
1025 
1026 	// Connect waypoints
1027 	connect_waypoints();
1028 
1029 	free_level();
1030 	return 0;
1031 }
1032 
mapgen_teleport_pair_str(int idx)1033 const char * mapgen_teleport_pair_str(int idx)
1034 {
1035 	const char *teleport_pair_str[] = {
1036 		"In - cloud; Out - cloud",
1037 		"In - cloud; Out - ladder up",
1038 		"In - ladder down; Out - cloud",
1039 		"In - ladder down; Out - ladder up"
1040 	};
1041 
1042 	return teleport_pair_str[idx];
1043 }
1044 
mapgen_cycle_teleport_pair(unsigned int value)1045 unsigned int mapgen_cycle_teleport_pair(unsigned int value)
1046 {
1047 	unsigned int sz = sizeof(teleport_pairs) / sizeof(teleport_pairs[0]);
1048 
1049 	value++;
1050 	if (value >= sz)
1051 		value = 0;
1052 
1053 	return value;
1054 }
1055