1 /* NetHack 3.6	mkmaze.c	$NHDT-Date: 1559422240 2019/06/01 20:50:40 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.74 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 #include "sp_lev.h"
8 #include "lev.h" /* save & restore info */
9 
10 /* from sp_lev.c, for fixup_special() */
11 extern lev_region *lregions;
12 extern int num_lregions;
13 /* for preserving the insect legs when wallifying baalz level */
14 static lev_region bughack = { {COLNO, ROWNO, 0, 0}, {COLNO, ROWNO, 0, 0} };
15 
16 STATIC_DCL int FDECL(iswall, (int, int));
17 STATIC_DCL int FDECL(iswall_or_stone, (int, int));
18 STATIC_DCL boolean FDECL(is_solid, (int, int));
19 STATIC_DCL int FDECL(extend_spine, (int[3][3], int, int, int));
20 STATIC_DCL boolean FDECL(okay, (int, int, int));
21 STATIC_DCL void FDECL(maze0xy, (coord *));
22 STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
23                                             XCHAR_P, XCHAR_P, XCHAR_P,
24                                             XCHAR_P, BOOLEAN_P, d_level *));
25 STATIC_DCL void NDECL(baalz_fixup);
26 STATIC_DCL void NDECL(setup_waterlevel);
27 STATIC_DCL void NDECL(unsetup_waterlevel);
28 STATIC_DCL void FDECL(check_ransacked, (char *));
29 STATIC_DCL void FDECL(migr_booty_item, (int, const char *));
30 STATIC_DCL void FDECL(migrate_orc, (struct monst *, unsigned long));
31 STATIC_DCL void NDECL(stolen_booty);
32 
33 /* adjust a coordinate one step in the specified direction */
34 #define mz_move(X, Y, dir) \
35     do {                                                         \
36         switch (dir) {                                           \
37         case 0:  --(Y);  break;                                  \
38         case 1:  (X)++;  break;                                  \
39         case 2:  (Y)++;  break;                                  \
40         case 3:  --(X);  break;                                  \
41         default: panic("mz_move: bad direction %d", dir);        \
42         }                                                        \
43     } while (0)
44 
45 STATIC_OVL int
iswall(x,y)46 iswall(x, y)
47 int x, y;
48 {
49     int type;
50 
51     if (!isok(x, y))
52         return 0;
53     type = levl[x][y].typ;
54     return (IS_WALL(type) || IS_DOOR(type)
55             || type == SDOOR || type == IRONBARS);
56 }
57 
58 STATIC_OVL int
iswall_or_stone(x,y)59 iswall_or_stone(x, y)
60 int x, y;
61 {
62     /* out of bounds = stone */
63     if (!isok(x, y))
64         return 1;
65 
66     return (levl[x][y].typ == STONE || iswall(x, y));
67 }
68 
69 /* return TRUE if out of bounds, wall or rock */
70 STATIC_OVL boolean
is_solid(x,y)71 is_solid(x, y)
72 int x, y;
73 {
74     return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
75 }
76 
77 /*
78  * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
79  * a wall spine in the (dx,dy) direction.  Return 0 otherwise.
80  *
81  * To extend a wall spine in that direction, first there must be a wall there.
82  * Then, extend a spine unless the current position is surrounded by walls
83  * in the direction given by (dx,dy).  E.g. if 'x' is our location, 'W'
84  * a wall, '.' a room, 'a' anything (we don't care), and our direction is
85  * (0,1) - South or down - then:
86  *
87  *              a a a
88  *              W x W           This would not extend a spine from x down
89  *              W W W           (a corridor of walls is formed).
90  *
91  *              a a a
92  *              W x W           This would extend a spine from x down.
93  *              . W W
94  */
95 STATIC_OVL int
extend_spine(locale,wall_there,dx,dy)96 extend_spine(locale, wall_there, dx, dy)
97 int locale[3][3];
98 int wall_there, dx, dy;
99 {
100     int spine, nx, ny;
101 
102     nx = 1 + dx;
103     ny = 1 + dy;
104 
105     if (wall_there) { /* wall in that direction */
106         if (dx) {
107             if (locale[1][0] && locale[1][2]         /* EW are wall/stone */
108                 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
109                 spine = 0;
110             } else {
111                 spine = 1;
112             }
113         } else { /* dy */
114             if (locale[0][1] && locale[2][1]         /* NS are wall/stone */
115                 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
116                 spine = 0;
117             } else {
118                 spine = 1;
119             }
120         }
121     } else {
122         spine = 0;
123     }
124 
125     return spine;
126 }
127 
128 /* Remove walls totally surrounded by stone */
129 void
wall_cleanup(x1,y1,x2,y2)130 wall_cleanup(x1, y1, x2, y2)
131 int x1, y1, x2, y2;
132 {
133     uchar type;
134     int x, y;
135     struct rm *lev;
136 
137     /* sanity check on incoming variables */
138     if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
139         panic("wall_cleanup: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
140 
141     /* change walls surrounded by rock to rock. */
142     for (x = x1; x <= x2; x++)
143         for (y = y1; y <= y2; y++) {
144             if (within_bounded_area(x, y,
145                                     bughack.inarea.x1, bughack.inarea.y1,
146                                     bughack.inarea.x2, bughack.inarea.y2))
147                 continue;
148             lev = &levl[x][y];
149             type = lev->typ;
150             if (IS_WALL(type) && type != DBWALL) {
151                 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
152                     && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
153                     && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
154                     && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
155                     lev->typ = STONE;
156             }
157         }
158 }
159 
160 /* Correct wall types so they extend and connect to each other */
161 void
fix_wall_spines(x1,y1,x2,y2)162 fix_wall_spines(x1, y1, x2, y2)
163 int x1, y1, x2, y2;
164 {
165     uchar type;
166     int x, y;
167     struct rm *lev;
168     int FDECL((*loc_f), (int, int));
169     int bits;
170     int locale[3][3]; /* rock or wall status surrounding positions */
171 
172     /*
173      * Value 0 represents a free-standing wall.  It could be anything,
174      * so even though this table says VWALL, we actually leave whatever
175      * typ was there alone.
176      */
177     static xchar spine_array[16] = { VWALL, HWALL,    HWALL,    HWALL,
178                                      VWALL, TRCORNER, TLCORNER, TDWALL,
179                                      VWALL, BRCORNER, BLCORNER, TUWALL,
180                                      VWALL, TLWALL,   TRWALL,   CROSSWALL };
181 
182     /* sanity check on incoming variables */
183     if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
184         panic("wall_extends: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
185 
186     /* set the correct wall type. */
187     for (x = x1; x <= x2; x++)
188         for (y = y1; y <= y2; y++) {
189             lev = &levl[x][y];
190             type = lev->typ;
191             if (!(IS_WALL(type) && type != DBWALL))
192                 continue;
193 
194             /* set the locations TRUE if rock or wall or out of bounds */
195             loc_f = within_bounded_area(x, y, /* for baalz insect */
196                                         bughack.inarea.x1, bughack.inarea.y1,
197                                         bughack.inarea.x2, bughack.inarea.y2)
198                        ? iswall
199                        : iswall_or_stone;
200             locale[0][0] = (*loc_f)(x - 1, y - 1);
201             locale[1][0] = (*loc_f)(x, y - 1);
202             locale[2][0] = (*loc_f)(x + 1, y - 1);
203 
204             locale[0][1] = (*loc_f)(x - 1, y);
205             locale[2][1] = (*loc_f)(x + 1, y);
206 
207             locale[0][2] = (*loc_f)(x - 1, y + 1);
208             locale[1][2] = (*loc_f)(x, y + 1);
209             locale[2][2] = (*loc_f)(x + 1, y + 1);
210 
211             /* determine if wall should extend to each direction NSEW */
212             bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
213                    | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
214                    | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
215                    | extend_spine(locale, iswall(x - 1, y), -1, 0);
216 
217             /* don't change typ if wall is free-standing */
218             if (bits)
219                 lev->typ = spine_array[bits];
220         }
221 }
222 
223 void
wallification(x1,y1,x2,y2)224 wallification(x1, y1, x2, y2)
225 int x1, y1, x2, y2;
226 {
227     wall_cleanup(x1, y1, x2, y2);
228     fix_wall_spines(x1, y1, x2, y2);
229 }
230 
231 STATIC_OVL boolean
okay(x,y,dir)232 okay(x, y, dir)
233 int x, y;
234 int dir;
235 {
236     mz_move(x, y, dir);
237     mz_move(x, y, dir);
238     if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max
239         || levl[x][y].typ != STONE)
240         return FALSE;
241     return TRUE;
242 }
243 
244 /* find random starting point for maze generation */
245 STATIC_OVL void
maze0xy(cc)246 maze0xy(cc)
247 coord *cc;
248 {
249     cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
250     cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
251     return;
252 }
253 
254 /*
255  * Bad if:
256  *      pos is occupied OR
257  *      pos is inside restricted region (lx,ly,hx,hy) OR
258  *      NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
259  */
260 boolean
bad_location(x,y,lx,ly,hx,hy)261 bad_location(x, y, lx, ly, hx, hy)
262 xchar x, y;
263 xchar lx, ly, hx, hy;
264 {
265     return (boolean) (occupied(x, y)
266                       || within_bounded_area(x, y, lx, ly, hx, hy)
267                       || !((levl[x][y].typ == CORR && level.flags.is_maze_lev)
268                            || levl[x][y].typ == ROOM
269                            || levl[x][y].typ == AIR));
270 }
271 
272 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
273    and place something (based on rtype) in that region */
274 void
place_lregion(lx,ly,hx,hy,nlx,nly,nhx,nhy,rtype,lev)275 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
276 xchar lx, ly, hx, hy;
277 xchar nlx, nly, nhx, nhy;
278 xchar rtype;
279 d_level *lev;
280 {
281     int trycnt;
282     boolean oneshot;
283     xchar x, y;
284 
285     if (!lx) { /* default to whole level */
286         /*
287          * if there are rooms and this a branch, let place_branch choose
288          * the branch location (to avoid putting branches in corridors).
289          */
290         if (rtype == LR_BRANCH && nroom) {
291             place_branch(Is_branchlev(&u.uz), 0, 0);
292             return;
293         }
294 
295         lx = 1; /* column 0 is not used */
296         hx = COLNO - 1;
297         ly = 0; /* 3.6.0 and earlier erroneously had 1 here */
298         hy = ROWNO - 1;
299     }
300 
301     /* first a probabilistic approach */
302 
303     oneshot = (lx == hx && ly == hy);
304     for (trycnt = 0; trycnt < 200; trycnt++) {
305         x = rn1((hx - lx) + 1, lx);
306         y = rn1((hy - ly) + 1, ly);
307         if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
308             return;
309     }
310 
311     /* then a deterministic one */
312 
313     for (x = lx; x <= hx; x++)
314         for (y = ly; y <= hy; y++)
315             if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, TRUE, lev))
316                 return;
317 
318     impossible("Couldn't place lregion type %d!", rtype);
319 }
320 
321 STATIC_OVL boolean
put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)322 put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev)
323 xchar x, y;
324 xchar nlx, nly, nhx, nhy;
325 xchar rtype;
326 boolean oneshot;
327 d_level *lev;
328 {
329     struct monst *mtmp;
330 
331     if (bad_location(x, y, nlx, nly, nhx, nhy)) {
332         if (!oneshot) {
333             return FALSE; /* caller should try again */
334         } else {
335             /* Must make do with the only location possible;
336                avoid failure due to a misplaced trap.
337                It might still fail if there's a dungeon feature here. */
338             struct trap *t = t_at(x, y);
339 
340             if (t && t->ttyp != MAGIC_PORTAL && t->ttyp != VIBRATING_SQUARE)
341                 deltrap(t);
342             if (bad_location(x, y, nlx, nly, nhx, nhy))
343                 return FALSE;
344         }
345     }
346     switch (rtype) {
347     case LR_TELE:
348     case LR_UPTELE:
349     case LR_DOWNTELE:
350         /* "something" means the player in this case */
351         if ((mtmp = m_at(x, y)) != 0) {
352             /* move the monster if no choice, or just try again */
353             if (oneshot) {
354                 if (!rloc(mtmp, TRUE))
355                     m_into_limbo(mtmp);
356             } else
357                 return FALSE;
358         }
359         u_on_newpos(x, y);
360         break;
361     case LR_PORTAL:
362         mkportal(x, y, lev->dnum, lev->dlevel);
363         break;
364     case LR_DOWNSTAIR:
365     case LR_UPSTAIR:
366         mkstairs(x, y, (char) rtype, (struct mkroom *) 0);
367         break;
368     case LR_BRANCH:
369         place_branch(Is_branchlev(&u.uz), x, y);
370         break;
371     }
372     return TRUE;
373 }
374 
375 /* fix up Baalzebub's lair, which depicts a level-sized beetle;
376    its legs are walls within solid rock--regular wallification
377    classifies them as superfluous and gets rid of them */
378 STATIC_OVL void
baalz_fixup()379 baalz_fixup()
380 {
381     struct monst *mtmp;
382     int x, y, lastx, lasty;
383 
384     /*
385      * baalz level's nondiggable region surrounds the "insect" and rooms.
386      * The outermost perimeter of that region is subject to wall cleanup
387      * (hence 'x + 1' and 'y + 1' for starting don't-clean column and row,
388      * 'lastx - 1' and 'lasty - 1' for ending don't-clean column and row)
389      * and the interior is protected against that (in wall_cleanup()).
390      *
391      * Assumes level.flags.corrmaze is True, otherwise the bug legs will
392      * have already been "cleaned" away by general wallification.
393      */
394 
395     /* find low and high x for to-be-wallified portion of level */
396     y = ROWNO / 2;
397     for (lastx = x = 0; x < COLNO; ++x)
398         if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
399             if (!lastx)
400                 bughack.inarea.x1 = x + 1;
401             lastx = x;
402         }
403     bughack.inarea.x2 = ((lastx > bughack.inarea.x1) ? lastx : x) - 1;
404     /* find low and high y for to-be-wallified portion of level */
405     x = bughack.inarea.x1;
406     for (lasty = y = 0; y < ROWNO; ++y)
407         if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
408             if (!lasty)
409                 bughack.inarea.y1 = y + 1;
410             lasty = y;
411         }
412     bughack.inarea.y2 = ((lasty > bughack.inarea.y1) ? lasty : y) - 1;
413     /* two pools mark where special post-wallify fix-ups are needed */
414     for (x = bughack.inarea.x1; x <= bughack.inarea.x2; ++x)
415         for (y = bughack.inarea.y1; y <= bughack.inarea.y2; ++y)
416             if (levl[x][y].typ == POOL) {
417                 levl[x][y].typ = HWALL;
418                 if (bughack.delarea.x1 == COLNO)
419                     bughack.delarea.x1 = x, bughack.delarea.y1 = y;
420                 else
421                     bughack.delarea.x2 = x, bughack.delarea.y2 = y;
422             } else if (levl[x][y].typ == IRONBARS) {
423                 /* novelty effect; allowing digging in front of 'eyes' */
424                 levl[x - 1][y].wall_info &= ~W_NONDIGGABLE;
425                 if (isok(x - 2, y))
426                     levl[x - 2][y].wall_info &= ~W_NONDIGGABLE;
427             }
428 
429     wallification(max(bughack.inarea.x1 - 2, 1),
430                   max(bughack.inarea.y1 - 2, 0),
431                   min(bughack.inarea.x2 + 2, COLNO - 1),
432                   min(bughack.inarea.y2 + 2, ROWNO - 1));
433 
434     /* bughack hack for rear-most legs on baalz level; first joint on
435        both top and bottom gets a bogus extra connection to room area,
436        producing unwanted rectangles; change back to separated legs */
437     x = bughack.delarea.x1, y = bughack.delarea.y1;
438     if (isok(x, y) && levl[x][y].typ == TLWALL
439         && isok(x, y + 1) && levl[x][y + 1].typ == TUWALL) {
440         levl[x][y].typ = BRCORNER;
441         levl[x][y + 1].typ = HWALL;
442         if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
443             (void) rloc(mtmp, FALSE);
444     }
445     x = bughack.delarea.x2, y = bughack.delarea.y2;
446     if (isok(x, y) && levl[x][y].typ == TLWALL
447         && isok(x, y - 1) && levl[x][y - 1].typ == TDWALL) {
448         levl[x][y].typ = TRCORNER;
449         levl[x][y - 1].typ = HWALL;
450         if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
451             (void) rloc(mtmp, FALSE);
452     }
453 
454     /* reset bughack region; set low end to <COLNO,ROWNO> so that
455        within_bounded_region() in fix_wall_spines() will fail
456        most quickly--on its first test--when loading other levels */
457     bughack.inarea.x1 = bughack.delarea.x1 = COLNO;
458     bughack.inarea.y1 = bughack.delarea.y1 = ROWNO;
459     bughack.inarea.x2 = bughack.delarea.x2 = 0;
460     bughack.inarea.y2 = bughack.delarea.y2 = 0;
461 }
462 
463 /* this is special stuff that the level compiler cannot (yet) handle */
464 void
fixup_special()465 fixup_special()
466 {
467     lev_region *r = lregions;
468     struct d_level lev;
469     int x, y;
470     struct mkroom *croom;
471     boolean added_branch = FALSE;
472 
473     if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
474         level.flags.hero_memory = 0;
475         /* water level is an odd beast - it has to be set up
476            before calling place_lregions etc. */
477         setup_waterlevel();
478     }
479     for (x = 0; x < num_lregions; x++, r++) {
480         switch (r->rtype) {
481         case LR_BRANCH:
482             added_branch = TRUE;
483             goto place_it;
484 
485         case LR_PORTAL:
486             if (*r->rname.str >= '0' && *r->rname.str <= '9') {
487                 /* "chutes and ladders" */
488                 lev = u.uz;
489                 lev.dlevel = atoi(r->rname.str);
490             } else {
491                 s_level *sp = find_level(r->rname.str);
492 
493                 lev = sp->dlevel;
494             }
495             /*FALLTHRU*/
496 
497         case LR_UPSTAIR:
498         case LR_DOWNSTAIR:
499  place_it:
500             place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
501                           r->inarea.y2, r->delarea.x1, r->delarea.y1,
502                           r->delarea.x2, r->delarea.y2, r->rtype, &lev);
503             break;
504 
505         case LR_TELE:
506         case LR_UPTELE:
507         case LR_DOWNTELE:
508             /* save the region outlines for goto_level() */
509             if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
510                 updest.lx = r->inarea.x1;
511                 updest.ly = r->inarea.y1;
512                 updest.hx = r->inarea.x2;
513                 updest.hy = r->inarea.y2;
514                 updest.nlx = r->delarea.x1;
515                 updest.nly = r->delarea.y1;
516                 updest.nhx = r->delarea.x2;
517                 updest.nhy = r->delarea.y2;
518             }
519             if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
520                 dndest.lx = r->inarea.x1;
521                 dndest.ly = r->inarea.y1;
522                 dndest.hx = r->inarea.x2;
523                 dndest.hy = r->inarea.y2;
524                 dndest.nlx = r->delarea.x1;
525                 dndest.nly = r->delarea.y1;
526                 dndest.nhx = r->delarea.x2;
527                 dndest.nhy = r->delarea.y2;
528             }
529             /* place_lregion gets called from goto_level() */
530             break;
531         }
532 
533         if (r->rname.str)
534             free((genericptr_t) r->rname.str), r->rname.str = 0;
535     }
536 
537     /* place dungeon branch if not placed above */
538     if (!added_branch && Is_branchlev(&u.uz)) {
539         place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
540     }
541 
542     /* Still need to add some stuff to level file */
543     if (Is_medusa_level(&u.uz)) {
544         struct obj *otmp;
545         int tryct;
546 
547         croom = &rooms[0]; /* only one room on the medusa level */
548         for (tryct = rnd(4); tryct; tryct--) {
549             x = somex(croom);
550             y = somey(croom);
551             if (goodpos(x, y, (struct monst *) 0, 0)) {
552                 otmp = mk_tt_object(STATUE, x, y);
553                 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm])
554                                 || pm_resistance(&mons[otmp->corpsenm],
555                                                  MR_STONE))) {
556                     /* set_corpsenm() handles weight too */
557                     set_corpsenm(otmp, rndmonnum());
558                 }
559             }
560         }
561 
562         if (rn2(2))
563             otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
564         else /* Medusa statues don't contain books */
565             otmp =
566                 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
567                            somex(croom), somey(croom), CORPSTAT_NONE);
568         if (otmp) {
569             while (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
570                    || poly_when_stoned(&mons[otmp->corpsenm])) {
571                 /* set_corpsenm() handles weight too */
572                 set_corpsenm(otmp, rndmonnum());
573             }
574         }
575     } else if (Is_wiz1_level(&u.uz)) {
576         croom = search_special(MORGUE);
577 
578         create_secret_door(croom, W_SOUTH | W_EAST | W_WEST);
579     } else if (Is_knox(&u.uz)) {
580         /* using an unfilled morgue for rm id */
581         croom = search_special(MORGUE);
582         /* avoid inappropriate morgue-related messages */
583         level.flags.graveyard = level.flags.has_morgue = 0;
584         croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
585         /* stock the main vault */
586         for (x = croom->lx; x <= croom->hx; x++)
587             for (y = croom->ly; y <= croom->hy; y++) {
588                 (void) mkgold((long) rn1(300, 600), x, y);
589                 if (!rn2(3) && !is_pool(x, y))
590                     (void) maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
591             }
592     } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
593         /* less chance for undead corpses (lured from lower morgues) */
594         level.flags.graveyard = 1;
595     } else if (Is_stronghold(&u.uz)) {
596         level.flags.graveyard = 1;
597     } else if (Is_sanctum(&u.uz)) {
598         croom = search_special(TEMPLE);
599 
600         create_secret_door(croom, W_ANY);
601     } else if (on_level(&u.uz, &orcus_level)) {
602         struct monst *mtmp, *mtmp2;
603 
604         /* it's a ghost town, get rid of shopkeepers */
605         for (mtmp = fmon; mtmp; mtmp = mtmp2) {
606             mtmp2 = mtmp->nmon;
607             if (mtmp->isshk)
608                 mongone(mtmp);
609         }
610     } else if (on_level(&u.uz, &baalzebub_level)) {
611         /* custom wallify the "beetle" potion of the level */
612         baalz_fixup();
613     } else if (u.uz.dnum == mines_dnum && ransacked) {
614        stolen_booty();
615     }
616 
617     if (lregions)
618         free((genericptr_t) lregions), lregions = 0;
619     num_lregions = 0;
620 }
621 
622 STATIC_OVL void
check_ransacked(s)623 check_ransacked(s)
624 char *s;
625 {
626     /* this kludge only works as long as orctown is minetn-1 */
627     ransacked = (u.uz.dnum == mines_dnum && !strcmp(s, "minetn-1"));
628 }
629 
630 #define ORC_LEADER 1
631 static const char *orcfruit[] = { "paddle cactus", "dwarven root" };
632 
633 STATIC_OVL void
migrate_orc(mtmp,mflags)634 migrate_orc(mtmp, mflags)
635 struct monst *mtmp;
636 unsigned long mflags;
637 {
638     int nlev, max_depth, cur_depth;
639     d_level dest;
640 
641     cur_depth = (int) depth(&u.uz);
642     max_depth = dunlevs_in_dungeon(&u.uz)
643                 + (dungeons[u.uz.dnum].depth_start - 1);
644     if (mflags == ORC_LEADER) {
645         /* Note that the orc leader will take possession of any
646          * remaining stuff not already delivered to other
647          * orcs between here and the bottom of the mines.
648          */
649         nlev = max_depth;
650         /* once in a blue moon, he won't be at the very bottom */
651         if (!rn2(40))
652             nlev--;
653         mtmp->mspare1 |= MIGR_LEFTOVERS;
654     } else {
655         nlev = rn2((max_depth - cur_depth) + 1) + cur_depth;
656         if (nlev == cur_depth)
657             nlev++;
658         if (nlev > max_depth)
659             nlev = max_depth;
660         mtmp->mspare1 = (mtmp->mspare1 & ~MIGR_LEFTOVERS);
661     }
662     get_level(&dest, nlev);
663     migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0);
664 }
665 
666 void
shiny_orc_stuff(mtmp)667 shiny_orc_stuff(mtmp)
668 struct monst *mtmp;
669 {
670     int gemprob, goldprob, otyp;
671     struct obj *otmp;
672     boolean is_captain = (mtmp->data == &mons[PM_ORC_CAPTAIN]);
673 
674     /* probabilities */
675     goldprob = is_captain ? 600 : 300;
676     gemprob = goldprob / 4;
677     if (rn2(1000) < goldprob) {
678         if ((otmp = mksobj(GOLD_PIECE, TRUE, FALSE)) != 0) {
679             otmp->quan = 1L + rnd(goldprob);
680             otmp->owt = weight(otmp);
681             add_to_minv(mtmp, otmp);
682         }
683     }
684     if (rn2(1000) < gemprob) {
685         if ((otmp = mkobj(GEM_CLASS, FALSE)) != 0) {
686             if (otmp->otyp == ROCK)
687                 dealloc_obj(otmp);
688             else
689                 add_to_minv(mtmp, otmp);
690         }
691     }
692     if (is_captain || !rn2(8)) {
693         otyp = shiny_obj(RING_CLASS);
694         if (otyp != STRANGE_OBJECT && (otmp = mksobj(otyp, TRUE, FALSE)) != 0)
695             add_to_minv(mtmp, otmp);
696     }
697 }
698 STATIC_OVL void
migr_booty_item(otyp,gang)699 migr_booty_item(otyp, gang)
700 int otyp;
701 const char *gang;
702 {
703     struct obj *otmp;
704 
705     otmp = mksobj_migr_to_species(otyp, (unsigned long) M2_ORC, TRUE, FALSE);
706     if (otmp && gang) {
707         new_oname(otmp, strlen(gang) + 1); /* removes old name if present */
708         Strcpy(ONAME(otmp), gang);
709         if (otyp >= TRIPE_RATION && otyp <= TIN) {
710             if (otyp == SLIME_MOLD)
711                 otmp->spe = fruitadd((char *) orcfruit[rn2(SIZE(orcfruit))],
712                                      (struct fruit *) 0);
713             otmp->quan += (long) rn2(3);
714             otmp->owt = weight(otmp);
715         }
716     }
717 }
718 
719 STATIC_OVL void
stolen_booty(VOID_ARGS)720 stolen_booty(VOID_ARGS)
721 {
722     char *gang, gang_name[BUFSZ];
723     struct monst *mtmp;
724     int cnt, i, otyp;
725 
726     /*
727      * --------------------------------------------------------
728      * Mythos:
729      *
730      *      A tragic accident has occurred in Frontier Town...
731      *      It has been overrun by orcs.
732      *
733      *      The booty that the orcs took from the town is now
734      *      in the possession of the orcs that did this and
735      *      have long since fled the level.
736      * --------------------------------------------------------
737      */
738 
739     gang = rndorcname(gang_name);
740     /* create the stuff that the gang took */
741     cnt = rnd(4);
742     for (i = 0; i < cnt; ++i)
743         migr_booty_item(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, gang);
744     cnt = rnd(3);
745     for (i = 0; i < cnt; ++i)
746         migr_booty_item(SKELETON_KEY, gang);
747     otyp = rn2((GAUNTLETS_OF_DEXTERITY - LEATHER_GLOVES) + 1) + LEATHER_GLOVES;
748     migr_booty_item(otyp, gang);
749     cnt = rnd(10);
750     for (i = 0; i < cnt; ++i) {
751         /* Food items - but no lembas! (or some other weird things) */
752         otyp = rn2((TIN - TRIPE_RATION) + 1) + TRIPE_RATION;
753         if (otyp != LEMBAS_WAFER && otyp != GLOB_OF_GRAY_OOZE
754             && otyp != GLOB_OF_BROWN_PUDDING && otyp != GLOB_OF_GREEN_SLIME
755             && otyp != GLOB_OF_BLACK_PUDDING && otyp != MEAT_STICK
756             && otyp != MEATBALL && otyp != MEAT_STICK && otyp != MEAT_RING
757             && otyp != HUGE_CHUNK_OF_MEAT && otyp != CORPSE)
758             migr_booty_item(otyp, gang);
759     }
760     migr_booty_item(rn2(2) ? LONG_SWORD : SILVER_SABER, gang);
761     /* create the leader of the orc gang */
762     mtmp = makemon(&mons[PM_ORC_CAPTAIN], 0, 0, MM_NONAME);
763     if (mtmp) {
764         mtmp = christen_monst(mtmp, upstart(gang));
765         mtmp->mpeaceful = 0;
766         shiny_orc_stuff(mtmp);
767         migrate_orc(mtmp, ORC_LEADER);
768     }
769     /* Make most of the orcs on the level be part of the invading gang */
770     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
771         if (DEADMONSTER(mtmp))
772             continue;
773 
774         if (is_orc(mtmp->data) && !has_mname(mtmp) && rn2(10)) {
775             /*
776              * We'll consider the orc captain from the level
777              * .des file to be the captain of a rival orc horde
778              * who is there to see what has transpired, and to
779              * contemplate future action.
780              *
781              * Don't christen the orc captain as a subordinate
782              * member of the main orc horde.
783              */
784             if (mtmp->data != &mons[PM_ORC_CAPTAIN])
785                 mtmp = christen_orc(mtmp, upstart(gang), "");
786         }
787     }
788     /* Lastly, ensure there's several more orcs from the gang along the way.
789      * The mechanics are such that they aren't actually identified as
790      * members of the invading gang until they get their spoils assigned
791      * to the inventory; handled during that assignment.
792      */
793     cnt = rn2(10) + 5;
794     for (i = 0; i < cnt; ++i) {
795         int mtyp;
796 
797         mtyp = rn2((PM_ORC_SHAMAN - PM_ORC) + 1) + PM_ORC;
798         mtmp = makemon(&mons[mtyp], 0, 0, MM_NONAME);
799         if (mtmp) {
800             shiny_orc_stuff(mtmp);
801             migrate_orc(mtmp, 0UL);
802         }
803     }
804     ransacked = 0;
805 }
806 
807 #undef ORC_LEADER
808 
809 boolean
maze_inbounds(x,y)810 maze_inbounds(x, y)
811 int x, y;
812 {
813     return (x >= 2 && y >= 2
814             && x < x_maze_max && y < y_maze_max && isok(x, y));
815 }
816 
817 void
maze_remove_deadends(typ)818 maze_remove_deadends(typ)
819 xchar typ;
820 {
821     char dirok[4];
822     int x, y, dir, idx, idx2, dx, dy, dx2, dy2;
823 
824     dirok[0] = 0; /* lint suppression */
825     for (x = 2; x < x_maze_max; x++)
826         for (y = 2; y < y_maze_max; y++)
827             if (ACCESSIBLE(levl[x][y].typ) && (x % 2) && (y % 2)) {
828                 idx = idx2 = 0;
829                 for (dir = 0; dir < 4; dir++) {
830                     /* note: mz_move() is a macro which modifies
831                        one of its first two parameters */
832                     dx = dx2 = x;
833                     dy = dy2 = y;
834                     mz_move(dx, dy, dir);
835                     if (!maze_inbounds(dx, dy)) {
836                         idx2++;
837                         continue;
838                     }
839                     mz_move(dx2, dy2, dir);
840                     mz_move(dx2, dy2, dir);
841                     if (!maze_inbounds(dx2, dy2)) {
842                         idx2++;
843                         continue;
844                     }
845                     if (!ACCESSIBLE(levl[dx][dy].typ)
846                         && ACCESSIBLE(levl[dx2][dy2].typ)) {
847                         dirok[idx++] = dir;
848                         idx2++;
849                     }
850                 }
851                 if (idx2 >= 3 && idx > 0) {
852                     dx = x;
853                     dy = y;
854                     dir = dirok[rn2(idx)];
855                     mz_move(dx, dy, dir);
856                     levl[dx][dy].typ = typ;
857                 }
858             }
859 }
860 
861 /* Create a maze with specified corridor width and wall thickness
862  * TODO: rewrite walkfrom so it works on temp space, not levl
863  */
864 void
create_maze(corrwid,wallthick)865 create_maze(corrwid, wallthick)
866 int corrwid;
867 int wallthick;
868 {
869     int x,y;
870     coord mm;
871     int tmp_xmax = x_maze_max;
872     int tmp_ymax = y_maze_max;
873     int rdx = 0;
874     int rdy = 0;
875     int scale;
876 
877     if (wallthick < 1)
878         wallthick = 1;
879     else if (wallthick > 5)
880         wallthick = 5;
881 
882     if (corrwid < 1)
883         corrwid = 1;
884     else if (corrwid > 5)
885         corrwid = 5;
886 
887     scale = corrwid + wallthick;
888     rdx = (x_maze_max / scale);
889     rdy = (y_maze_max / scale);
890 
891     if (level.flags.corrmaze)
892         for (x = 2; x < (rdx * 2); x++)
893             for (y = 2; y < (rdy * 2); y++)
894                 levl[x][y].typ = STONE;
895     else
896         for (x = 2; x <= (rdx * 2); x++)
897             for (y = 2; y <= (rdy * 2); y++)
898                 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
899 
900     /* set upper bounds for maze0xy and walkfrom */
901     x_maze_max = (rdx * 2);
902     y_maze_max = (rdy * 2);
903 
904     /* create maze */
905     maze0xy(&mm);
906     walkfrom((int) mm.x, (int) mm.y, 0);
907 
908     if (!rn2(5))
909         maze_remove_deadends((level.flags.corrmaze) ? CORR : ROOM);
910 
911     /* restore bounds */
912     x_maze_max = tmp_xmax;
913     y_maze_max = tmp_ymax;
914 
915     /* scale maze up if needed */
916     if (scale > 2) {
917         char tmpmap[COLNO][ROWNO];
918         int rx = 1, ry = 1;
919 
920         /* back up the existing smaller maze */
921         for (x = 1; x < x_maze_max; x++)
922             for (y = 1; y < y_maze_max; y++) {
923                 tmpmap[x][y] = levl[x][y].typ;
924             }
925 
926         /* do the scaling */
927         rx = x = 2;
928         while (rx < x_maze_max) {
929             int mx = (x % 2) ? corrwid
930                              : ((x == 2 || x == (rdx * 2)) ? 1
931                                                            : wallthick);
932             ry = y = 2;
933             while (ry < y_maze_max) {
934                 int dx = 0, dy = 0;
935                 int my = (y % 2) ? corrwid
936                                  : ((y == 2 || y == (rdy * 2)) ? 1
937                                                                : wallthick);
938                 for (dx = 0; dx < mx; dx++)
939                     for (dy = 0; dy < my; dy++) {
940                         if (rx+dx >= x_maze_max
941                             || ry+dy >= y_maze_max)
942                             break;
943                         levl[rx + dx][ry + dy].typ = tmpmap[x][y];
944                     }
945                 ry += my;
946                 y++;
947             }
948             rx += mx;
949             x++;
950         }
951 
952     }
953 }
954 
955 
956 void
makemaz(s)957 makemaz(s)
958 const char *s;
959 {
960     int x, y;
961     char protofile[20];
962     s_level *sp = Is_special(&u.uz);
963     coord mm;
964 
965     if (*s) {
966         if (sp && sp->rndlevs)
967             Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
968         else
969             Strcpy(protofile, s);
970     } else if (*(dungeons[u.uz.dnum].proto)) {
971         if (dunlevs_in_dungeon(&u.uz) > 1) {
972             if (sp && sp->rndlevs)
973                 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
974                         dunlev(&u.uz), rnd((int) sp->rndlevs));
975             else
976                 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
977                         dunlev(&u.uz));
978         } else if (sp && sp->rndlevs) {
979             Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
980                     rnd((int) sp->rndlevs));
981         } else
982             Strcpy(protofile, dungeons[u.uz.dnum].proto);
983 
984     } else
985         Strcpy(protofile, "");
986 
987     /* SPLEVTYPE format is "level-choice,level-choice"... */
988     if (wizard && *protofile && sp && sp->rndlevs) {
989         char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
990         if (ep) {
991             /* rindex always succeeds due to code in prior block */
992             int len = (int) ((rindex(protofile, '-') - protofile) + 1);
993 
994             while (ep && *ep) {
995                 if (!strncmp(ep, protofile, len)) {
996                     int pick = atoi(ep + len);
997                     /* use choice only if valid */
998                     if (pick > 0 && pick <= (int) sp->rndlevs)
999                         Sprintf(protofile + len, "%d", pick);
1000                     break;
1001                 } else {
1002                     ep = index(ep, ',');
1003                     if (ep)
1004                         ++ep;
1005                 }
1006             }
1007         }
1008     }
1009 
1010     if (*protofile) {
1011         check_ransacked(protofile);
1012         Strcat(protofile, LEV_EXT);
1013         if (load_special(protofile)) {
1014             /* some levels can end up with monsters
1015                on dead mon list, including light source monsters */
1016             dmonsfree();
1017             return; /* no mazification right now */
1018         }
1019         impossible("Couldn't load \"%s\" - making a maze.", protofile);
1020     }
1021 
1022     level.flags.is_maze_lev = TRUE;
1023     level.flags.corrmaze = !rn2(3);
1024 
1025     if (!Invocation_lev(&u.uz) && rn2(2)) {
1026         int corrscale = rnd(4);
1027         create_maze(corrscale,rnd(4)-corrscale);
1028     } else {
1029         create_maze(1,1);
1030     }
1031 
1032     if (!level.flags.corrmaze)
1033         wallification(2, 2, x_maze_max, y_maze_max);
1034 
1035     mazexy(&mm);
1036     mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
1037     if (!Invocation_lev(&u.uz)) {
1038         mazexy(&mm);
1039         mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
1040     } else { /* choose "vibrating square" location */
1041 #define x_maze_min 2
1042 #define y_maze_min 2
1043 /*
1044  * Pick a position where the stairs down to Moloch's Sanctum
1045  * level will ultimately be created.  At that time, an area
1046  * will be altered:  walls removed, moat and traps generated,
1047  * boulders destroyed.  The position picked here must ensure
1048  * that that invocation area won't extend off the map.
1049  *
1050  * We actually allow up to 2 squares around the usual edge of
1051  * the area to get truncated; see mkinvokearea(mklev.c).
1052  */
1053 #define INVPOS_X_MARGIN (6 - 2)
1054 #define INVPOS_Y_MARGIN (5 - 2)
1055 #define INVPOS_DISTANCE 11
1056         int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
1057             y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
1058 
1059         if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
1060             || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
1061             debugpline2("inv_pos: maze is too small! (%d x %d)",
1062                         x_maze_max, y_maze_max);
1063         }
1064         inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
1065         do {
1066             x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
1067             y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
1068             /* we don't want it to be too near the stairs, nor
1069                to be on a spot that's already in use (wall|trap) */
1070         } while (x == xupstair || y == yupstair /*(direct line)*/
1071                  || abs(x - xupstair) == abs(y - yupstair)
1072                  || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
1073                  || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
1074         inv_pos.x = x;
1075         inv_pos.y = y;
1076         maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
1077 #undef INVPOS_X_MARGIN
1078 #undef INVPOS_Y_MARGIN
1079 #undef INVPOS_DISTANCE
1080 #undef x_maze_min
1081 #undef y_maze_min
1082     }
1083 
1084     /* place branch stair or portal */
1085     place_branch(Is_branchlev(&u.uz), 0, 0);
1086 
1087     for (x = rn1(8, 11); x; x--) {
1088         mazexy(&mm);
1089         (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
1090     }
1091     for (x = rn1(10, 2); x; x--) {
1092         mazexy(&mm);
1093         (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
1094     }
1095     for (x = rn2(3); x; x--) {
1096         mazexy(&mm);
1097         (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
1098     }
1099     for (x = rn1(5, 7); x; x--) {
1100         mazexy(&mm);
1101         (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
1102     }
1103     for (x = rn1(6, 7); x; x--) {
1104         mazexy(&mm);
1105         (void) mkgold(0L, mm.x, mm.y);
1106     }
1107     for (x = rn1(6, 7); x; x--)
1108         mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
1109 }
1110 
1111 #ifdef MICRO
1112 /* Make the mazewalk iterative by faking a stack.  This is needed to
1113  * ensure the mazewalk is successful in the limited stack space of
1114  * the program.  This iterative version uses the minimum amount of stack
1115  * that is totally safe.
1116  */
1117 void
walkfrom(x,y,typ)1118 walkfrom(x, y, typ)
1119 int x, y;
1120 schar typ;
1121 {
1122 #define CELLS (ROWNO * COLNO) / 4            /* a maze cell is 4 squares */
1123     char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
1124     int q, a, dir, pos;
1125     int dirs[4];
1126 
1127     if (!typ) {
1128         if (level.flags.corrmaze)
1129             typ = CORR;
1130         else
1131             typ = ROOM;
1132     }
1133 
1134     pos = 1;
1135     mazex[pos] = (char) x;
1136     mazey[pos] = (char) y;
1137     while (pos) {
1138         x = (int) mazex[pos];
1139         y = (int) mazey[pos];
1140         if (!IS_DOOR(levl[x][y].typ)) {
1141             /* might still be on edge of MAP, so don't overwrite */
1142             levl[x][y].typ = typ;
1143             levl[x][y].flags = 0;
1144         }
1145         q = 0;
1146         for (a = 0; a < 4; a++)
1147             if (okay(x, y, a))
1148                 dirs[q++] = a;
1149         if (!q)
1150             pos--;
1151         else {
1152             dir = dirs[rn2(q)];
1153             mz_move(x, y, dir);
1154             levl[x][y].typ = typ;
1155             mz_move(x, y, dir);
1156             pos++;
1157             if (pos > CELLS)
1158                 panic("Overflow in walkfrom");
1159             mazex[pos] = (char) x;
1160             mazey[pos] = (char) y;
1161         }
1162     }
1163 }
1164 #else /* !MICRO */
1165 
1166 void
walkfrom(x,y,typ)1167 walkfrom(x, y, typ)
1168 int x, y;
1169 schar typ;
1170 {
1171     int q, a, dir;
1172     int dirs[4];
1173 
1174     if (!typ) {
1175         if (level.flags.corrmaze)
1176             typ = CORR;
1177         else
1178             typ = ROOM;
1179     }
1180 
1181     if (!IS_DOOR(levl[x][y].typ)) {
1182         /* might still be on edge of MAP, so don't overwrite */
1183         levl[x][y].typ = typ;
1184         levl[x][y].flags = 0;
1185     }
1186 
1187     while (1) {
1188         q = 0;
1189         for (a = 0; a < 4; a++)
1190             if (okay(x, y, a))
1191                 dirs[q++] = a;
1192         if (!q)
1193             return;
1194         dir = dirs[rn2(q)];
1195         mz_move(x, y, dir);
1196         levl[x][y].typ = typ;
1197         mz_move(x, y, dir);
1198         walkfrom(x, y, typ);
1199     }
1200 }
1201 #endif /* ?MICRO */
1202 
1203 /* find random point in generated corridors,
1204    so we don't create items in moats, bunkers, or walls */
1205 void
mazexy(cc)1206 mazexy(cc)
1207 coord *cc;
1208 {
1209     int cpt = 0;
1210 
1211     do {
1212         cc->x = 1 + rn2(x_maze_max);
1213         cc->y = 1 + rn2(y_maze_max);
1214         cpt++;
1215     } while (cpt < 100
1216              && levl[cc->x][cc->y].typ
1217                     != (level.flags.corrmaze ? CORR : ROOM));
1218     if (cpt >= 100) {
1219         int x, y;
1220 
1221         /* last try */
1222         for (x = 1; x < x_maze_max; x++)
1223             for (y = 1; y < y_maze_max; y++) {
1224                 cc->x = x;
1225                 cc->y = y;
1226                 if (levl[cc->x][cc->y].typ
1227                     == (level.flags.corrmaze ? CORR : ROOM))
1228                     return;
1229             }
1230         panic("mazexy: can't find a place!");
1231     }
1232     return;
1233 }
1234 
1235 /* put a non-diggable boundary around the initial portion of a level map.
1236  * assumes that no level will initially put things beyond the isok() range.
1237  *
1238  * we can't bound unconditionally on the last line with something in it,
1239  * because that something might be a niche which was already reachable,
1240  * so the boundary would be breached
1241  *
1242  * we can't bound unconditionally on one beyond the last line, because
1243  * that provides a window of abuse for wallified special levels
1244  */
1245 void
bound_digging()1246 bound_digging()
1247 {
1248     int x, y;
1249     unsigned typ;
1250     struct rm *lev;
1251     boolean found, nonwall;
1252     int xmin, xmax, ymin, ymax;
1253 
1254     if (Is_earthlevel(&u.uz))
1255         return; /* everything diggable here */
1256 
1257     found = nonwall = FALSE;
1258     for (xmin = 0; !found && xmin <= COLNO; xmin++) {
1259         lev = &levl[xmin][0];
1260         for (y = 0; y <= ROWNO - 1; y++, lev++) {
1261             typ = lev->typ;
1262             if (typ != STONE) {
1263                 found = TRUE;
1264                 if (!IS_WALL(typ))
1265                     nonwall = TRUE;
1266             }
1267         }
1268     }
1269     xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1270     if (xmin < 0)
1271         xmin = 0;
1272 
1273     found = nonwall = FALSE;
1274     for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
1275         lev = &levl[xmax][0];
1276         for (y = 0; y <= ROWNO - 1; y++, lev++) {
1277             typ = lev->typ;
1278             if (typ != STONE) {
1279                 found = TRUE;
1280                 if (!IS_WALL(typ))
1281                     nonwall = TRUE;
1282             }
1283         }
1284     }
1285     xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1286     if (xmax >= COLNO)
1287         xmax = COLNO - 1;
1288 
1289     found = nonwall = FALSE;
1290     for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
1291         lev = &levl[xmin][ymin];
1292         for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1293             typ = lev->typ;
1294             if (typ != STONE) {
1295                 found = TRUE;
1296                 if (!IS_WALL(typ))
1297                     nonwall = TRUE;
1298             }
1299         }
1300     }
1301     ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1302 
1303     found = nonwall = FALSE;
1304     for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
1305         lev = &levl[xmin][ymax];
1306         for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1307             typ = lev->typ;
1308             if (typ != STONE) {
1309                 found = TRUE;
1310                 if (!IS_WALL(typ))
1311                     nonwall = TRUE;
1312             }
1313         }
1314     }
1315     ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1316 
1317     for (x = 0; x < COLNO; x++)
1318         for (y = 0; y < ROWNO; y++)
1319             if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
1320 #ifdef DCC30_BUG
1321                 lev = &levl[x][y];
1322                 lev->wall_info |= W_NONDIGGABLE;
1323 #else
1324                 levl[x][y].wall_info |= W_NONDIGGABLE;
1325 #endif
1326             }
1327 }
1328 
1329 void
mkportal(x,y,todnum,todlevel)1330 mkportal(x, y, todnum, todlevel)
1331 xchar x, y, todnum, todlevel;
1332 {
1333     /* a portal "trap" must be matched by a
1334        portal in the destination dungeon/dlevel */
1335     struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
1336 
1337     if (!ttmp) {
1338         impossible("portal on top of portal??");
1339         return;
1340     }
1341     debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
1342                 dungeons[todnum].dname, todlevel);
1343     ttmp->dst.dnum = todnum;
1344     ttmp->dst.dlevel = todlevel;
1345     return;
1346 }
1347 
1348 void
fumaroles()1349 fumaroles()
1350 {
1351     xchar n;
1352     boolean snd = FALSE, loud = FALSE;
1353 
1354     for (n = rn2(3) + 2; n; n--) {
1355         xchar x = rn1(COLNO - 4, 3);
1356         xchar y = rn1(ROWNO - 4, 3);
1357 
1358         if (levl[x][y].typ == LAVAPOOL) {
1359             NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
1360 
1361             clear_heros_fault(r);
1362             snd = TRUE;
1363             if (distu(x, y) < 15)
1364                 loud = TRUE;
1365         }
1366     }
1367     if (snd && !Deaf)
1368         Norep("You hear a %swhoosh!", loud ? "loud " : "");  /* Deaf-aware */
1369 }
1370 
1371 /*
1372  * Special waterlevel stuff in endgame (TH).
1373  *
1374  * Some of these functions would probably logically belong to some
1375  * other source files, but they are all so nicely encapsulated here.
1376  */
1377 
1378 static struct bubble *bbubbles, *ebubbles;
1379 
1380 static struct trap *wportal;
1381 static int xmin, ymin, xmax, ymax; /* level boundaries */
1382 /* bubble movement boundaries */
1383 #define bxmin (xmin + 1)
1384 #define bymin (ymin + 1)
1385 #define bxmax (xmax - 1)
1386 #define bymax (ymax - 1)
1387 
1388 STATIC_DCL void NDECL(set_wportal);
1389 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
1390 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
1391 
1392 void
movebubbles()1393 movebubbles()
1394 {
1395     static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
1396                                          0, 0, 0, 0, 0, 0 };
1397     static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
1398                                        1, 0, 0, 0, 0 };
1399     static boolean up = FALSE;
1400     struct bubble *b;
1401     struct container *cons;
1402     struct trap *btrap;
1403     int x, y, i, j, bcpin = 0;
1404 
1405     /* set up the portal the first time bubbles are moved */
1406     if (!wportal)
1407         set_wportal();
1408 
1409     vision_recalc(2);
1410 
1411     if (Is_waterlevel(&u.uz)) {
1412         /* keep attached ball&chain separate from bubble objects */
1413         if (Punished)
1414             bcpin = unplacebc_and_covet_placebc();
1415 
1416         /*
1417          * Pick up everything inside of a bubble then fill all bubble
1418          * locations.
1419          */
1420         for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1421             if (b->cons)
1422                 panic("movebubbles: cons != null");
1423             for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1424                 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1425                     if (b->bm[j + 2] & (1 << i)) {
1426                         if (!isok(x, y)) {
1427                             impossible("movebubbles: bad pos (%d,%d)", x, y);
1428                             continue;
1429                         }
1430 
1431                         /* pick up objects, monsters, hero, and traps */
1432                         if (OBJ_AT(x, y)) {
1433                             struct obj *olist = (struct obj *) 0, *otmp;
1434 
1435                             while ((otmp = level.objects[x][y]) != 0) {
1436                                 remove_object(otmp);
1437                                 otmp->ox = otmp->oy = 0;
1438                                 otmp->nexthere = olist;
1439                                 olist = otmp;
1440                             }
1441 
1442                             cons = (struct container *) alloc(sizeof *cons);
1443                             cons->x = x;
1444                             cons->y = y;
1445                             cons->what = CONS_OBJ;
1446                             cons->list = (genericptr_t) olist;
1447                             cons->next = b->cons;
1448                             b->cons = cons;
1449                         }
1450                         if (MON_AT(x, y)) {
1451                             struct monst *mon = m_at(x, y);
1452 
1453                             cons = (struct container *) alloc(sizeof *cons);
1454                             cons->x = x;
1455                             cons->y = y;
1456                             cons->what = CONS_MON;
1457                             cons->list = (genericptr_t) mon;
1458 
1459                             cons->next = b->cons;
1460                             b->cons = cons;
1461 
1462                             if (mon->wormno)
1463                                 remove_worm(mon);
1464                             else
1465                                 remove_monster(x, y);
1466 
1467                             newsym(x, y); /* clean up old position */
1468                             mon->mx = mon->my = 0;
1469                             mon->mstate |= MON_BUBBLEMOVE;
1470                         }
1471                         if (!u.uswallow && x == u.ux && y == u.uy) {
1472                             cons = (struct container *) alloc(sizeof *cons);
1473                             cons->x = x;
1474                             cons->y = y;
1475                             cons->what = CONS_HERO;
1476                             cons->list = (genericptr_t) 0;
1477 
1478                             cons->next = b->cons;
1479                             b->cons = cons;
1480                         }
1481                         if ((btrap = t_at(x, y)) != 0) {
1482                             cons = (struct container *) alloc(sizeof *cons);
1483                             cons->x = x;
1484                             cons->y = y;
1485                             cons->what = CONS_TRAP;
1486                             cons->list = (genericptr_t) btrap;
1487 
1488                             cons->next = b->cons;
1489                             b->cons = cons;
1490                         }
1491 
1492                         levl[x][y] = water_pos;
1493                         block_point(x, y);
1494                     }
1495         }
1496     } else if (Is_airlevel(&u.uz)) {
1497         boolean xedge, yedge;
1498 
1499         for (x = 1; x <= (COLNO - 1); x++)
1500             for (y = 0; y <= (ROWNO - 1); y++) {
1501                 levl[x][y] = air_pos;
1502                 unblock_point(x, y);
1503                 /* all air or all cloud around the perimeter of the Air
1504                    level tends to look strange; break up the pattern */
1505                 xedge = (boolean) (x < bxmin || x > bxmax);
1506                 yedge = (boolean) (y < bymin || y > bymax);
1507                 if (xedge || yedge) {
1508                     if (!rn2(xedge ? 3 : 5)) {
1509                         levl[x][y].typ = CLOUD;
1510                         block_point(x, y);
1511                     }
1512                 }
1513             }
1514     }
1515 
1516     /*
1517      * Every second time traverse down.  This is because otherwise
1518      * all the junk that changes owners when bubbles overlap
1519      * would eventually end up in the last bubble in the chain.
1520      */
1521     up = !up;
1522     for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1523         int rx = rn2(3), ry = rn2(3);
1524 
1525         mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1526                   b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1527     }
1528 
1529     /* put attached ball&chain back */
1530     if (Is_waterlevel(&u.uz) && Punished)
1531         lift_covet_and_placebc(bcpin);
1532     vision_full_recalc = 1;
1533 }
1534 
1535 /* when moving in water, possibly (1 in 3) alter the intended destination */
1536 void
water_friction()1537 water_friction()
1538 {
1539     int x, y, dx, dy;
1540     boolean eff = FALSE;
1541 
1542     if (Swimming && rn2(4))
1543         return; /* natural swimmers have advantage */
1544 
1545     if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1546         /* cancel delta x and choose an arbitrary delta y value */
1547         x = u.ux;
1548         do {
1549             dy = rn2(3) - 1; /* -1, 0, 1 */
1550             y = u.uy + dy;
1551         } while (dy && (!isok(x, y) || !is_pool(x, y)));
1552         u.dx = 0;
1553         u.dy = dy;
1554         eff = TRUE;
1555     } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1556         /* cancel delta y and choose an arbitrary delta x value */
1557         y = u.uy;
1558         do {
1559             dx = rn2(3) - 1; /* -1 .. 1 */
1560             x = u.ux + dx;
1561         } while (dx && (!isok(x, y) || !is_pool(x, y)));
1562         u.dy = 0;
1563         u.dx = dx;
1564         eff = TRUE;
1565     }
1566     if (eff)
1567         pline("Water turbulence affects your movements.");
1568 }
1569 
1570 void
save_waterlevel(fd,mode)1571 save_waterlevel(fd, mode)
1572 int fd, mode;
1573 {
1574     struct bubble *b;
1575 
1576     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1577         return;
1578 
1579     if (perform_bwrite(mode)) {
1580         int n = 0;
1581         for (b = bbubbles; b; b = b->next)
1582             ++n;
1583         bwrite(fd, (genericptr_t) &n, sizeof n);
1584         bwrite(fd, (genericptr_t) &xmin, sizeof xmin);
1585         bwrite(fd, (genericptr_t) &ymin, sizeof ymin);
1586         bwrite(fd, (genericptr_t) &xmax, sizeof xmax);
1587         bwrite(fd, (genericptr_t) &ymax, sizeof ymax);
1588         for (b = bbubbles; b; b = b->next)
1589             bwrite(fd, (genericptr_t) b, sizeof *b);
1590     }
1591     if (release_data(mode))
1592         unsetup_waterlevel();
1593 }
1594 
1595 void
restore_waterlevel(fd)1596 restore_waterlevel(fd)
1597 int fd;
1598 {
1599     struct bubble *b = (struct bubble *) 0, *btmp;
1600     int i, n;
1601 
1602     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1603         return;
1604 
1605     if (fd == -1) { /* special handling for restore in goto_level() */
1606         if (!wizard)
1607             impossible("restore_waterlevel: returning to %s?",
1608                        Is_waterlevel(&u.uz) ? "Water" : "Air");
1609         setup_waterlevel();
1610         return;
1611     }
1612 
1613     set_wportal();
1614     mread(fd, (genericptr_t) &n, sizeof n);
1615     mread(fd, (genericptr_t) &xmin, sizeof xmin);
1616     mread(fd, (genericptr_t) &ymin, sizeof ymin);
1617     mread(fd, (genericptr_t) &xmax, sizeof xmax);
1618     mread(fd, (genericptr_t) &ymax, sizeof ymax);
1619     for (i = 0; i < n; i++) {
1620         btmp = b;
1621         b = (struct bubble *) alloc(sizeof *b);
1622         mread(fd, (genericptr_t) b, sizeof *b);
1623         if (bbubbles) {
1624             btmp->next = b;
1625             b->prev = btmp;
1626         } else {
1627             bbubbles = b;
1628             b->prev = (struct bubble *) 0;
1629         }
1630         mv_bubble(b, 0, 0, TRUE);
1631     }
1632     ebubbles = b;
1633     b->next = (struct bubble *) 0;
1634 }
1635 
1636 const char *
waterbody_name(x,y)1637 waterbody_name(x, y)
1638 xchar x, y;
1639 {
1640     struct rm *lev;
1641     schar ltyp;
1642 
1643     if (!isok(x, y))
1644         return "drink"; /* should never happen */
1645     lev = &levl[x][y];
1646     ltyp = lev->typ;
1647     if (ltyp == DRAWBRIDGE_UP)
1648         ltyp = db_under_typ(lev->drawbridgemask);
1649 
1650     if (ltyp == LAVAPOOL)
1651         return hliquid("lava");
1652     else if (ltyp == ICE)
1653         return "ice";
1654     else if (ltyp == POOL)
1655         return "pool of water";
1656     else if (ltyp == WATER || Is_waterlevel(&u.uz))
1657         ; /* fall through to default return value */
1658     else if (Is_juiblex_level(&u.uz))
1659         return "swamp";
1660     else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1661         return "moat";
1662 
1663     return hliquid("water");
1664 }
1665 
1666 STATIC_OVL void
set_wportal()1667 set_wportal()
1668 {
1669     /* there better be only one magic portal on water level... */
1670     for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1671         if (wportal->ttyp == MAGIC_PORTAL)
1672             return;
1673     impossible("set_wportal(): no portal!");
1674 }
1675 
1676 STATIC_OVL void
setup_waterlevel()1677 setup_waterlevel()
1678 {
1679     int x, y, xskip, yskip, typ, glyph;
1680 
1681     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1682         panic("setup_waterlevel(): [%d:%d] neither 'Water' nor 'Air'",
1683               (int) u.uz.dnum, (int) u.uz.dlevel);
1684 
1685     /* ouch, hardcoded... (file scope statics and used in bxmin,bymax,&c) */
1686     xmin = 3;
1687     ymin = 1;
1688     /* use separate statements so that compiler won't complain about min()
1689        comparing two constants; the alternative is to do this in the
1690        preprocessor: #if (20 > ROWNO-1) ymax=ROWNO-1 #else ymax=20 #endif */
1691     xmax = 78;
1692     xmax = min(xmax, (COLNO - 1) - 1);
1693     ymax = 20;
1694     ymax = min(ymax, (ROWNO - 1));
1695 
1696     /* entire level is remembered as one glyph and any unspecified portion
1697        should default to level's base element rather than to usual stone */
1698     glyph = cmap_to_glyph(Is_waterlevel(&u.uz) ? S_water : S_air);
1699     typ = Is_waterlevel(&u.uz) ? WATER : AIR;
1700 
1701     /* set unspecified terrain (stone) and hero's memory to water or air */
1702     for (x = 1; x <= COLNO - 1; x++)
1703         for (y = 0; y <= ROWNO - 1; y++) {
1704             levl[x][y].glyph = glyph;
1705             if (levl[x][y].typ == STONE)
1706                 levl[x][y].typ = typ;
1707         }
1708 
1709     /* make bubbles */
1710     if (Is_waterlevel(&u.uz)) {
1711         xskip = 10 + rn2(10);
1712         yskip = 4 + rn2(4);
1713     } else {
1714         xskip = 6 + rn2(4);
1715         yskip = 3 + rn2(3);
1716     }
1717 
1718     for (x = bxmin; x <= bxmax; x += xskip)
1719         for (y = bymin; y <= bymax; y += yskip)
1720             mk_bubble(x, y, rn2(7));
1721 }
1722 
1723 STATIC_OVL void
unsetup_waterlevel()1724 unsetup_waterlevel()
1725 {
1726     struct bubble *b, *bb;
1727 
1728     /* free bubbles */
1729     for (b = bbubbles; b; b = bb) {
1730         bb = b->next;
1731         free((genericptr_t) b);
1732     }
1733     bbubbles = ebubbles = (struct bubble *) 0;
1734 }
1735 
1736 STATIC_OVL void
mk_bubble(x,y,n)1737 mk_bubble(x, y, n)
1738 int x, y, n;
1739 {
1740     /*
1741      * These bit masks make visually pleasing bubbles on a normal aspect
1742      * 25x80 terminal, which naturally results in them being mathematically
1743      * anything but symmetric.  For this reason they cannot be computed
1744      * in situ, either.  The first two elements tell the dimensions of
1745      * the bubble's bounding box.
1746      */
1747     static const uchar
1748         bm2[] = { 2, 1, 0x3 },
1749         bm3[] = { 3, 2, 0x7, 0x7 },
1750         bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1751         bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1752         bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1753         bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1754         bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1755         *const bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1756     struct bubble *b;
1757 
1758     if (x >= bxmax || y >= bymax)
1759         return;
1760     if (n >= SIZE(bmask)) {
1761         impossible("n too large (mk_bubble)");
1762         n = SIZE(bmask) - 1;
1763     }
1764     if (bmask[n][1] > MAX_BMASK) {
1765         panic("bmask size is larger than MAX_BMASK");
1766     }
1767     b = (struct bubble *) alloc(sizeof *b);
1768     if ((x + (int) bmask[n][0] - 1) > bxmax)
1769         x = bxmax - bmask[n][0] + 1;
1770     if ((y + (int) bmask[n][1] - 1) > bymax)
1771         y = bymax - bmask[n][1] + 1;
1772     b->x = x;
1773     b->y = y;
1774     b->dx = 1 - rn2(3);
1775     b->dy = 1 - rn2(3);
1776     /* y dimension is the length of bitmap data - see bmask above */
1777     (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1778                   (bmask[n][1] + 2) * sizeof (b->bm[0]));
1779     b->cons = 0;
1780     if (!bbubbles)
1781         bbubbles = b;
1782     if (ebubbles) {
1783         ebubbles->next = b;
1784         b->prev = ebubbles;
1785     } else
1786         b->prev = (struct bubble *) 0;
1787     b->next = (struct bubble *) 0;
1788     ebubbles = b;
1789     mv_bubble(b, 0, 0, TRUE);
1790 }
1791 
1792 /*
1793  * The player, the portal and all other objects and monsters
1794  * float along with their associated bubbles.  Bubbles may overlap
1795  * freely, and the contents may get associated with other bubbles in
1796  * the process.  Bubbles are "sticky", meaning that if the player is
1797  * in the immediate neighborhood of one, he/she may get sucked inside.
1798  * This property also makes leaving a bubble slightly difficult.
1799  */
1800 STATIC_OVL void
mv_bubble(b,dx,dy,ini)1801 mv_bubble(b, dx, dy, ini)
1802 struct bubble *b;
1803 int dx, dy;
1804 boolean ini;
1805 {
1806     int x, y, i, j, colli = 0;
1807     struct container *cons, *ctemp;
1808 
1809     /* clouds move slowly */
1810     if (!Is_airlevel(&u.uz) || !rn2(6)) {
1811         /* move bubble */
1812         if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1813             /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1814             dx = sgn(dx);
1815             dy = sgn(dy);
1816         }
1817 
1818         /*
1819          * collision with level borders?
1820          *      1 = horizontal border, 2 = vertical, 3 = corner
1821          */
1822         if (b->x <= bxmin)
1823             colli |= 2;
1824         if (b->y <= bymin)
1825             colli |= 1;
1826         if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1827             colli |= 2;
1828         if ((int) (b->y + b->bm[1] - 1) >= bymax)
1829             colli |= 1;
1830 
1831         if (b->x < bxmin) {
1832             pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1833             b->x = bxmin;
1834         }
1835         if (b->y < bymin) {
1836             pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1837             b->y = bymin;
1838         }
1839         if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1840             pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1841                   bxmax);
1842             b->x = bxmax - b->bm[0] + 1;
1843         }
1844         if ((int) (b->y + b->bm[1] - 1) > bymax) {
1845             pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1846                   bymax);
1847             b->y = bymax - b->bm[1] + 1;
1848         }
1849 
1850         /* bounce if we're trying to move off the border */
1851         if (b->x == bxmin && dx < 0)
1852             dx = -dx;
1853         if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1854             dx = -dx;
1855         if (b->y == bymin && dy < 0)
1856             dy = -dy;
1857         if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1858             dy = -dy;
1859 
1860         b->x += dx;
1861         b->y += dy;
1862     }
1863 
1864     /* draw the bubbles */
1865     for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1866         for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1867             if (b->bm[j + 2] & (1 << i)) {
1868                 if (Is_waterlevel(&u.uz)) {
1869                     levl[x][y].typ = AIR;
1870                     levl[x][y].lit = 1;
1871                     unblock_point(x, y);
1872                 } else if (Is_airlevel(&u.uz)) {
1873                     levl[x][y].typ = CLOUD;
1874                     levl[x][y].lit = 1;
1875                     block_point(x, y);
1876                 }
1877             }
1878 
1879     if (Is_waterlevel(&u.uz)) {
1880         /* replace contents of bubble */
1881         for (cons = b->cons; cons; cons = ctemp) {
1882             ctemp = cons->next;
1883             cons->x += dx;
1884             cons->y += dy;
1885 
1886             switch (cons->what) {
1887             case CONS_OBJ: {
1888                 struct obj *olist, *otmp;
1889 
1890                 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1891                     otmp = olist->nexthere;
1892                     place_object(olist, cons->x, cons->y);
1893                     stackobj(olist);
1894                 }
1895                 break;
1896             }
1897 
1898             case CONS_MON: {
1899                 struct monst *mon = (struct monst *) cons->list;
1900 
1901                 /* mnearto() might fail. We can jump right to elemental_clog
1902                    from here rather than deal_with_overcrowding() */
1903                 if (!mnearto(mon, cons->x, cons->y, TRUE))
1904                     elemental_clog(mon);
1905                 break;
1906             }
1907 
1908             case CONS_HERO: {
1909                 struct monst *mtmp = m_at(cons->x, cons->y);
1910                 int ux0 = u.ux, uy0 = u.uy;
1911 
1912                 u_on_newpos(cons->x, cons->y);
1913                 newsym(ux0, uy0); /* clean up old position */
1914 
1915                 if (mtmp) {
1916                     mnexto(mtmp);
1917                 }
1918                 break;
1919             }
1920 
1921             case CONS_TRAP: {
1922                 struct trap *btrap = (struct trap *) cons->list;
1923 
1924                 btrap->tx = cons->x;
1925                 btrap->ty = cons->y;
1926                 break;
1927             }
1928 
1929             default:
1930                 impossible("mv_bubble: unknown bubble contents");
1931                 break;
1932             }
1933             free((genericptr_t) cons);
1934         }
1935         b->cons = 0;
1936     }
1937 
1938     /* boing? */
1939     switch (colli) {
1940     case 1:
1941         b->dy = -b->dy;
1942         break;
1943     case 3:
1944         b->dy = -b->dy;
1945         /*FALLTHRU*/
1946     case 2:
1947         b->dx = -b->dx;
1948         break;
1949     default:
1950         /* sometimes alter direction for fun anyway
1951            (higher probability for stationary bubbles) */
1952         if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
1953             b->dx = 1 - rn2(3);
1954             b->dy = 1 - rn2(3);
1955         }
1956     }
1957 }
1958 
1959 /*mkmaze.c*/
1960