NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / mkmaze.c
blobd99b8c92af5078e05f9ad84ec3e11e38355902b4
1 /* aNetHack 0.0.1 mkmaze.c $ANH-Date: 1469930897 2016/07/31 02:08:17 $ $ANH-Branch: master $:$ANH-Revision: 1.50 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "sp_lev.h"
7 #include "lev.h" /* save & restore info */
9 /* from sp_lev.c, for fixup_special() */
10 extern lev_region *lregions;
11 extern int num_lregions;
12 /* for preserving the insect legs when wallifying baalz level */
13 static lev_region bughack = { {COLNO, ROWNO, 0, 0}, {COLNO, ROWNO, 0, 0} };
15 STATIC_DCL int FDECL(iswall, (int, int));
16 STATIC_DCL int FDECL(iswall_or_stone, (int, int));
17 STATIC_DCL boolean FDECL(is_solid, (int, int));
18 STATIC_DCL int FDECL(extend_spine, (int[3][3], int, int, int));
19 STATIC_DCL boolean FDECL(okay, (int, int, int));
20 STATIC_DCL void FDECL(maze0xy, (coord *));
21 STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
22 XCHAR_P, XCHAR_P, XCHAR_P,
23 XCHAR_P, BOOLEAN_P, d_level *));
24 STATIC_DCL void NDECL(baalz_fixup);
25 STATIC_DCL void NDECL(setup_waterlevel);
26 STATIC_DCL void NDECL(unsetup_waterlevel);
28 /* adjust a coordinate one step in the specified direction */
29 #define mz_move(X, Y, dir) \
30 do { \
31 switch (dir) { \
32 case 0: --(Y); break; \
33 case 1: (X)++; break; \
34 case 2: (Y)++; break; \
35 case 3: --(X); break; \
36 default: panic("mz_move: bad direction %d", dir); \
37 } \
38 } while (0)
40 STATIC_OVL int
41 iswall(x, y)
42 int x, y;
44 int type;
46 if (!isok(x, y))
47 return 0;
48 type = levl[x][y].typ;
49 return (IS_WALL(type) || IS_DOOR(type)
50 || type == SDOOR || type == IRONBARS);
53 STATIC_OVL int
54 iswall_or_stone(x, y)
55 int x, y;
57 /* out of bounds = stone */
58 if (!isok(x, y))
59 return 1;
61 return (levl[x][y].typ == STONE || iswall(x, y));
64 /* return TRUE if out of bounds, wall or rock */
65 STATIC_OVL boolean
66 is_solid(x, y)
67 int x, y;
69 return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
73 * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
74 * a wall spine in the (dx,dy) direction. Return 0 otherwise.
76 * To extend a wall spine in that direction, first there must be a wall there.
77 * Then, extend a spine unless the current position is surrounded by walls
78 * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W'
79 * a wall, '.' a room, 'a' anything (we don't care), and our direction is
80 * (0,1) - South or down - then:
82 * a a a
83 * W x W This would not extend a spine from x down
84 * W W W (a corridor of walls is formed).
86 * a a a
87 * W x W This would extend a spine from x down.
88 * . W W
90 STATIC_OVL int
91 extend_spine(locale, wall_there, dx, dy)
92 int locale[3][3];
93 int wall_there, dx, dy;
95 int spine, nx, ny;
97 nx = 1 + dx;
98 ny = 1 + dy;
100 if (wall_there) { /* wall in that direction */
101 if (dx) {
102 if (locale[1][0] && locale[1][2] /* EW are wall/stone */
103 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
104 spine = 0;
105 } else {
106 spine = 1;
108 } else { /* dy */
109 if (locale[0][1] && locale[2][1] /* NS are wall/stone */
110 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
111 spine = 0;
112 } else {
113 spine = 1;
116 } else {
117 spine = 0;
120 return spine;
123 /* Remove walls totally surrounded by stone */
124 void
125 wall_cleanup(x1, y1, x2, y2)
126 int x1, y1, x2, y2;
128 uchar type;
129 int x, y;
130 struct rm *lev;
132 /* sanity check on incoming variables */
133 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
134 panic("wall_cleanup: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
136 /* change walls surrounded by rock to rock. */
137 for (x = x1; x <= x2; x++)
138 for (y = y1; y <= y2; y++) {
139 if (within_bounded_area(x, y,
140 bughack.inarea.x1, bughack.inarea.y1,
141 bughack.inarea.x2, bughack.inarea.y2))
142 continue;
143 lev = &levl[x][y];
144 type = lev->typ;
145 if (IS_WALL(type) && type != DBWALL) {
146 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
147 && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
148 && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
149 && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
150 lev->typ = STONE;
155 /* Correct wall types so they extend and connect to each other */
156 void
157 fix_wall_spines(x1, y1, x2, y2)
158 int x1, y1, x2, y2;
160 uchar type;
161 int x, y;
162 struct rm *lev;
163 int FDECL((*loc_f), (int, int));
164 int bits;
165 int locale[3][3]; /* rock or wall status surrounding positions */
168 * Value 0 represents a free-standing wall. It could be anything,
169 * so even though this table says VWALL, we actually leave whatever
170 * typ was there alone.
172 static xchar spine_array[16] = { VWALL, HWALL, HWALL, HWALL,
173 VWALL, TRCORNER, TLCORNER, TDWALL,
174 VWALL, BRCORNER, BLCORNER, TUWALL,
175 VWALL, TLWALL, TRWALL, CROSSWALL };
177 /* sanity check on incoming variables */
178 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
179 panic("wall_extends: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
181 /* set the correct wall type. */
182 for (x = x1; x <= x2; x++)
183 for (y = y1; y <= y2; y++) {
184 lev = &levl[x][y];
185 type = lev->typ;
186 if (!(IS_WALL(type) && type != DBWALL))
187 continue;
189 /* set the locations TRUE if rock or wall or out of bounds */
190 loc_f = within_bounded_area(x, y, /* for baalz insect */
191 bughack.inarea.x1, bughack.inarea.y1,
192 bughack.inarea.x2, bughack.inarea.y2)
193 ? iswall
194 : iswall_or_stone;
195 locale[0][0] = (*loc_f)(x - 1, y - 1);
196 locale[1][0] = (*loc_f)(x, y - 1);
197 locale[2][0] = (*loc_f)(x + 1, y - 1);
199 locale[0][1] = (*loc_f)(x - 1, y);
200 locale[2][1] = (*loc_f)(x + 1, y);
202 locale[0][2] = (*loc_f)(x - 1, y + 1);
203 locale[1][2] = (*loc_f)(x, y + 1);
204 locale[2][2] = (*loc_f)(x + 1, y + 1);
206 /* determine if wall should extend to each direction NSEW */
207 bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
208 | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
209 | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
210 | extend_spine(locale, iswall(x - 1, y), -1, 0);
212 /* don't change typ if wall is free-standing */
213 if (bits)
214 lev->typ = spine_array[bits];
218 void
219 wallification(x1, y1, x2, y2)
220 int x1, y1, x2, y2;
222 wall_cleanup(x1, y1, x2, y2);
223 fix_wall_spines(x1, y1, x2, y2);
226 STATIC_OVL boolean
227 okay(x, y, dir)
228 int x, y;
229 int dir;
231 mz_move(x, y, dir);
232 mz_move(x, y, dir);
233 if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max
234 || levl[x][y].typ != STONE)
235 return FALSE;
236 return TRUE;
239 /* find random starting point for maze generation */
240 STATIC_OVL void
241 maze0xy(cc)
242 coord *cc;
244 cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
245 cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
246 return;
250 * Bad if:
251 * pos is occupied OR
252 * pos is inside restricted region (lx,ly,hx,hy) OR
253 * NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
255 boolean
256 bad_location(x, y, lx, ly, hx, hy)
257 xchar x, y;
258 xchar lx, ly, hx, hy;
260 return (boolean) (occupied(x, y)
261 || within_bounded_area(x, y, lx, ly, hx, hy)
262 || !((levl[x][y].typ == CORR && level.flags.is_maze_lev)
263 || levl[x][y].typ == ROOM
264 || levl[x][y].typ == AIR));
267 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
268 and place something (based on rtype) in that region */
269 void
270 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
271 xchar lx, ly, hx, hy;
272 xchar nlx, nly, nhx, nhy;
273 xchar rtype;
274 d_level *lev;
276 int trycnt;
277 boolean oneshot;
278 xchar x, y;
280 if (!lx) { /* default to whole level */
282 * if there are rooms and this a branch, let place_branch choose
283 * the branch location (to avoid putting branches in corridors).
285 if (rtype == LR_BRANCH && nroom) {
286 place_branch(Is_branchlev(&u.uz), 0, 0);
287 return;
290 lx = 1;
291 hx = COLNO - 1;
292 ly = 1;
293 hy = ROWNO - 1;
296 /* first a probabilistic approach */
298 oneshot = (lx == hx && ly == hy);
299 for (trycnt = 0; trycnt < 200; trycnt++) {
300 x = rn1((hx - lx) + 1, lx);
301 y = rn1((hy - ly) + 1, ly);
302 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
303 return;
306 /* then a deterministic one */
308 oneshot = TRUE;
309 for (x = lx; x <= hx; x++)
310 for (y = ly; y <= hy; y++)
311 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot,
312 lev))
313 return;
315 impossible("Couldn't place lregion type %d!", rtype);
318 STATIC_OVL boolean
319 put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev)
320 xchar x, y;
321 xchar nlx, nly, nhx, nhy;
322 xchar rtype;
323 boolean oneshot;
324 d_level *lev;
326 if (bad_location(x, y, nlx, nly, nhx, nhy)) {
327 if (!oneshot) {
328 return FALSE; /* caller should try again */
329 } else {
330 /* Must make do with the only location possible;
331 avoid failure due to a misplaced trap.
332 It might still fail if there's a dungeon feature here. */
333 struct trap *t = t_at(x, y);
335 if (t && t->ttyp != MAGIC_PORTAL && t->ttyp != VIBRATING_SQUARE)
336 deltrap(t);
337 if (bad_location(x, y, nlx, nly, nhx, nhy))
338 return FALSE;
341 switch (rtype) {
342 case LR_TELE:
343 case LR_UPTELE:
344 case LR_DOWNTELE:
345 /* "something" means the player in this case */
346 if (MON_AT(x, y)) {
347 /* move the monster if no choice, or just try again */
348 if (oneshot)
349 (void) rloc(m_at(x, y), FALSE);
350 else
351 return FALSE;
353 u_on_newpos(x, y);
354 break;
355 case LR_PORTAL:
356 mkportal(x, y, lev->dnum, lev->dlevel);
357 break;
358 case LR_DOWNSTAIR:
359 case LR_UPSTAIR:
360 mkstairs(x, y, (char) rtype, (struct mkroom *) 0);
361 break;
362 case LR_BRANCH:
363 place_branch(Is_branchlev(&u.uz), x, y);
364 break;
366 return TRUE;
369 /* fix up Baalzebub's lair, which depicts a level-sized beetle;
370 its legs are walls within solid rock--regular wallification
371 classifies them as superfluous and gets rid of them */
372 STATIC_OVL void
373 baalz_fixup()
375 struct monst *mtmp;
376 int x, y, lastx, lasty;
379 * baalz level's nondiggable region surrounds the "insect" and rooms.
380 * The outermost perimeter of that region is subject to wall cleanup
381 * (hence 'x + 1' and 'y + 1' for starting don't-clean column and row,
382 * 'lastx - 1' and 'lasty - 1' for ending don't-clean column and row)
383 * and the interior is protected against that (in wall_cleanup()).
385 * Assumes level.flags.corrmaze is True, otherwise the bug legs will
386 * have already been "cleaned" away by general wallification.
389 /* find low and high x for to-be-wallified portion of level */
390 y = ROWNO / 2;
391 for (lastx = x = 0; x < COLNO; ++x)
392 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
393 if (!lastx)
394 bughack.inarea.x1 = x + 1;
395 lastx = x;
397 bughack.inarea.x2 = ((lastx > bughack.inarea.x1) ? lastx : x) - 1;
398 /* find low and high y for to-be-wallified portion of level */
399 x = bughack.inarea.x1;
400 for (lasty = y = 0; y < ROWNO; ++y)
401 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
402 if (!lasty)
403 bughack.inarea.y1 = y + 1;
404 lasty = y;
406 bughack.inarea.y2 = ((lasty > bughack.inarea.y1) ? lasty : y) - 1;
407 /* two pools mark where special post-wallify fix-ups are needed */
408 for (x = bughack.inarea.x1; x <= bughack.inarea.x2; ++x)
409 for (y = bughack.inarea.y1; y <= bughack.inarea.y2; ++y)
410 if (levl[x][y].typ == POOL) {
411 levl[x][y].typ = HWALL;
412 if (bughack.delarea.x1 == COLNO)
413 bughack.delarea.x1 = x, bughack.delarea.y1 = y;
414 else
415 bughack.delarea.x2 = x, bughack.delarea.y2 = y;
416 } else if (levl[x][y].typ == IRONBARS) {
417 /* novelty effect; allowing digging in front of 'eyes' */
418 levl[x - 1][y].wall_info &= ~W_NONDIGGABLE;
419 if (isok(x - 2, y))
420 levl[x - 2][y].wall_info &= ~W_NONDIGGABLE;
423 wallification(max(bughack.inarea.x1 - 2, 1),
424 max(bughack.inarea.y1 - 2, 0),
425 min(bughack.inarea.x2 + 2, COLNO - 1),
426 min(bughack.inarea.y2 + 2, ROWNO - 1));
428 /* bughack hack for rear-most legs on baalz level; first joint on
429 both top and bottom gets a bogus extra connection to room area,
430 producing unwanted rectangles; change back to separated legs */
431 x = bughack.delarea.x1, y = bughack.delarea.y1;
432 if (isok(x, y) && levl[x][y].typ == TLWALL
433 && isok(x, y + 1) && levl[x][y + 1].typ == TUWALL) {
434 levl[x][y].typ = BRCORNER;
435 levl[x][y + 1].typ = HWALL;
436 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
437 (void) rloc(mtmp, FALSE);
439 x = bughack.delarea.x2, y = bughack.delarea.y2;
440 if (isok(x, y) && levl[x][y].typ == TLWALL
441 && isok(x, y - 1) && levl[x][y - 1].typ == TDWALL) {
442 levl[x][y].typ = TRCORNER;
443 levl[x][y - 1].typ = HWALL;
444 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
445 (void) rloc(mtmp, FALSE);
448 /* reset bughack region; set low end to <COLNO,ROWNO> so that
449 within_bounded_region() in fix_wall_spines() will fail
450 most quickly--on its first test--when loading other levels */
451 bughack.inarea.x1 = bughack.delarea.x1 = COLNO;
452 bughack.inarea.y1 = bughack.delarea.y1 = ROWNO;
453 bughack.inarea.x2 = bughack.delarea.x2 = 0;
454 bughack.inarea.y2 = bughack.delarea.y2 = 0;
457 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
459 /* this is special stuff that the level compiler cannot (yet) handle */
460 void
461 fixup_special()
463 lev_region *r = lregions;
464 struct d_level lev;
465 int x, y;
466 struct mkroom *croom;
467 boolean added_branch = FALSE;
469 if (was_waterlevel) {
470 was_waterlevel = FALSE;
471 u.uinwater = 0;
472 unsetup_waterlevel();
474 if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
475 level.flags.hero_memory = 0;
476 was_waterlevel = TRUE;
477 /* water level is an odd beast - it has to be set up
478 before calling place_lregions etc. */
479 setup_waterlevel();
481 for (x = 0; x < num_lregions; x++, r++) {
482 switch (r->rtype) {
483 case LR_BRANCH:
484 added_branch = TRUE;
485 goto place_it;
487 case LR_PORTAL:
488 if (*r->rname.str >= '0' && *r->rname.str <= '9') {
489 /* "chutes and ladders" */
490 lev = u.uz;
491 lev.dlevel = atoi(r->rname.str);
492 } else {
493 s_level *sp = find_level(r->rname.str);
494 lev = sp->dlevel;
496 /* fall into... */
498 case LR_UPSTAIR:
499 case LR_DOWNSTAIR:
500 place_it:
501 place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
502 r->inarea.y2, r->delarea.x1, r->delarea.y1,
503 r->delarea.x2, r->delarea.y2, r->rtype, &lev);
504 break;
506 case LR_TELE:
507 case LR_UPTELE:
508 case LR_DOWNTELE:
509 /* save the region outlines for goto_level() */
510 if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
511 updest.lx = r->inarea.x1;
512 updest.ly = r->inarea.y1;
513 updest.hx = r->inarea.x2;
514 updest.hy = r->inarea.y2;
515 updest.nlx = r->delarea.x1;
516 updest.nly = r->delarea.y1;
517 updest.nhx = r->delarea.x2;
518 updest.nhy = r->delarea.y2;
520 if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
521 dndest.lx = r->inarea.x1;
522 dndest.ly = r->inarea.y1;
523 dndest.hx = r->inarea.x2;
524 dndest.hy = r->inarea.y2;
525 dndest.nlx = r->delarea.x1;
526 dndest.nly = r->delarea.y1;
527 dndest.nhx = r->delarea.x2;
528 dndest.nhy = r->delarea.y2;
530 /* place_lregion gets called from goto_level() */
531 break;
534 if (r->rname.str)
535 free((genericptr_t) r->rname.str), r->rname.str = 0;
538 /* place dungeon branch if not placed above */
539 if (!added_branch && Is_branchlev(&u.uz)) {
540 place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
543 /* Still need to add some stuff to level file */
544 if (Is_medusa_level(&u.uz)) {
545 struct obj *otmp;
546 int tryct;
548 croom = &rooms[0]; /* only one room on the medusa level */
549 for (tryct = rnd(4); tryct; tryct--) {
550 x = somex(croom);
551 y = somey(croom);
552 if (goodpos(x, y, (struct monst *) 0, 0)) {
553 otmp = mk_tt_object(STATUE, x, y);
554 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm])
555 || pm_resistance(&mons[otmp->corpsenm],
556 MR_STONE))) {
557 /* set_corpsenm() handles weight too */
558 set_corpsenm(otmp, rndmonnum());
563 if (rn2(2))
564 otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
565 else /* Medusa statues don't contain books */
566 otmp =
567 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
568 somex(croom), somey(croom), CORPSTAT_NONE);
569 if (otmp) {
570 while (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
571 || poly_when_stoned(&mons[otmp->corpsenm])) {
572 /* set_corpsenm() handles weight too */
573 set_corpsenm(otmp, rndmonnum());
576 } else if (Is_wiz1_level(&u.uz)) {
577 croom = search_special(MORGUE);
579 create_secret_door(croom, W_SOUTH | W_EAST | W_WEST);
580 } else if (Is_knox(&u.uz)) {
581 /* using an unfilled morgue for rm id */
582 croom = search_special(MORGUE);
583 /* avoid inappropriate morgue-related messages */
584 level.flags.graveyard = level.flags.has_morgue = 0;
585 croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
586 /* stock the main vault */
587 for (x = croom->lx; x <= croom->hx; x++)
588 for (y = croom->ly; y <= croom->hy; y++) {
589 (void) mkgold((long) rn1(300, 600), x, y);
590 if (!rn2(3) && !is_pool(x, y))
591 (void) maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
593 } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
594 /* less chance for undead corpses (lured from lower morgues) */
595 level.flags.graveyard = 1;
596 } else if (Is_stronghold(&u.uz)) {
597 level.flags.graveyard = 1;
598 } else if (Is_sanctum(&u.uz)) {
599 croom = search_special(TEMPLE);
601 create_secret_door(croom, W_ANY);
602 } else if (on_level(&u.uz, &orcus_level)) {
603 struct monst *mtmp, *mtmp2;
605 /* it's a ghost town, get rid of shopkeepers */
606 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
607 mtmp2 = mtmp->nmon;
608 if (mtmp->isshk)
609 mongone(mtmp);
611 } else if (on_level(&u.uz, &baalzebub_level)) {
612 /* custom wallify the "beetle" potion of the level */
613 baalz_fixup();
616 if (lregions)
617 free((genericptr_t) lregions), lregions = 0;
618 num_lregions = 0;
621 boolean
622 maze_inbounds(x, y)
623 int x, y;
625 return (x >= 2 && y >= 2
626 && x < x_maze_max && y < y_maze_max && isok(x, y));
629 void
630 maze_remove_deadends(typ)
631 xchar typ;
633 char dirok[4];
634 int x, y, dir, idx, idx2, dx, dy, dx2, dy2;
636 dirok[0] = 0; /* lint suppression */
637 for (x = 2; x < x_maze_max; x++)
638 for (y = 2; y < y_maze_max; y++)
639 if (ACCESSIBLE(levl[x][y].typ) && (x % 2) && (y % 2)) {
640 idx = idx2 = 0;
641 for (dir = 0; dir < 4; dir++) {
642 /* note: mz_move() is a macro which modifies
643 one of its first two parameters */
644 dx = dx2 = x;
645 dy = dy2 = y;
646 mz_move(dx, dy, dir);
647 if (!maze_inbounds(dx, dy)) {
648 idx2++;
649 continue;
651 mz_move(dx2, dy2, dir);
652 mz_move(dx2, dy2, dir);
653 if (!maze_inbounds(dx2, dy2)) {
654 idx2++;
655 continue;
657 if (!ACCESSIBLE(levl[dx][dy].typ)
658 && ACCESSIBLE(levl[dx2][dy2].typ)) {
659 dirok[idx++] = dir;
660 idx2++;
663 if (idx2 >= 3 && idx > 0) {
664 dx = x;
665 dy = y;
666 dir = dirok[rn2(idx)];
667 mz_move(dx, dy, dir);
668 levl[dx][dy].typ = typ;
673 /* Create a maze with specified corridor width and wall thickness
674 * TODO: rewrite walkfrom so it works on temp space, not levl
676 void
677 create_maze(corrwid, wallthick)
678 int corrwid;
679 int wallthick;
681 int x,y;
682 coord mm;
683 int tmp_xmax = x_maze_max;
684 int tmp_ymax = y_maze_max;
685 int rdx = 0;
686 int rdy = 0;
687 int scale;
689 if (wallthick < 1)
690 wallthick = 1;
691 else if (wallthick > 5)
692 wallthick = 5;
694 if (corrwid < 1)
695 corrwid = 1;
696 else if (corrwid > 5)
697 corrwid = 5;
699 scale = corrwid + wallthick;
700 rdx = (x_maze_max / scale);
701 rdy = (y_maze_max / scale);
703 if (level.flags.corrmaze)
704 for (x = 2; x < (rdx * 2); x++)
705 for (y = 2; y < (rdy * 2); y++)
706 levl[x][y].typ = STONE;
707 else
708 for (x = 2; x <= (rdx * 2); x++)
709 for (y = 2; y <= (rdy * 2); y++)
710 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
712 /* set upper bounds for maze0xy and walkfrom */
713 x_maze_max = (rdx * 2);
714 y_maze_max = (rdy * 2);
716 /* create maze */
717 maze0xy(&mm);
718 walkfrom((int) mm.x, (int) mm.y, 0);
720 if (!rn2(5))
721 maze_remove_deadends((level.flags.corrmaze) ? CORR : ROOM);
723 /* restore bounds */
724 x_maze_max = tmp_xmax;
725 y_maze_max = tmp_ymax;
727 /* scale maze up if needed */
728 if (scale > 2) {
729 char tmpmap[COLNO][ROWNO];
730 int rx = 1, ry = 1;
732 /* back up the existing smaller maze */
733 for (x = 1; x < x_maze_max; x++)
734 for (y = 1; y < y_maze_max; y++) {
735 tmpmap[x][y] = levl[x][y].typ;
738 /* do the scaling */
739 rx = x = 2;
740 while (rx < x_maze_max) {
741 int mx = (x % 2) ? corrwid
742 : ((x == 2 || x == (rdx * 2)) ? 1
743 : wallthick);
744 ry = y = 2;
745 while (ry < y_maze_max) {
746 int dx = 0, dy = 0;
747 int my = (y % 2) ? corrwid
748 : ((y == 2 || y == (rdy * 2)) ? 1
749 : wallthick);
750 for (dx = 0; dx < mx; dx++)
751 for (dy = 0; dy < my; dy++) {
752 if (rx+dx >= x_maze_max
753 || ry+dy >= y_maze_max)
754 break;
755 levl[rx + dx][ry + dy].typ = tmpmap[x][y];
757 ry += my;
758 y++;
760 rx += mx;
761 x++;
768 void
769 makemaz(s)
770 const char *s;
772 int x, y;
773 char protofile[20];
774 s_level *sp = Is_special(&u.uz);
775 coord mm;
777 if (*s) {
778 if (sp && sp->rndlevs)
779 Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
780 else
781 Strcpy(protofile, s);
782 } else if (*(dungeons[u.uz.dnum].proto)) {
783 if (dunlevs_in_dungeon(&u.uz) > 1) {
784 if (sp && sp->rndlevs)
785 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
786 dunlev(&u.uz), rnd((int) sp->rndlevs));
787 else
788 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
789 dunlev(&u.uz));
790 } else if (sp && sp->rndlevs) {
791 Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
792 rnd((int) sp->rndlevs));
793 } else
794 Strcpy(protofile, dungeons[u.uz.dnum].proto);
796 } else
797 Strcpy(protofile, "");
799 /* SPLEVTYPE format is "level-choice,level-choice"... */
800 if (wizard && *protofile && sp && sp->rndlevs) {
801 char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
802 if (ep) {
803 /* rindex always succeeds due to code in prior block */
804 int len = (int) ((rindex(protofile, '-') - protofile) + 1);
806 while (ep && *ep) {
807 if (!strncmp(ep, protofile, len)) {
808 int pick = atoi(ep + len);
809 /* use choice only if valid */
810 if (pick > 0 && pick <= (int) sp->rndlevs)
811 Sprintf(protofile + len, "%d", pick);
812 break;
813 } else {
814 ep = index(ep, ',');
815 if (ep)
816 ++ep;
822 if (*protofile) {
823 Strcat(protofile, LEV_EXT);
824 if (load_special(protofile)) {
825 /* some levels can end up with monsters
826 on dead mon list, including light source monsters */
827 dmonsfree();
828 return; /* no mazification right now */
830 impossible("Couldn't load \"%s\" - making a maze.", protofile);
833 level.flags.is_maze_lev = TRUE;
834 level.flags.corrmaze = !rn2(3);
836 if (!Invocation_lev(&u.uz) && rn2(2)) {
837 int corrscale = rnd(4);
838 create_maze(corrscale,rnd(4)-corrscale);
839 } else {
840 create_maze(1,1);
843 if (!level.flags.corrmaze)
844 wallification(2, 2, x_maze_max, y_maze_max);
846 mazexy(&mm);
847 mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
848 if (!Invocation_lev(&u.uz)) {
849 mazexy(&mm);
850 mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
851 } else { /* choose "vibrating square" location */
852 #define x_maze_min 2
853 #define y_maze_min 2
855 * Pick a position where the stairs down to Moloch's Sanctum
856 * level will ultimately be created. At that time, an area
857 * will be altered: walls removed, moat and traps generated,
858 * boulders destroyed. The position picked here must ensure
859 * that that invocation area won't extend off the map.
861 * We actually allow up to 2 squares around the usual edge of
862 * the area to get truncated; see mkinvokearea(mklev.c).
864 #define INVPOS_X_MARGIN (6 - 2)
865 #define INVPOS_Y_MARGIN (5 - 2)
866 #define INVPOS_DISTANCE 11
867 int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
868 y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
870 if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
871 || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
872 debugpline2("inv_pos: maze is too small! (%d x %d)",
873 x_maze_max, y_maze_max);
875 inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
876 do {
877 x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
878 y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
879 /* we don't want it to be too near the stairs, nor
880 to be on a spot that's already in use (wall|trap) */
881 } while (x == xupstair || y == yupstair /*(direct line)*/
882 || abs(x - xupstair) == abs(y - yupstair)
883 || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
884 || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
885 inv_pos.x = x;
886 inv_pos.y = y;
887 maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
888 #undef INVPOS_X_MARGIN
889 #undef INVPOS_Y_MARGIN
890 #undef INVPOS_DISTANCE
891 #undef x_maze_min
892 #undef y_maze_min
895 /* place branch stair or portal */
896 place_branch(Is_branchlev(&u.uz), 0, 0);
898 for (x = rn1(8, 11); x; x--) {
899 mazexy(&mm);
900 (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
902 for (x = rn1(10, 2); x; x--) {
903 mazexy(&mm);
904 (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
906 for (x = rn2(3); x; x--) {
907 mazexy(&mm);
908 (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
910 for (x = rn1(5, 7); x; x--) {
911 mazexy(&mm);
912 (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
914 for (x = rn1(6, 7); x; x--) {
915 mazexy(&mm);
916 (void) mkgold(0L, mm.x, mm.y);
918 for (x = rn1(6, 7); x; x--)
919 mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
922 #ifdef MICRO
923 /* Make the mazewalk iterative by faking a stack. This is needed to
924 * ensure the mazewalk is successful in the limited stack space of
925 * the program. This iterative version uses the minimum amount of stack
926 * that is totally safe.
928 void
929 walkfrom(x, y, typ)
930 int x, y;
931 schar typ;
933 #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */
934 char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
935 int q, a, dir, pos;
936 int dirs[4];
938 if (!typ) {
939 if (level.flags.corrmaze)
940 typ = CORR;
941 else
942 typ = ROOM;
945 pos = 1;
946 mazex[pos] = (char) x;
947 mazey[pos] = (char) y;
948 while (pos) {
949 x = (int) mazex[pos];
950 y = (int) mazey[pos];
951 if (!IS_DOOR(levl[x][y].typ)) {
952 /* might still be on edge of MAP, so don't overwrite */
953 levl[x][y].typ = typ;
954 levl[x][y].flags = 0;
956 q = 0;
957 for (a = 0; a < 4; a++)
958 if (okay(x, y, a))
959 dirs[q++] = a;
960 if (!q)
961 pos--;
962 else {
963 dir = dirs[rn2(q)];
964 mz_move(x, y, dir);
965 levl[x][y].typ = typ;
966 mz_move(x, y, dir);
967 pos++;
968 if (pos > CELLS)
969 panic("Overflow in walkfrom");
970 mazex[pos] = (char) x;
971 mazey[pos] = (char) y;
975 #else /* !MICRO */
977 void
978 walkfrom(x, y, typ)
979 int x, y;
980 schar typ;
982 int q, a, dir;
983 int dirs[4];
985 if (!typ) {
986 if (level.flags.corrmaze)
987 typ = CORR;
988 else
989 typ = ROOM;
992 if (!IS_DOOR(levl[x][y].typ)) {
993 /* might still be on edge of MAP, so don't overwrite */
994 levl[x][y].typ = typ;
995 levl[x][y].flags = 0;
998 while (1) {
999 q = 0;
1000 for (a = 0; a < 4; a++)
1001 if (okay(x, y, a))
1002 dirs[q++] = a;
1003 if (!q)
1004 return;
1005 dir = dirs[rn2(q)];
1006 mz_move(x, y, dir);
1007 levl[x][y].typ = typ;
1008 mz_move(x, y, dir);
1009 walkfrom(x, y, typ);
1012 #endif /* ?MICRO */
1014 /* find random point in generated corridors,
1015 so we don't create items in moats, bunkers, or walls */
1016 void
1017 mazexy(cc)
1018 coord *cc;
1020 int cpt = 0;
1022 do {
1023 cc->x = 1 + rn2(x_maze_max);
1024 cc->y = 1 + rn2(y_maze_max);
1025 cpt++;
1026 } while (cpt < 100
1027 && levl[cc->x][cc->y].typ
1028 != (level.flags.corrmaze ? CORR : ROOM));
1029 if (cpt >= 100) {
1030 int x, y;
1032 /* last try */
1033 for (x = 1; x < x_maze_max; x++)
1034 for (y = 1; y < y_maze_max; y++) {
1035 cc->x = x;
1036 cc->y = y;
1037 if (levl[cc->x][cc->y].typ
1038 == (level.flags.corrmaze ? CORR : ROOM))
1039 return;
1041 panic("mazexy: can't find a place!");
1043 return;
1046 /* put a non-diggable boundary around the initial portion of a level map.
1047 * assumes that no level will initially put things beyond the isok() range.
1049 * we can't bound unconditionally on the last line with something in it,
1050 * because that something might be a niche which was already reachable,
1051 * so the boundary would be breached
1053 * we can't bound unconditionally on one beyond the last line, because
1054 * that provides a window of abuse for wallified special levels
1056 void
1057 bound_digging()
1059 int x, y;
1060 unsigned typ;
1061 struct rm *lev;
1062 boolean found, nonwall;
1063 int xmin, xmax, ymin, ymax;
1065 if (Is_earthlevel(&u.uz))
1066 return; /* everything diggable here */
1068 found = nonwall = FALSE;
1069 for (xmin = 0; !found && xmin <= COLNO; xmin++) {
1070 lev = &levl[xmin][0];
1071 for (y = 0; y <= ROWNO - 1; y++, lev++) {
1072 typ = lev->typ;
1073 if (typ != STONE) {
1074 found = TRUE;
1075 if (!IS_WALL(typ))
1076 nonwall = TRUE;
1080 xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1081 if (xmin < 0)
1082 xmin = 0;
1084 found = nonwall = FALSE;
1085 for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
1086 lev = &levl[xmax][0];
1087 for (y = 0; y <= ROWNO - 1; y++, lev++) {
1088 typ = lev->typ;
1089 if (typ != STONE) {
1090 found = TRUE;
1091 if (!IS_WALL(typ))
1092 nonwall = TRUE;
1096 xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1097 if (xmax >= COLNO)
1098 xmax = COLNO - 1;
1100 found = nonwall = FALSE;
1101 for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
1102 lev = &levl[xmin][ymin];
1103 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1104 typ = lev->typ;
1105 if (typ != STONE) {
1106 found = TRUE;
1107 if (!IS_WALL(typ))
1108 nonwall = TRUE;
1112 ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1114 found = nonwall = FALSE;
1115 for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
1116 lev = &levl[xmin][ymax];
1117 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1118 typ = lev->typ;
1119 if (typ != STONE) {
1120 found = TRUE;
1121 if (!IS_WALL(typ))
1122 nonwall = TRUE;
1126 ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1128 for (x = 0; x < COLNO; x++)
1129 for (y = 0; y < ROWNO; y++)
1130 if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
1131 #ifdef DCC30_BUG
1132 lev = &levl[x][y];
1133 lev->wall_info |= W_NONDIGGABLE;
1134 #else
1135 levl[x][y].wall_info |= W_NONDIGGABLE;
1136 #endif
1140 void
1141 mkportal(x, y, todnum, todlevel)
1142 xchar x, y, todnum, todlevel;
1144 /* a portal "trap" must be matched by a
1145 portal in the destination dungeon/dlevel */
1146 struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
1148 if (!ttmp) {
1149 impossible("portal on top of portal??");
1150 return;
1152 debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
1153 dungeons[todnum].dname, todlevel);
1154 ttmp->dst.dnum = todnum;
1155 ttmp->dst.dlevel = todlevel;
1156 return;
1159 void
1160 fumaroles()
1162 xchar n;
1163 boolean snd = FALSE, loud = FALSE;
1165 for (n = rn2(3) + 2; n; n--) {
1166 xchar x = rn1(COLNO - 4, 3);
1167 xchar y = rn1(ROWNO - 4, 3);
1169 if (levl[x][y].typ == LAVAPOOL) {
1170 NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
1172 clear_heros_fault(r);
1173 snd = TRUE;
1174 if (distu(x, y) < 15)
1175 loud = TRUE;
1178 if (snd && !Deaf)
1179 Norep("You hear a %swhoosh!", loud ? "loud " : "");
1183 * Special waterlevel stuff in endgame (TH).
1185 * Some of these functions would probably logically belong to some
1186 * other source files, but they are all so nicely encapsulated here.
1189 #define CONS_OBJ 0
1190 #define CONS_MON 1
1191 #define CONS_HERO 2
1192 #define CONS_TRAP 3
1194 static struct bubble *bbubbles, *ebubbles;
1196 static struct trap *wportal;
1197 static int xmin, ymin, xmax, ymax; /* level boundaries */
1198 /* bubble movement boundaries */
1199 #define bxmin (xmin + 1)
1200 #define bymin (ymin + 1)
1201 #define bxmax (xmax - 1)
1202 #define bymax (ymax - 1)
1204 STATIC_DCL void NDECL(set_wportal);
1205 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
1206 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
1208 void
1209 movebubbles()
1211 static boolean up;
1212 struct bubble *b;
1213 int x, y, i, j;
1214 struct trap *btrap;
1215 static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
1216 0, 0, 0, 0, 0, 0 };
1217 static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
1218 1, 0, 0, 0, 0 };
1220 /* set up the portal the first time bubbles are moved */
1221 if (!wportal)
1222 set_wportal();
1224 vision_recalc(2);
1226 if (Is_waterlevel(&u.uz)) {
1227 /* keep attached ball&chain separate from bubble objects */
1228 if (Punished)
1229 unplacebc();
1232 * Pick up everything inside of a bubble then fill all bubble
1233 * locations.
1235 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1236 if (b->cons)
1237 panic("movebubbles: cons != null");
1238 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1239 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1240 if (b->bm[j + 2] & (1 << i)) {
1241 if (!isok(x, y)) {
1242 impossible("movebubbles: bad pos (%d,%d)", x, y);
1243 continue;
1246 /* pick up objects, monsters, hero, and traps */
1247 if (OBJ_AT(x, y)) {
1248 struct obj *olist = (struct obj *) 0, *otmp;
1249 struct container *cons =
1250 (struct container *) alloc(
1251 sizeof(struct container));
1253 while ((otmp = level.objects[x][y]) != 0) {
1254 remove_object(otmp);
1255 otmp->ox = otmp->oy = 0;
1256 otmp->nexthere = olist;
1257 olist = otmp;
1260 cons->x = x;
1261 cons->y = y;
1262 cons->what = CONS_OBJ;
1263 cons->list = (genericptr_t) olist;
1264 cons->next = b->cons;
1265 b->cons = cons;
1267 if (MON_AT(x, y)) {
1268 struct monst *mon = m_at(x, y);
1269 struct container *cons =
1270 (struct container *) alloc(
1271 sizeof(struct container));
1273 cons->x = x;
1274 cons->y = y;
1275 cons->what = CONS_MON;
1276 cons->list = (genericptr_t) mon;
1278 cons->next = b->cons;
1279 b->cons = cons;
1281 if (mon->wormno)
1282 remove_worm(mon);
1283 else
1284 remove_monster(x, y);
1286 newsym(x, y); /* clean up old position */
1287 mon->mx = mon->my = 0;
1289 if (!u.uswallow && x == u.ux && y == u.uy) {
1290 struct container *cons =
1291 (struct container *) alloc(
1292 sizeof(struct container));
1294 cons->x = x;
1295 cons->y = y;
1296 cons->what = CONS_HERO;
1297 cons->list = (genericptr_t) 0;
1299 cons->next = b->cons;
1300 b->cons = cons;
1302 if ((btrap = t_at(x, y)) != 0) {
1303 struct container *cons =
1304 (struct container *) alloc(
1305 sizeof(struct container));
1307 cons->x = x;
1308 cons->y = y;
1309 cons->what = CONS_TRAP;
1310 cons->list = (genericptr_t) btrap;
1312 cons->next = b->cons;
1313 b->cons = cons;
1316 levl[x][y] = water_pos;
1317 block_point(x, y);
1320 } else if (Is_airlevel(&u.uz)) {
1321 for (x = 0; x < COLNO; x++)
1322 for (y = 0; y < ROWNO; y++) {
1323 levl[x][y] = air_pos;
1324 unblock_point(x, y);
1329 * Every second time traverse down. This is because otherwise
1330 * all the junk that changes owners when bubbles overlap
1331 * would eventually end up in the last bubble in the chain.
1333 up = !up;
1334 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1335 int rx = rn2(3), ry = rn2(3);
1337 mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1338 b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1341 /* put attached ball&chain back */
1342 if (Is_waterlevel(&u.uz) && Punished)
1343 placebc();
1344 vision_full_recalc = 1;
1347 /* when moving in water, possibly (1 in 3) alter the intended destination */
1348 void
1349 water_friction()
1351 int x, y, dx, dy;
1352 boolean eff = FALSE;
1354 if (Swimming && rn2(4))
1355 return; /* natural swimmers have advantage */
1357 if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1358 /* cancel delta x and choose an arbitrary delta y value */
1359 x = u.ux;
1360 do {
1361 dy = rn2(3) - 1; /* -1, 0, 1 */
1362 y = u.uy + dy;
1363 } while (dy && (!isok(x, y) || !is_pool(x, y)));
1364 u.dx = 0;
1365 u.dy = dy;
1366 eff = TRUE;
1367 } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1368 /* cancel delta y and choose an arbitrary delta x value */
1369 y = u.uy;
1370 do {
1371 dx = rn2(3) - 1; /* -1 .. 1 */
1372 x = u.ux + dx;
1373 } while (dx && (!isok(x, y) || !is_pool(x, y)));
1374 u.dy = 0;
1375 u.dx = dx;
1376 eff = TRUE;
1378 if (eff)
1379 pline("Water turbulence affects your movements.");
1382 void
1383 save_waterlevel(fd, mode)
1384 int fd, mode;
1386 struct bubble *b;
1388 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1389 return;
1391 if (perform_bwrite(mode)) {
1392 int n = 0;
1393 for (b = bbubbles; b; b = b->next)
1394 ++n;
1395 bwrite(fd, (genericptr_t) &n, sizeof(int));
1396 bwrite(fd, (genericptr_t) &xmin, sizeof(int));
1397 bwrite(fd, (genericptr_t) &ymin, sizeof(int));
1398 bwrite(fd, (genericptr_t) &xmax, sizeof(int));
1399 bwrite(fd, (genericptr_t) &ymax, sizeof(int));
1400 for (b = bbubbles; b; b = b->next)
1401 bwrite(fd, (genericptr_t) b, sizeof(struct bubble));
1403 if (release_data(mode))
1404 unsetup_waterlevel();
1407 void
1408 restore_waterlevel(fd)
1409 int fd;
1411 struct bubble *b = (struct bubble *) 0, *btmp;
1412 int i, n;
1414 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1415 return;
1417 set_wportal();
1418 mread(fd, (genericptr_t) &n, sizeof(int));
1419 mread(fd, (genericptr_t) &xmin, sizeof(int));
1420 mread(fd, (genericptr_t) &ymin, sizeof(int));
1421 mread(fd, (genericptr_t) &xmax, sizeof(int));
1422 mread(fd, (genericptr_t) &ymax, sizeof(int));
1423 for (i = 0; i < n; i++) {
1424 btmp = b;
1425 b = (struct bubble *) alloc(sizeof(struct bubble));
1426 mread(fd, (genericptr_t) b, sizeof(struct bubble));
1427 if (bbubbles) {
1428 btmp->next = b;
1429 b->prev = btmp;
1430 } else {
1431 bbubbles = b;
1432 b->prev = (struct bubble *) 0;
1434 mv_bubble(b, 0, 0, TRUE);
1436 ebubbles = b;
1437 b->next = (struct bubble *) 0;
1438 was_waterlevel = TRUE;
1441 const char *
1442 waterbody_name(x, y)
1443 xchar x, y;
1445 struct rm *lev;
1446 schar ltyp;
1448 if (!isok(x, y))
1449 return "drink"; /* should never happen */
1450 lev = &levl[x][y];
1451 ltyp = lev->typ;
1452 if (ltyp == DRAWBRIDGE_UP)
1453 ltyp = db_under_typ(lev->drawbridgemask);
1455 if (ltyp == LAVAPOOL)
1456 return hliquid("lava");
1457 else if (ltyp == ICE)
1458 return "ice";
1459 else if (ltyp == POOL)
1460 return "pool of water";
1461 else if (ltyp == WATER || Is_waterlevel(&u.uz))
1462 ; /* fall through to default return value */
1463 else if (Is_juiblex_level(&u.uz))
1464 return "swamp";
1465 else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1466 return "moat";
1468 return hliquid("water");
1471 STATIC_OVL void
1472 set_wportal()
1474 /* there better be only one magic portal on water level... */
1475 for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1476 if (wportal->ttyp == MAGIC_PORTAL)
1477 return;
1478 impossible("set_wportal(): no portal!");
1481 STATIC_OVL void
1482 setup_waterlevel()
1484 int x, y;
1485 int xskip, yskip;
1486 int water_glyph = cmap_to_glyph(S_water),
1487 air_glyph = cmap_to_glyph(S_air);
1489 /* ouch, hardcoded... */
1491 xmin = 3;
1492 ymin = 1;
1493 xmax = 78;
1494 ymax = 20;
1496 /* set hero's memory to water */
1498 for (x = xmin; x <= xmax; x++)
1499 for (y = ymin; y <= ymax; y++)
1500 levl[x][y].glyph = Is_waterlevel(&u.uz) ? water_glyph : air_glyph;
1502 /* make bubbles */
1504 if (Is_waterlevel(&u.uz)) {
1505 xskip = 10 + rn2(10);
1506 yskip = 4 + rn2(4);
1507 } else {
1508 xskip = 6 + rn2(4);
1509 yskip = 3 + rn2(3);
1512 for (x = bxmin; x <= bxmax; x += xskip)
1513 for (y = bymin; y <= bymax; y += yskip)
1514 mk_bubble(x, y, rn2(7));
1517 STATIC_OVL void
1518 unsetup_waterlevel()
1520 struct bubble *b, *bb;
1522 /* free bubbles */
1524 for (b = bbubbles; b; b = bb) {
1525 bb = b->next;
1526 free((genericptr_t) b);
1528 bbubbles = ebubbles = (struct bubble *) 0;
1531 STATIC_OVL void
1532 mk_bubble(x, y, n)
1533 int x, y, n;
1536 * These bit masks make visually pleasing bubbles on a normal aspect
1537 * 25x80 terminal, which naturally results in them being mathematically
1538 * anything but symmetric. For this reason they cannot be computed
1539 * in situ, either. The first two elements tell the dimensions of
1540 * the bubble's bounding box.
1542 static uchar bm2[] = { 2, 1, 0x3 },
1543 bm3[] = { 3, 2, 0x7, 0x7 },
1544 bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1545 bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1546 bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1547 bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1548 bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1549 *bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1550 struct bubble *b;
1552 if (x >= bxmax || y >= bymax)
1553 return;
1554 if (n >= SIZE(bmask)) {
1555 impossible("n too large (mk_bubble)");
1556 n = SIZE(bmask) - 1;
1558 if (bmask[n][1] > MAX_BMASK) {
1559 panic("bmask size is larger than MAX_BMASK");
1561 b = (struct bubble *) alloc(sizeof(struct bubble));
1562 if ((x + (int) bmask[n][0] - 1) > bxmax)
1563 x = bxmax - bmask[n][0] + 1;
1564 if ((y + (int) bmask[n][1] - 1) > bymax)
1565 y = bymax - bmask[n][1] + 1;
1566 b->x = x;
1567 b->y = y;
1568 b->dx = 1 - rn2(3);
1569 b->dy = 1 - rn2(3);
1570 /* y dimension is the length of bitmap data - see bmask above */
1571 (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1572 (bmask[n][1] + 2) * sizeof(b->bm[0]));
1573 b->cons = 0;
1574 if (!bbubbles)
1575 bbubbles = b;
1576 if (ebubbles) {
1577 ebubbles->next = b;
1578 b->prev = ebubbles;
1579 } else
1580 b->prev = (struct bubble *) 0;
1581 b->next = (struct bubble *) 0;
1582 ebubbles = b;
1583 mv_bubble(b, 0, 0, TRUE);
1587 * The player, the portal and all other objects and monsters
1588 * float along with their associated bubbles. Bubbles may overlap
1589 * freely, and the contents may get associated with other bubbles in
1590 * the process. Bubbles are "sticky", meaning that if the player is
1591 * in the immediate neighborhood of one, he/she may get sucked inside.
1592 * This property also makes leaving a bubble slightly difficult.
1594 STATIC_OVL void
1595 mv_bubble(b, dx, dy, ini)
1596 struct bubble *b;
1597 int dx, dy;
1598 boolean ini;
1600 int x, y, i, j, colli = 0;
1601 struct container *cons, *ctemp;
1603 /* clouds move slowly */
1604 if (!Is_airlevel(&u.uz) || !rn2(6)) {
1605 /* move bubble */
1606 if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1607 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1608 dx = sgn(dx);
1609 dy = sgn(dy);
1613 * collision with level borders?
1614 * 1 = horizontal border, 2 = vertical, 3 = corner
1616 if (b->x <= bxmin)
1617 colli |= 2;
1618 if (b->y <= bymin)
1619 colli |= 1;
1620 if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1621 colli |= 2;
1622 if ((int) (b->y + b->bm[1] - 1) >= bymax)
1623 colli |= 1;
1625 if (b->x < bxmin) {
1626 pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1627 b->x = bxmin;
1629 if (b->y < bymin) {
1630 pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1631 b->y = bymin;
1633 if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1634 pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1635 bxmax);
1636 b->x = bxmax - b->bm[0] + 1;
1638 if ((int) (b->y + b->bm[1] - 1) > bymax) {
1639 pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1640 bymax);
1641 b->y = bymax - b->bm[1] + 1;
1644 /* bounce if we're trying to move off the border */
1645 if (b->x == bxmin && dx < 0)
1646 dx = -dx;
1647 if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1648 dx = -dx;
1649 if (b->y == bymin && dy < 0)
1650 dy = -dy;
1651 if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1652 dy = -dy;
1654 b->x += dx;
1655 b->y += dy;
1658 /* draw the bubbles */
1659 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1660 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1661 if (b->bm[j + 2] & (1 << i)) {
1662 if (Is_waterlevel(&u.uz)) {
1663 levl[x][y].typ = AIR;
1664 levl[x][y].lit = 1;
1665 unblock_point(x, y);
1666 } else if (Is_airlevel(&u.uz)) {
1667 levl[x][y].typ = CLOUD;
1668 levl[x][y].lit = 1;
1669 block_point(x, y);
1673 if (Is_waterlevel(&u.uz)) {
1674 /* replace contents of bubble */
1675 for (cons = b->cons; cons; cons = ctemp) {
1676 ctemp = cons->next;
1677 cons->x += dx;
1678 cons->y += dy;
1680 switch (cons->what) {
1681 case CONS_OBJ: {
1682 struct obj *olist, *otmp;
1684 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1685 otmp = olist->nexthere;
1686 place_object(olist, cons->x, cons->y);
1688 break;
1691 case CONS_MON: {
1692 struct monst *mon = (struct monst *) cons->list;
1693 (void) mnearto(mon, cons->x, cons->y, TRUE);
1694 break;
1697 case CONS_HERO: {
1698 int ux0 = u.ux, uy0 = u.uy;
1700 /* change u.ux0 and u.uy0? */
1701 u.ux = cons->x;
1702 u.uy = cons->y;
1703 newsym(ux0, uy0); /* clean up old position */
1705 if (MON_AT(cons->x, cons->y)) {
1706 mnexto(m_at(cons->x, cons->y));
1708 break;
1711 case CONS_TRAP: {
1712 struct trap *btrap = (struct trap *) cons->list;
1713 btrap->tx = cons->x;
1714 btrap->ty = cons->y;
1715 break;
1718 default:
1719 impossible("mv_bubble: unknown bubble contents");
1720 break;
1722 free((genericptr_t) cons);
1724 b->cons = 0;
1727 /* boing? */
1728 switch (colli) {
1729 case 1:
1730 b->dy = -b->dy;
1731 break;
1732 case 3:
1733 b->dy = -b->dy; /* fall through */
1734 case 2:
1735 b->dx = -b->dx;
1736 break;
1737 default:
1738 /* sometimes alter direction for fun anyway
1739 (higher probability for stationary bubbles) */
1740 if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
1741 b->dx = 1 - rn2(3);
1742 b->dy = 1 - rn2(3);
1747 /*mkmaze.c*/