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