NetHack->aNetHack
[aNetHack.git] / src / mkmap.c
blobdf2e37122a1a876b2a2f1db6280438655bd5110d
1 /* NetHack 3.6 mkmap.c $NHDT-Date: 1432512767 2015/05/25 00:12:47 $ $NHDT-Branch: master $:$NHDT-Revision: 1.16 $ */
2 /* Copyright (c) J. C. Collet, M. Stephenson and D. Cohrs, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "sp_lev.h"
8 #define HEIGHT (ROWNO - 1)
9 #define WIDTH (COLNO - 2)
11 STATIC_DCL void FDECL(init_map, (SCHAR_P));
12 STATIC_DCL void FDECL(init_fill, (SCHAR_P, SCHAR_P));
13 STATIC_DCL schar FDECL(get_map, (int, int, SCHAR_P));
14 STATIC_DCL void FDECL(pass_one, (SCHAR_P, SCHAR_P));
15 STATIC_DCL void FDECL(pass_two, (SCHAR_P, SCHAR_P));
16 STATIC_DCL void FDECL(pass_three, (SCHAR_P, SCHAR_P));
17 STATIC_DCL void NDECL(wallify_map);
18 STATIC_DCL void FDECL(join_map, (SCHAR_P, SCHAR_P));
19 STATIC_DCL void FDECL(finish_map,
20 (SCHAR_P, SCHAR_P, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P));
21 STATIC_DCL void FDECL(remove_room, (unsigned));
22 void FDECL(mkmap, (lev_init *));
24 static char *new_locations;
25 int min_rx, max_rx, min_ry, max_ry; /* rectangle bounds for regions */
26 static int n_loc_filled;
28 STATIC_OVL void
29 init_map(bg_typ)
30 schar bg_typ;
32 register int i, j;
34 for (i = 1; i < COLNO; i++)
35 for (j = 0; j < ROWNO; j++)
36 levl[i][j].typ = bg_typ;
39 STATIC_OVL void
40 init_fill(bg_typ, fg_typ)
41 schar bg_typ, fg_typ;
43 register int i, j;
44 long limit, count;
46 limit = (WIDTH * HEIGHT * 2) / 5;
47 count = 0;
48 while (count < limit) {
49 i = rn1(WIDTH - 1, 2);
50 j = rnd(HEIGHT - 1);
51 if (levl[i][j].typ == bg_typ) {
52 levl[i][j].typ = fg_typ;
53 count++;
58 STATIC_OVL schar
59 get_map(col, row, bg_typ)
60 int col, row;
61 schar bg_typ;
63 if (col <= 0 || row < 0 || col > WIDTH || row >= HEIGHT)
64 return bg_typ;
65 return levl[col][row].typ;
68 static int dirs[16] = { -1, -1 /**/, -1, 0 /**/, -1, 1 /**/, 0, -1 /**/,
69 0, 1 /**/, 1, -1 /**/, 1, 0 /**/, 1, 1 };
71 STATIC_OVL void
72 pass_one(bg_typ, fg_typ)
73 schar bg_typ, fg_typ;
75 register int i, j;
76 short count, dr;
78 for (i = 2; i <= WIDTH; i++)
79 for (j = 1; j < HEIGHT; j++) {
80 for (count = 0, dr = 0; dr < 8; dr++)
81 if (get_map(i + dirs[dr * 2], j + dirs[(dr * 2) + 1], bg_typ)
82 == fg_typ)
83 count++;
85 switch (count) {
86 case 0: /* death */
87 case 1:
88 case 2:
89 levl[i][j].typ = bg_typ;
90 break;
91 case 5:
92 case 6:
93 case 7:
94 case 8:
95 levl[i][j].typ = fg_typ;
96 break;
97 default:
98 break;
103 #define new_loc(i, j) *(new_locations + ((j) * (WIDTH + 1)) + (i))
105 STATIC_OVL void
106 pass_two(bg_typ, fg_typ)
107 schar bg_typ, fg_typ;
109 register int i, j;
110 short count, dr;
112 for (i = 2; i <= WIDTH; i++)
113 for (j = 1; j < HEIGHT; j++) {
114 for (count = 0, dr = 0; dr < 8; dr++)
115 if (get_map(i + dirs[dr * 2], j + dirs[(dr * 2) + 1], bg_typ)
116 == fg_typ)
117 count++;
118 if (count == 5)
119 new_loc(i, j) = bg_typ;
120 else
121 new_loc(i, j) = get_map(i, j, bg_typ);
124 for (i = 2; i <= WIDTH; i++)
125 for (j = 1; j < HEIGHT; j++)
126 levl[i][j].typ = new_loc(i, j);
129 STATIC_OVL void
130 pass_three(bg_typ, fg_typ)
131 schar bg_typ, fg_typ;
133 register int i, j;
134 short count, dr;
136 for (i = 2; i <= WIDTH; i++)
137 for (j = 1; j < HEIGHT; j++) {
138 for (count = 0, dr = 0; dr < 8; dr++)
139 if (get_map(i + dirs[dr * 2], j + dirs[(dr * 2) + 1], bg_typ)
140 == fg_typ)
141 count++;
142 if (count < 3)
143 new_loc(i, j) = bg_typ;
144 else
145 new_loc(i, j) = get_map(i, j, bg_typ);
148 for (i = 2; i <= WIDTH; i++)
149 for (j = 1; j < HEIGHT; j++)
150 levl[i][j].typ = new_loc(i, j);
154 * use a flooding algorithm to find all locations that should
155 * have the same rm number as the current location.
156 * if anyroom is TRUE, use IS_ROOM to check room membership instead of
157 * exactly matching levl[sx][sy].typ and walls are included as well.
159 void
160 flood_fill_rm(sx, sy, rmno, lit, anyroom)
161 int sx;
162 register int sy;
163 register int rmno;
164 boolean lit;
165 boolean anyroom;
167 register int i;
168 int nx;
169 schar fg_typ = levl[sx][sy].typ;
171 /* back up to find leftmost uninitialized location */
172 while (sx > 0 && (anyroom ? IS_ROOM(levl[sx][sy].typ)
173 : levl[sx][sy].typ == fg_typ)
174 && (int) levl[sx][sy].roomno != rmno)
175 sx--;
176 sx++; /* compensate for extra decrement */
178 /* assume sx,sy is valid */
179 if (sx < min_rx)
180 min_rx = sx;
181 if (sy < min_ry)
182 min_ry = sy;
184 for (i = sx; i <= WIDTH && levl[i][sy].typ == fg_typ; i++) {
185 levl[i][sy].roomno = rmno;
186 levl[i][sy].lit = lit;
187 if (anyroom) {
188 /* add walls to room as well */
189 register int ii, jj;
190 for (ii = (i == sx ? i - 1 : i); ii <= i + 1; ii++)
191 for (jj = sy - 1; jj <= sy + 1; jj++)
192 if (isok(ii, jj) && (IS_WALL(levl[ii][jj].typ)
193 || IS_DOOR(levl[ii][jj].typ)
194 || levl[ii][jj].typ == SDOOR)) {
195 levl[ii][jj].edge = 1;
196 if (lit)
197 levl[ii][jj].lit = lit;
198 if ((int) levl[ii][jj].roomno != rmno)
199 levl[ii][jj].roomno = SHARED;
202 n_loc_filled++;
204 nx = i;
206 if (isok(sx, sy - 1)) {
207 for (i = sx; i < nx; i++)
208 if (levl[i][sy - 1].typ == fg_typ) {
209 if ((int) levl[i][sy - 1].roomno != rmno)
210 flood_fill_rm(i, sy - 1, rmno, lit, anyroom);
211 } else {
212 if ((i > sx || isok(i - 1, sy - 1))
213 && levl[i - 1][sy - 1].typ == fg_typ) {
214 if ((int) levl[i - 1][sy - 1].roomno != rmno)
215 flood_fill_rm(i - 1, sy - 1, rmno, lit, anyroom);
217 if ((i < nx - 1 || isok(i + 1, sy - 1))
218 && levl[i + 1][sy - 1].typ == fg_typ) {
219 if ((int) levl[i + 1][sy - 1].roomno != rmno)
220 flood_fill_rm(i + 1, sy - 1, rmno, lit, anyroom);
224 if (isok(sx, sy + 1)) {
225 for (i = sx; i < nx; i++)
226 if (levl[i][sy + 1].typ == fg_typ) {
227 if ((int) levl[i][sy + 1].roomno != rmno)
228 flood_fill_rm(i, sy + 1, rmno, lit, anyroom);
229 } else {
230 if ((i > sx || isok(i - 1, sy + 1))
231 && levl[i - 1][sy + 1].typ == fg_typ) {
232 if ((int) levl[i - 1][sy + 1].roomno != rmno)
233 flood_fill_rm(i - 1, sy + 1, rmno, lit, anyroom);
235 if ((i < nx - 1 || isok(i + 1, sy + 1))
236 && levl[i + 1][sy + 1].typ == fg_typ) {
237 if ((int) levl[i + 1][sy + 1].roomno != rmno)
238 flood_fill_rm(i + 1, sy + 1, rmno, lit, anyroom);
243 if (nx > max_rx)
244 max_rx = nx - 1; /* nx is just past valid region */
245 if (sy > max_ry)
246 max_ry = sy;
250 * If we have drawn a map without walls, this allows us to
251 * auto-magically wallify it. Taken from lev_main.c.
253 STATIC_OVL void
254 wallify_map()
256 int x, y, xx, yy;
258 for (x = 1; x < COLNO; x++)
259 for (y = 0; y < ROWNO; y++)
260 if (levl[x][y].typ == STONE) {
261 for (yy = y - 1; yy <= y + 1; yy++)
262 for (xx = x - 1; xx <= x + 1; xx++)
263 if (isok(xx, yy) && levl[xx][yy].typ == ROOM) {
264 if (yy != y)
265 levl[x][y].typ = HWALL;
266 else
267 levl[x][y].typ = VWALL;
272 STATIC_OVL void
273 join_map(bg_typ, fg_typ)
274 schar bg_typ, fg_typ;
276 register struct mkroom *croom, *croom2;
278 register int i, j;
279 int sx, sy;
280 coord sm, em;
282 /* first, use flood filling to find all of the regions that need joining
284 for (i = 2; i <= WIDTH; i++)
285 for (j = 1; j < HEIGHT; j++) {
286 if (levl[i][j].typ == fg_typ && levl[i][j].roomno == NO_ROOM) {
287 min_rx = max_rx = i;
288 min_ry = max_ry = j;
289 n_loc_filled = 0;
290 flood_fill_rm(i, j, nroom + ROOMOFFSET, FALSE, FALSE);
291 if (n_loc_filled > 3) {
292 add_room(min_rx, min_ry, max_rx, max_ry, FALSE, OROOM,
293 TRUE);
294 rooms[nroom - 1].irregular = TRUE;
295 if (nroom >= (MAXNROFROOMS * 2))
296 goto joinm;
297 } else {
299 * it's a tiny hole; erase it from the map to avoid
300 * having the player end up here with no way out.
302 for (sx = min_rx; sx <= max_rx; sx++)
303 for (sy = min_ry; sy <= max_ry; sy++)
304 if ((int) levl[sx][sy].roomno
305 == nroom + ROOMOFFSET) {
306 levl[sx][sy].typ = bg_typ;
307 levl[sx][sy].roomno = NO_ROOM;
313 joinm:
315 * Ok, now we can actually join the regions with fg_typ's.
316 * The rooms are already sorted due to the previous loop,
317 * so don't call sort_rooms(), which can screw up the roomno's
318 * validity in the levl structure.
320 for (croom = &rooms[0], croom2 = croom + 1; croom2 < &rooms[nroom];) {
321 /* pick random starting and end locations for "corridor" */
322 if (!somexy(croom, &sm) || !somexy(croom2, &em)) {
323 /* ack! -- the level is going to be busted */
324 /* arbitrarily pick centers of both rooms and hope for the best */
325 impossible("No start/end room loc in join_map.");
326 sm.x = croom->lx + ((croom->hx - croom->lx) / 2);
327 sm.y = croom->ly + ((croom->hy - croom->ly) / 2);
328 em.x = croom2->lx + ((croom2->hx - croom2->lx) / 2);
329 em.y = croom2->ly + ((croom2->hy - croom2->ly) / 2);
332 (void) dig_corridor(&sm, &em, FALSE, fg_typ, bg_typ);
334 /* choose next region to join */
335 /* only increment croom if croom and croom2 are non-overlapping */
336 if (croom2->lx > croom->hx
337 || ((croom2->ly > croom->hy || croom2->hy < croom->ly)
338 && rn2(3))) {
339 croom = croom2;
341 croom2++; /* always increment the next room */
345 STATIC_OVL void
346 finish_map(fg_typ, bg_typ, lit, walled, icedpools)
347 schar fg_typ, bg_typ;
348 boolean lit, walled, icedpools;
350 int i, j;
352 if (walled)
353 wallify_map();
355 if (lit) {
356 for (i = 1; i < COLNO; i++)
357 for (j = 0; j < ROWNO; j++)
358 if ((!IS_ROCK(fg_typ) && levl[i][j].typ == fg_typ)
359 || (!IS_ROCK(bg_typ) && levl[i][j].typ == bg_typ)
360 || (bg_typ == TREE && levl[i][j].typ == bg_typ)
361 || (walled && IS_WALL(levl[i][j].typ)))
362 levl[i][j].lit = TRUE;
363 for (i = 0; i < nroom; i++)
364 rooms[i].rlit = 1;
366 /* light lava even if everything's otherwise unlit;
367 ice might be frozen pool rather than frozen moat */
368 for (i = 1; i < COLNO; i++)
369 for (j = 0; j < ROWNO; j++) {
370 if (levl[i][j].typ == LAVAPOOL)
371 levl[i][j].lit = TRUE;
372 else if (levl[i][j].typ == ICE)
373 levl[i][j].icedpool = icedpools ? ICED_POOL : ICED_MOAT;
378 * When level processed by join_map is overlaid by a MAP, some rooms may no
379 * longer be valid. All rooms in the region lx <= x < hx, ly <= y < hy are
380 * removed. Rooms partially in the region are truncated. This function
381 * must be called before the REGIONs or ROOMs of the map are processed, or
382 * those rooms will be removed as well. Assumes roomno fields in the
383 * region are already cleared, and roomno and irregular fields outside the
384 * region are all set.
386 void
387 remove_rooms(lx, ly, hx, hy)
388 int lx, ly, hx, hy;
390 int i;
391 struct mkroom *croom;
393 for (i = nroom - 1; i >= 0; --i) {
394 croom = &rooms[i];
395 if (croom->hx < lx || croom->lx >= hx || croom->hy < ly
396 || croom->ly >= hy)
397 continue; /* no overlap */
399 if (croom->lx < lx || croom->hx >= hx || croom->ly < ly
400 || croom->hy >= hy) { /* partial overlap */
401 /* TODO: ensure remaining parts of room are still joined */
403 if (!croom->irregular)
404 impossible("regular room in joined map");
405 } else {
406 /* total overlap, remove the room */
407 remove_room((unsigned) i);
413 * Remove roomno from the rooms array, decrementing nroom. Also updates
414 * all level roomno values of affected higher numbered rooms. Assumes
415 * level structure contents corresponding to roomno have already been reset.
416 * Currently handles only the removal of rooms that have no subrooms.
418 STATIC_OVL void
419 remove_room(roomno)
420 unsigned roomno;
422 struct mkroom *croom = &rooms[roomno];
423 struct mkroom *maxroom = &rooms[--nroom];
424 int i, j;
425 unsigned oroomno;
427 if (croom != maxroom) {
428 /* since the order in the array only matters for making corridors,
429 * copy the last room over the one being removed on the assumption
430 * that corridors have already been dug. */
431 (void) memcpy((genericptr_t) croom, (genericptr_t) maxroom,
432 sizeof(struct mkroom));
434 /* since maxroom moved, update affected level roomno values */
435 oroomno = nroom + ROOMOFFSET;
436 roomno += ROOMOFFSET;
437 for (i = croom->lx; i <= croom->hx; ++i)
438 for (j = croom->ly; j <= croom->hy; ++j) {
439 if (levl[i][j].roomno == oroomno)
440 levl[i][j].roomno = roomno;
444 maxroom->hx = -1; /* just like add_room */
447 #define N_P1_ITER 1 /* tune map generation via this value */
448 #define N_P2_ITER 1 /* tune map generation via this value */
449 #define N_P3_ITER 2 /* tune map smoothing via this value */
451 void
452 mkmap(init_lev)
453 lev_init *init_lev;
455 schar bg_typ = init_lev->bg, fg_typ = init_lev->fg;
456 boolean smooth = init_lev->smoothed, join = init_lev->joined;
457 xchar lit = init_lev->lit, walled = init_lev->walled;
458 int i;
460 if (lit < 0)
461 lit = (rnd(1 + abs(depth(&u.uz))) < 11 && rn2(77)) ? 1 : 0;
463 new_locations = (char *) alloc((WIDTH + 1) * HEIGHT);
465 init_map(bg_typ);
466 init_fill(bg_typ, fg_typ);
468 for (i = 0; i < N_P1_ITER; i++)
469 pass_one(bg_typ, fg_typ);
471 for (i = 0; i < N_P2_ITER; i++)
472 pass_two(bg_typ, fg_typ);
474 if (smooth)
475 for (i = 0; i < N_P3_ITER; i++)
476 pass_three(bg_typ, fg_typ);
478 if (join)
479 join_map(bg_typ, fg_typ);
481 finish_map(fg_typ, bg_typ, (boolean) lit, (boolean) walled,
482 init_lev->icedpools);
483 /* a walled, joined level is cavernous, not mazelike -dlc */
484 if (walled && join) {
485 level.flags.is_maze_lev = FALSE;
486 level.flags.is_cavernous_lev = TRUE;
488 free(new_locations);
491 /*mkmap.c*/