OSDN Git Service

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