OSDN Git Service

fix #39767
[jnethack/source.git] / src / mkmaze.c
1 /* NetHack 3.6  mkmaze.c        $NHDT-Date: 1555022325 2019/04/11 22:38:45 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.68 $ */
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-2019            */
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 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
469
470 /* this is special stuff that the level compiler cannot (yet) handle */
471 void
472 fixup_special()
473 {
474     lev_region *r = lregions;
475     struct d_level lev;
476     int x, y;
477     struct mkroom *croom;
478     boolean added_branch = FALSE;
479
480     if (was_waterlevel) {
481         was_waterlevel = FALSE;
482         u.uinwater = 0;
483         unsetup_waterlevel();
484     }
485     if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
486         level.flags.hero_memory = 0;
487         was_waterlevel = TRUE;
488         /* water level is an odd beast - it has to be set up
489            before calling place_lregions etc. */
490         setup_waterlevel();
491     }
492     for (x = 0; x < num_lregions; x++, r++) {
493         switch (r->rtype) {
494         case LR_BRANCH:
495             added_branch = TRUE;
496             goto place_it;
497
498         case LR_PORTAL:
499             if (*r->rname.str >= '0' && *r->rname.str <= '9') {
500                 /* "chutes and ladders" */
501                 lev = u.uz;
502                 lev.dlevel = atoi(r->rname.str);
503             } else {
504                 s_level *sp = find_level(r->rname.str);
505                 lev = sp->dlevel;
506             }
507             /*FALLTHRU*/
508
509         case LR_UPSTAIR:
510         case LR_DOWNSTAIR:
511         place_it:
512             place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
513                           r->inarea.y2, r->delarea.x1, r->delarea.y1,
514                           r->delarea.x2, r->delarea.y2, r->rtype, &lev);
515             break;
516
517         case LR_TELE:
518         case LR_UPTELE:
519         case LR_DOWNTELE:
520             /* save the region outlines for goto_level() */
521             if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
522                 updest.lx = r->inarea.x1;
523                 updest.ly = r->inarea.y1;
524                 updest.hx = r->inarea.x2;
525                 updest.hy = r->inarea.y2;
526                 updest.nlx = r->delarea.x1;
527                 updest.nly = r->delarea.y1;
528                 updest.nhx = r->delarea.x2;
529                 updest.nhy = r->delarea.y2;
530             }
531             if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
532                 dndest.lx = r->inarea.x1;
533                 dndest.ly = r->inarea.y1;
534                 dndest.hx = r->inarea.x2;
535                 dndest.hy = r->inarea.y2;
536                 dndest.nlx = r->delarea.x1;
537                 dndest.nly = r->delarea.y1;
538                 dndest.nhx = r->delarea.x2;
539                 dndest.nhy = r->delarea.y2;
540             }
541             /* place_lregion gets called from goto_level() */
542             break;
543         }
544
545         if (r->rname.str)
546             free((genericptr_t) r->rname.str), r->rname.str = 0;
547     }
548
549     /* place dungeon branch if not placed above */
550     if (!added_branch && Is_branchlev(&u.uz)) {
551         place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
552     }
553
554     /* Still need to add some stuff to level file */
555     if (Is_medusa_level(&u.uz)) {
556         struct obj *otmp;
557         int tryct;
558
559         croom = &rooms[0]; /* only one room on the medusa level */
560         for (tryct = rnd(4); tryct; tryct--) {
561             x = somex(croom);
562             y = somey(croom);
563             if (goodpos(x, y, (struct monst *) 0, 0)) {
564                 otmp = mk_tt_object(STATUE, x, y);
565                 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm])
566                                 || pm_resistance(&mons[otmp->corpsenm],
567                                                  MR_STONE))) {
568                     /* set_corpsenm() handles weight too */
569                     set_corpsenm(otmp, rndmonnum());
570                 }
571             }
572         }
573
574         if (rn2(2))
575             otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
576         else /* Medusa statues don't contain books */
577             otmp =
578                 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
579                            somex(croom), somey(croom), CORPSTAT_NONE);
580         if (otmp) {
581             while (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
582                    || poly_when_stoned(&mons[otmp->corpsenm])) {
583                 /* set_corpsenm() handles weight too */
584                 set_corpsenm(otmp, rndmonnum());
585             }
586         }
587     } else if (Is_wiz1_level(&u.uz)) {
588         croom = search_special(MORGUE);
589
590         create_secret_door(croom, W_SOUTH | W_EAST | W_WEST);
591     } else if (Is_knox(&u.uz)) {
592         /* using an unfilled morgue for rm id */
593         croom = search_special(MORGUE);
594         /* avoid inappropriate morgue-related messages */
595         level.flags.graveyard = level.flags.has_morgue = 0;
596         croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
597         /* stock the main vault */
598         for (x = croom->lx; x <= croom->hx; x++)
599             for (y = croom->ly; y <= croom->hy; y++) {
600                 (void) mkgold((long) rn1(300, 600), x, y);
601                 if (!rn2(3) && !is_pool(x, y))
602                     (void) maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
603             }
604     } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
605         /* less chance for undead corpses (lured from lower morgues) */
606         level.flags.graveyard = 1;
607     } else if (Is_stronghold(&u.uz)) {
608         level.flags.graveyard = 1;
609     } else if (Is_sanctum(&u.uz)) {
610         croom = search_special(TEMPLE);
611
612         create_secret_door(croom, W_ANY);
613     } else if (on_level(&u.uz, &orcus_level)) {
614         struct monst *mtmp, *mtmp2;
615
616         /* it's a ghost town, get rid of shopkeepers */
617         for (mtmp = fmon; mtmp; mtmp = mtmp2) {
618             mtmp2 = mtmp->nmon;
619             if (mtmp->isshk)
620                 mongone(mtmp);
621         }
622     } else if (on_level(&u.uz, &baalzebub_level)) {
623         /* custom wallify the "beetle" potion of the level */
624         baalz_fixup();
625     } else if (u.uz.dnum == mines_dnum && ransacked) {
626        stolen_booty();
627     }
628
629     if (lregions)
630         free((genericptr_t) lregions), lregions = 0;
631     num_lregions = 0;
632 }
633
634 void
635 check_ransacked(s)
636 char *s;
637 {
638     /* this kludge only works as long as orctown is minetn-1 */
639     ransacked = (u.uz.dnum == mines_dnum && !strcmp(s, "minetn-1"));
640 }
641
642 #define ORC_LEADER 1
643 static const char *orcfruit[] = { "paddle cactus", "dwarven root" };
644
645 void
646 migrate_orc(mtmp, mflags)
647 struct monst *mtmp;
648 unsigned long mflags;
649 {
650     int nlev, max_depth, cur_depth;
651     d_level dest;
652
653     cur_depth = (int) depth(&u.uz);
654     max_depth = dunlevs_in_dungeon(&u.uz)
655                 + (dungeons[u.uz.dnum].depth_start - 1);
656     if (mflags == ORC_LEADER) {
657         /* Note that the orc leader will take possession of any
658          * remaining stuff not already delivered to other
659          * orcs between here and the bottom of the mines.
660          */
661         nlev = max_depth;
662         /* once in a blue moon, he won't be at the very bottom */
663         if (!rn2(40))
664             nlev--;
665         mtmp->mspare1 = MIGR_LEFTOVERS;
666     } else {
667         nlev = rn2((max_depth - cur_depth) + 1) + cur_depth;
668         if (nlev == cur_depth)
669             nlev++;
670         if (nlev > max_depth)
671             nlev = max_depth;
672         mtmp->mspare1 = 0L;
673     }
674     get_level(&dest, nlev);
675     migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0);
676 }
677
678 void
679 shiny_orc_stuff(mtmp)
680 struct monst *mtmp;
681 {
682     int gemprob, goldprob, otyp;
683     struct obj *otmp;
684     boolean is_captain = (mtmp->data == &mons[PM_ORC_CAPTAIN]);
685
686     /* probabilities */
687     goldprob = is_captain ? 600 : 300;
688     gemprob = goldprob / 4;
689     if (rn2(1000) < goldprob) {
690         if ((otmp = mksobj(GOLD_PIECE, FALSE, FALSE)) != 0) {
691             otmp->quan = 1L + rnd(goldprob);
692             otmp->owt = weight(otmp);
693             add_to_minv(mtmp, otmp);
694         }
695     }
696     if (rn2(1000) < gemprob) {
697         if ((otmp = mkobj(GEM_CLASS, FALSE)) != 0) {
698             if (otmp->otyp == ROCK)
699                 dealloc_obj(otmp);
700             else
701                 add_to_minv(mtmp, otmp);
702         }
703     }
704     if (is_captain || !rn2(8)) {
705         otyp = shiny_obj(RING_CLASS);
706         if (otyp != STRANGE_OBJECT && (otmp = mksobj(otyp, FALSE, FALSE)) != 0)
707             add_to_minv(mtmp, otmp);
708     }
709 }
710 void
711 migr_booty_item(otyp, gang)
712 int otyp;
713 const char *gang;
714 {
715     struct obj *otmp;
716
717     otmp = mksobj_migr_to_species(otyp, (unsigned long) M2_ORC, FALSE, FALSE);
718     if (otmp && gang) {
719         new_oname(otmp, strlen(gang) + 1); /* removes old name if present */
720         Strcpy(ONAME(otmp), gang);
721         if (otyp >= TRIPE_RATION && otyp <= TIN) {
722             if (otyp == SLIME_MOLD)
723                 otmp->spe = fruitadd((char *) orcfruit[rn2(SIZE(orcfruit))],
724                                      (struct fruit *) 0);
725             otmp->quan += (long) rn2(3);
726             otmp->owt = weight(otmp);
727         }
728     }
729 }
730
731 void
732 stolen_booty(VOID_ARGS)
733 {
734     char *gang, gang_name[BUFSZ];
735     struct monst *mtmp;
736     int cnt, i, otyp;
737
738     /*
739      * --------------------------------------------------------
740      * Mythos:
741      *
742      *      A tragic accident has occurred in Frontier Town...
743      *      It has been overrun by orcs.
744      *
745      *      The booty that the orcs took from the town is now
746      *      in the possession of the orcs that did this and
747      *      have long since fled the level.
748      * --------------------------------------------------------
749      */
750
751     gang = rndorcname(gang_name);
752     /* create the stuff that the gang took */
753     cnt = rnd(4);
754     for (i = 0; i < cnt; ++i)
755         migr_booty_item(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, gang);
756     cnt = rnd(3);
757     for (i = 0; i < cnt; ++i)
758         migr_booty_item(SKELETON_KEY, gang);
759     otyp = rn2((GAUNTLETS_OF_DEXTERITY - LEATHER_GLOVES) + 1) + LEATHER_GLOVES;
760     migr_booty_item(otyp, gang);
761     cnt = rnd(10);
762     for (i = 0; i < cnt; ++i) {
763         /* Food items - but no lembas! (or some other weird things) */
764         otyp = rn2((TIN - TRIPE_RATION) + 1) + TRIPE_RATION;
765         if (otyp != LEMBAS_WAFER && otyp != GLOB_OF_GRAY_OOZE
766             && otyp != GLOB_OF_BROWN_PUDDING && otyp != GLOB_OF_GREEN_SLIME
767             && otyp != GLOB_OF_BLACK_PUDDING && otyp != MEAT_STICK
768             && otyp != MEATBALL && otyp != MEAT_STICK && otyp != MEAT_RING
769             && otyp != HUGE_CHUNK_OF_MEAT && otyp != CORPSE)
770             migr_booty_item(otyp, gang);
771     }
772     migr_booty_item(rn2(2) ? LONG_SWORD : SILVER_SABER, gang);
773     /* create the leader of the orc gang */
774     mtmp = makemon(&mons[PM_ORC_CAPTAIN], 0, 0, MM_NONAME);
775     if (mtmp) {
776         mtmp = christen_monst(mtmp, upstart(gang));
777         mtmp->mpeaceful = 0;
778         shiny_orc_stuff(mtmp);
779         migrate_orc(mtmp, ORC_LEADER);
780     }
781     /* Make most of the orcs on the level be part of the invading gang */
782     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
783         if (DEADMONSTER(mtmp))
784             continue;
785
786         if (is_orc(mtmp->data) && !has_mname(mtmp) && rn2(10)) {
787             /*
788              * We'll consider the orc captain from the level
789              * .des file to be the captain of a rival orc horde
790              * who is there to see what has transpired, and to
791              * contemplate future action.
792              *
793              * Don't christen the orc captain as a subordinate
794              * member of the main orc horde.
795              */
796             if (mtmp->data != &mons[PM_ORC_CAPTAIN])
797                 mtmp = christen_orc(mtmp, upstart(gang), "");
798         }
799     }
800     /* Lastly, ensure there's several more orcs from the gang along the way.
801      * The mechanics are such that they aren't actually identified as
802      * members of the invading gang until they get their spoils assigned
803      * to the inventory; handled during that assignment.
804      */
805     cnt = rn2(10) + 5;
806     for (i = 0; i < cnt; ++i) {
807         int mtyp;
808
809         mtyp = rn2((PM_ORC_SHAMAN - PM_ORC) + 1) + PM_ORC;
810         mtmp = makemon(&mons[mtyp], 0, 0, MM_NONAME);
811         if (mtmp) {
812             shiny_orc_stuff(mtmp);
813             migrate_orc(mtmp, 0UL);
814         }
815     }
816     ransacked = 0;
817 }
818
819 #undef ORC_LEADER
820
821 boolean
822 maze_inbounds(x, y)
823 int x, y;
824 {
825     return (x >= 2 && y >= 2
826             && x < x_maze_max && y < y_maze_max && isok(x, y));
827 }
828
829 void
830 maze_remove_deadends(typ)
831 xchar typ;
832 {
833     char dirok[4];
834     int x, y, dir, idx, idx2, dx, dy, dx2, dy2;
835
836     dirok[0] = 0; /* lint suppression */
837     for (x = 2; x < x_maze_max; x++)
838         for (y = 2; y < y_maze_max; y++)
839             if (ACCESSIBLE(levl[x][y].typ) && (x % 2) && (y % 2)) {
840                 idx = idx2 = 0;
841                 for (dir = 0; dir < 4; dir++) {
842                     /* note: mz_move() is a macro which modifies
843                        one of its first two parameters */
844                     dx = dx2 = x;
845                     dy = dy2 = y;
846                     mz_move(dx, dy, dir);
847                     if (!maze_inbounds(dx, dy)) {
848                         idx2++;
849                         continue;
850                     }
851                     mz_move(dx2, dy2, dir);
852                     mz_move(dx2, dy2, dir);
853                     if (!maze_inbounds(dx2, dy2)) {
854                         idx2++;
855                         continue;
856                     }
857                     if (!ACCESSIBLE(levl[dx][dy].typ)
858                         && ACCESSIBLE(levl[dx2][dy2].typ)) {
859                         dirok[idx++] = dir;
860                         idx2++;
861                     }
862                 }
863                 if (idx2 >= 3 && idx > 0) {
864                     dx = x;
865                     dy = y;
866                     dir = dirok[rn2(idx)];
867                     mz_move(dx, dy, dir);
868                     levl[dx][dy].typ = typ;
869                 }
870             }
871 }
872
873 /* Create a maze with specified corridor width and wall thickness
874  * TODO: rewrite walkfrom so it works on temp space, not levl
875  */
876 void
877 create_maze(corrwid, wallthick)
878 int corrwid;
879 int wallthick;
880 {
881     int x,y;
882     coord mm;
883     int tmp_xmax = x_maze_max;
884     int tmp_ymax = y_maze_max;
885     int rdx = 0;
886     int rdy = 0;
887     int scale;
888
889     if (wallthick < 1)
890         wallthick = 1;
891     else if (wallthick > 5)
892         wallthick = 5;
893
894     if (corrwid < 1)
895         corrwid = 1;
896     else if (corrwid > 5)
897         corrwid = 5;
898
899     scale = corrwid + wallthick;
900     rdx = (x_maze_max / scale);
901     rdy = (y_maze_max / scale);
902
903     if (level.flags.corrmaze)
904         for (x = 2; x < (rdx * 2); x++)
905             for (y = 2; y < (rdy * 2); y++)
906                 levl[x][y].typ = STONE;
907     else
908         for (x = 2; x <= (rdx * 2); x++)
909             for (y = 2; y <= (rdy * 2); y++)
910                 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
911
912     /* set upper bounds for maze0xy and walkfrom */
913     x_maze_max = (rdx * 2);
914     y_maze_max = (rdy * 2);
915
916     /* create maze */
917     maze0xy(&mm);
918     walkfrom((int) mm.x, (int) mm.y, 0);
919
920     if (!rn2(5))
921         maze_remove_deadends((level.flags.corrmaze) ? CORR : ROOM);
922
923     /* restore bounds */
924     x_maze_max = tmp_xmax;
925     y_maze_max = tmp_ymax;
926
927     /* scale maze up if needed */
928     if (scale > 2) {
929         char tmpmap[COLNO][ROWNO];
930         int rx = 1, ry = 1;
931
932         /* back up the existing smaller maze */
933         for (x = 1; x < x_maze_max; x++)
934             for (y = 1; y < y_maze_max; y++) {
935                 tmpmap[x][y] = levl[x][y].typ;
936             }
937
938         /* do the scaling */
939         rx = x = 2;
940         while (rx < x_maze_max) {
941             int mx = (x % 2) ? corrwid
942                              : ((x == 2 || x == (rdx * 2)) ? 1
943                                                            : wallthick);
944             ry = y = 2;
945             while (ry < y_maze_max) {
946                 int dx = 0, dy = 0;
947                 int my = (y % 2) ? corrwid
948                                  : ((y == 2 || y == (rdy * 2)) ? 1
949                                                                : wallthick);
950                 for (dx = 0; dx < mx; dx++)
951                     for (dy = 0; dy < my; dy++) {
952                         if (rx+dx >= x_maze_max
953                             || ry+dy >= y_maze_max)
954                             break;
955                         levl[rx + dx][ry + dy].typ = tmpmap[x][y];
956                     }
957                 ry += my;
958                 y++;
959             }
960             rx += mx;
961             x++;
962         }
963
964     }
965 }
966
967
968 void
969 makemaz(s)
970 const char *s;
971 {
972     int x, y;
973     char protofile[20];
974     s_level *sp = Is_special(&u.uz);
975     coord mm;
976
977     if (*s) {
978         if (sp && sp->rndlevs)
979             Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
980         else
981             Strcpy(protofile, s);
982     } else if (*(dungeons[u.uz.dnum].proto)) {
983         if (dunlevs_in_dungeon(&u.uz) > 1) {
984             if (sp && sp->rndlevs)
985                 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
986                         dunlev(&u.uz), rnd((int) sp->rndlevs));
987             else
988                 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
989                         dunlev(&u.uz));
990         } else if (sp && sp->rndlevs) {
991             Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
992                     rnd((int) sp->rndlevs));
993         } else
994             Strcpy(protofile, dungeons[u.uz.dnum].proto);
995
996     } else
997         Strcpy(protofile, "");
998
999     /* SPLEVTYPE format is "level-choice,level-choice"... */
1000     if (wizard && *protofile && sp && sp->rndlevs) {
1001         char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
1002         if (ep) {
1003             /* rindex always succeeds due to code in prior block */
1004             int len = (int) ((rindex(protofile, '-') - protofile) + 1);
1005
1006             while (ep && *ep) {
1007                 if (!strncmp(ep, protofile, len)) {
1008                     int pick = atoi(ep + len);
1009                     /* use choice only if valid */
1010                     if (pick > 0 && pick <= (int) sp->rndlevs)
1011                         Sprintf(protofile + len, "%d", pick);
1012                     break;
1013                 } else {
1014                     ep = index(ep, ',');
1015                     if (ep)
1016                         ++ep;
1017                 }
1018             }
1019         }
1020     }
1021
1022     if (*protofile) {
1023         check_ransacked(protofile);
1024         Strcat(protofile, LEV_EXT);
1025         if (load_special(protofile)) {
1026             /* some levels can end up with monsters
1027                on dead mon list, including light source monsters */
1028             dmonsfree();
1029             return; /* no mazification right now */
1030         }
1031         impossible("Couldn't load \"%s\" - making a maze.", protofile);
1032     }
1033
1034     level.flags.is_maze_lev = TRUE;
1035     level.flags.corrmaze = !rn2(3);
1036
1037     if (!Invocation_lev(&u.uz) && rn2(2)) {
1038         int corrscale = rnd(4);
1039         create_maze(corrscale,rnd(4)-corrscale);
1040     } else {
1041         create_maze(1,1);
1042     }
1043
1044     if (!level.flags.corrmaze)
1045         wallification(2, 2, x_maze_max, y_maze_max);
1046
1047     mazexy(&mm);
1048     mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
1049     if (!Invocation_lev(&u.uz)) {
1050         mazexy(&mm);
1051         mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
1052     } else { /* choose "vibrating square" location */
1053 #define x_maze_min 2
1054 #define y_maze_min 2
1055 /*
1056  * Pick a position where the stairs down to Moloch's Sanctum
1057  * level will ultimately be created.  At that time, an area
1058  * will be altered:  walls removed, moat and traps generated,
1059  * boulders destroyed.  The position picked here must ensure
1060  * that that invocation area won't extend off the map.
1061  *
1062  * We actually allow up to 2 squares around the usual edge of
1063  * the area to get truncated; see mkinvokearea(mklev.c).
1064  */
1065 #define INVPOS_X_MARGIN (6 - 2)
1066 #define INVPOS_Y_MARGIN (5 - 2)
1067 #define INVPOS_DISTANCE 11
1068         int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
1069             y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
1070
1071         if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
1072             || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
1073             debugpline2("inv_pos: maze is too small! (%d x %d)",
1074                         x_maze_max, y_maze_max);
1075         }
1076         inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
1077         do {
1078             x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
1079             y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
1080             /* we don't want it to be too near the stairs, nor
1081                to be on a spot that's already in use (wall|trap) */
1082         } while (x == xupstair || y == yupstair /*(direct line)*/
1083                  || abs(x - xupstair) == abs(y - yupstair)
1084                  || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
1085                  || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
1086         inv_pos.x = x;
1087         inv_pos.y = y;
1088         maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
1089 #undef INVPOS_X_MARGIN
1090 #undef INVPOS_Y_MARGIN
1091 #undef INVPOS_DISTANCE
1092 #undef x_maze_min
1093 #undef y_maze_min
1094     }
1095
1096     /* place branch stair or portal */
1097     place_branch(Is_branchlev(&u.uz), 0, 0);
1098
1099     for (x = rn1(8, 11); x; x--) {
1100         mazexy(&mm);
1101         (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
1102     }
1103     for (x = rn1(10, 2); x; x--) {
1104         mazexy(&mm);
1105         (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
1106     }
1107     for (x = rn2(3); x; x--) {
1108         mazexy(&mm);
1109         (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
1110     }
1111     for (x = rn1(5, 7); x; x--) {
1112         mazexy(&mm);
1113         (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
1114     }
1115     for (x = rn1(6, 7); x; x--) {
1116         mazexy(&mm);
1117         (void) mkgold(0L, mm.x, mm.y);
1118     }
1119     for (x = rn1(6, 7); x; x--)
1120         mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
1121 }
1122
1123 #ifdef MICRO
1124 /* Make the mazewalk iterative by faking a stack.  This is needed to
1125  * ensure the mazewalk is successful in the limited stack space of
1126  * the program.  This iterative version uses the minimum amount of stack
1127  * that is totally safe.
1128  */
1129 void
1130 walkfrom(x, y, typ)
1131 int x, y;
1132 schar typ;
1133 {
1134 #define CELLS (ROWNO * COLNO) / 4            /* a maze cell is 4 squares */
1135     char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
1136     int q, a, dir, pos;
1137     int dirs[4];
1138
1139     if (!typ) {
1140         if (level.flags.corrmaze)
1141             typ = CORR;
1142         else
1143             typ = ROOM;
1144     }
1145
1146     pos = 1;
1147     mazex[pos] = (char) x;
1148     mazey[pos] = (char) y;
1149     while (pos) {
1150         x = (int) mazex[pos];
1151         y = (int) mazey[pos];
1152         if (!IS_DOOR(levl[x][y].typ)) {
1153             /* might still be on edge of MAP, so don't overwrite */
1154             levl[x][y].typ = typ;
1155             levl[x][y].flags = 0;
1156         }
1157         q = 0;
1158         for (a = 0; a < 4; a++)
1159             if (okay(x, y, a))
1160                 dirs[q++] = a;
1161         if (!q)
1162             pos--;
1163         else {
1164             dir = dirs[rn2(q)];
1165             mz_move(x, y, dir);
1166             levl[x][y].typ = typ;
1167             mz_move(x, y, dir);
1168             pos++;
1169             if (pos > CELLS)
1170                 panic("Overflow in walkfrom");
1171             mazex[pos] = (char) x;
1172             mazey[pos] = (char) y;
1173         }
1174     }
1175 }
1176 #else /* !MICRO */
1177
1178 void
1179 walkfrom(x, y, typ)
1180 int x, y;
1181 schar typ;
1182 {
1183     int q, a, dir;
1184     int dirs[4];
1185
1186     if (!typ) {
1187         if (level.flags.corrmaze)
1188             typ = CORR;
1189         else
1190             typ = ROOM;
1191     }
1192
1193     if (!IS_DOOR(levl[x][y].typ)) {
1194         /* might still be on edge of MAP, so don't overwrite */
1195         levl[x][y].typ = typ;
1196         levl[x][y].flags = 0;
1197     }
1198
1199     while (1) {
1200         q = 0;
1201         for (a = 0; a < 4; a++)
1202             if (okay(x, y, a))
1203                 dirs[q++] = a;
1204         if (!q)
1205             return;
1206         dir = dirs[rn2(q)];
1207         mz_move(x, y, dir);
1208         levl[x][y].typ = typ;
1209         mz_move(x, y, dir);
1210         walkfrom(x, y, typ);
1211     }
1212 }
1213 #endif /* ?MICRO */
1214
1215 /* find random point in generated corridors,
1216    so we don't create items in moats, bunkers, or walls */
1217 void
1218 mazexy(cc)
1219 coord *cc;
1220 {
1221     int cpt = 0;
1222
1223     do {
1224         cc->x = 1 + rn2(x_maze_max);
1225         cc->y = 1 + rn2(y_maze_max);
1226         cpt++;
1227     } while (cpt < 100
1228              && levl[cc->x][cc->y].typ
1229                     != (level.flags.corrmaze ? CORR : ROOM));
1230     if (cpt >= 100) {
1231         int x, y;
1232
1233         /* last try */
1234         for (x = 1; x < x_maze_max; x++)
1235             for (y = 1; y < y_maze_max; y++) {
1236                 cc->x = x;
1237                 cc->y = y;
1238                 if (levl[cc->x][cc->y].typ
1239                     == (level.flags.corrmaze ? CORR : ROOM))
1240                     return;
1241             }
1242         panic("mazexy: can't find a place!");
1243     }
1244     return;
1245 }
1246
1247 /* put a non-diggable boundary around the initial portion of a level map.
1248  * assumes that no level will initially put things beyond the isok() range.
1249  *
1250  * we can't bound unconditionally on the last line with something in it,
1251  * because that something might be a niche which was already reachable,
1252  * so the boundary would be breached
1253  *
1254  * we can't bound unconditionally on one beyond the last line, because
1255  * that provides a window of abuse for wallified special levels
1256  */
1257 void
1258 bound_digging()
1259 {
1260     int x, y;
1261     unsigned typ;
1262     struct rm *lev;
1263     boolean found, nonwall;
1264     int xmin, xmax, ymin, ymax;
1265
1266     if (Is_earthlevel(&u.uz))
1267         return; /* everything diggable here */
1268
1269     found = nonwall = FALSE;
1270     for (xmin = 0; !found && xmin <= COLNO; xmin++) {
1271         lev = &levl[xmin][0];
1272         for (y = 0; y <= ROWNO - 1; y++, lev++) {
1273             typ = lev->typ;
1274             if (typ != STONE) {
1275                 found = TRUE;
1276                 if (!IS_WALL(typ))
1277                     nonwall = TRUE;
1278             }
1279         }
1280     }
1281     xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1282     if (xmin < 0)
1283         xmin = 0;
1284
1285     found = nonwall = FALSE;
1286     for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
1287         lev = &levl[xmax][0];
1288         for (y = 0; y <= ROWNO - 1; y++, lev++) {
1289             typ = lev->typ;
1290             if (typ != STONE) {
1291                 found = TRUE;
1292                 if (!IS_WALL(typ))
1293                     nonwall = TRUE;
1294             }
1295         }
1296     }
1297     xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1298     if (xmax >= COLNO)
1299         xmax = COLNO - 1;
1300
1301     found = nonwall = FALSE;
1302     for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
1303         lev = &levl[xmin][ymin];
1304         for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1305             typ = lev->typ;
1306             if (typ != STONE) {
1307                 found = TRUE;
1308                 if (!IS_WALL(typ))
1309                     nonwall = TRUE;
1310             }
1311         }
1312     }
1313     ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1314
1315     found = nonwall = FALSE;
1316     for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
1317         lev = &levl[xmin][ymax];
1318         for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1319             typ = lev->typ;
1320             if (typ != STONE) {
1321                 found = TRUE;
1322                 if (!IS_WALL(typ))
1323                     nonwall = TRUE;
1324             }
1325         }
1326     }
1327     ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1328
1329     for (x = 0; x < COLNO; x++)
1330         for (y = 0; y < ROWNO; y++)
1331             if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
1332 #ifdef DCC30_BUG
1333                 lev = &levl[x][y];
1334                 lev->wall_info |= W_NONDIGGABLE;
1335 #else
1336                 levl[x][y].wall_info |= W_NONDIGGABLE;
1337 #endif
1338             }
1339 }
1340
1341 void
1342 mkportal(x, y, todnum, todlevel)
1343 xchar x, y, todnum, todlevel;
1344 {
1345     /* a portal "trap" must be matched by a
1346        portal in the destination dungeon/dlevel */
1347     struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
1348
1349     if (!ttmp) {
1350         impossible("portal on top of portal??");
1351         return;
1352     }
1353     debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
1354                 dungeons[todnum].dname, todlevel);
1355     ttmp->dst.dnum = todnum;
1356     ttmp->dst.dlevel = todlevel;
1357     return;
1358 }
1359
1360 void
1361 fumaroles()
1362 {
1363     xchar n;
1364     boolean snd = FALSE, loud = FALSE;
1365
1366     for (n = rn2(3) + 2; n; n--) {
1367         xchar x = rn1(COLNO - 4, 3);
1368         xchar y = rn1(ROWNO - 4, 3);
1369
1370         if (levl[x][y].typ == LAVAPOOL) {
1371             NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
1372
1373             clear_heros_fault(r);
1374             snd = TRUE;
1375             if (distu(x, y) < 15)
1376                 loud = TRUE;
1377         }
1378     }
1379     if (snd && !Deaf)
1380         Norep("You hear a %swhoosh!", loud ? "loud " : "");
1381 }
1382
1383 /*
1384  * Special waterlevel stuff in endgame (TH).
1385  *
1386  * Some of these functions would probably logically belong to some
1387  * other source files, but they are all so nicely encapsulated here.
1388  */
1389
1390 static struct bubble *bbubbles, *ebubbles;
1391
1392 static struct trap *wportal;
1393 static int xmin, ymin, xmax, ymax; /* level boundaries */
1394 /* bubble movement boundaries */
1395 #define bxmin (xmin + 1)
1396 #define bymin (ymin + 1)
1397 #define bxmax (xmax - 1)
1398 #define bymax (ymax - 1)
1399
1400 STATIC_DCL void NDECL(set_wportal);
1401 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
1402 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
1403
1404 void
1405 movebubbles()
1406 {
1407     static boolean up;
1408     struct bubble *b;
1409     int x, y, i, j;
1410     struct trap *btrap;
1411     static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
1412                                          0, 0, 0, 0, 0, 0 };
1413     static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
1414                                        1, 0, 0, 0, 0 };
1415
1416     /* set up the portal the first time bubbles are moved */
1417     if (!wportal)
1418         set_wportal();
1419
1420     vision_recalc(2);
1421
1422     if (Is_waterlevel(&u.uz)) {
1423         /* keep attached ball&chain separate from bubble objects */
1424         if (Punished)
1425             unplacebc();
1426
1427         /*
1428          * Pick up everything inside of a bubble then fill all bubble
1429          * locations.
1430          */
1431         for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1432             if (b->cons)
1433                 panic("movebubbles: cons != null");
1434             for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1435                 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1436                     if (b->bm[j + 2] & (1 << i)) {
1437                         if (!isok(x, y)) {
1438                             impossible("movebubbles: bad pos (%d,%d)", x, y);
1439                             continue;
1440                         }
1441
1442                         /* pick up objects, monsters, hero, and traps */
1443                         if (OBJ_AT(x, y)) {
1444                             struct obj *olist = (struct obj *) 0, *otmp;
1445                             struct container *cons =
1446                                 (struct container *) alloc(
1447                                     sizeof(struct container));
1448
1449                             while ((otmp = level.objects[x][y]) != 0) {
1450                                 remove_object(otmp);
1451                                 otmp->ox = otmp->oy = 0;
1452                                 otmp->nexthere = olist;
1453                                 olist = otmp;
1454                             }
1455
1456                             cons->x = x;
1457                             cons->y = y;
1458                             cons->what = CONS_OBJ;
1459                             cons->list = (genericptr_t) olist;
1460                             cons->next = b->cons;
1461                             b->cons = cons;
1462                         }
1463                         if (MON_AT(x, y)) {
1464                             struct monst *mon = m_at(x, y);
1465                             struct container *cons =
1466                                 (struct container *) alloc(
1467                                     sizeof(struct container));
1468
1469                             cons->x = x;
1470                             cons->y = y;
1471                             cons->what = CONS_MON;
1472                             cons->list = (genericptr_t) mon;
1473
1474                             cons->next = b->cons;
1475                             b->cons = cons;
1476
1477                             if (mon->wormno)
1478                                 remove_worm(mon);
1479                             else
1480                                 remove_monster(x, y);
1481
1482                             newsym(x, y); /* clean up old position */
1483                             mon->mx = mon->my = 0;
1484                         }
1485                         if (!u.uswallow && x == u.ux && y == u.uy) {
1486                             struct container *cons =
1487                                 (struct container *) alloc(
1488                                     sizeof(struct container));
1489
1490                             cons->x = x;
1491                             cons->y = y;
1492                             cons->what = CONS_HERO;
1493                             cons->list = (genericptr_t) 0;
1494
1495                             cons->next = b->cons;
1496                             b->cons = cons;
1497                         }
1498                         if ((btrap = t_at(x, y)) != 0) {
1499                             struct container *cons =
1500                                 (struct container *) alloc(
1501                                     sizeof(struct container));
1502
1503                             cons->x = x;
1504                             cons->y = y;
1505                             cons->what = CONS_TRAP;
1506                             cons->list = (genericptr_t) btrap;
1507
1508                             cons->next = b->cons;
1509                             b->cons = cons;
1510                         }
1511
1512                         levl[x][y] = water_pos;
1513                         block_point(x, y);
1514                     }
1515         }
1516     } else if (Is_airlevel(&u.uz)) {
1517         for (x = 0; x < COLNO; x++)
1518             for (y = 0; y < ROWNO; y++) {
1519                 levl[x][y] = air_pos;
1520                 unblock_point(x, y);
1521             }
1522     }
1523
1524     /*
1525      * Every second time traverse down.  This is because otherwise
1526      * all the junk that changes owners when bubbles overlap
1527      * would eventually end up in the last bubble in the chain.
1528      */
1529     up = !up;
1530     for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1531         int rx = rn2(3), ry = rn2(3);
1532
1533         mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1534                   b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1535     }
1536
1537     /* put attached ball&chain back */
1538     if (Is_waterlevel(&u.uz) && Punished)
1539         placebc();
1540     vision_full_recalc = 1;
1541 }
1542
1543 /* when moving in water, possibly (1 in 3) alter the intended destination */
1544 void
1545 water_friction()
1546 {
1547     int x, y, dx, dy;
1548     boolean eff = FALSE;
1549
1550     if (Swimming && rn2(4))
1551         return; /* natural swimmers have advantage */
1552
1553     if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1554         /* cancel delta x and choose an arbitrary delta y value */
1555         x = u.ux;
1556         do {
1557             dy = rn2(3) - 1; /* -1, 0, 1 */
1558             y = u.uy + dy;
1559         } while (dy && (!isok(x, y) || !is_pool(x, y)));
1560         u.dx = 0;
1561         u.dy = dy;
1562         eff = TRUE;
1563     } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1564         /* cancel delta y and choose an arbitrary delta x value */
1565         y = u.uy;
1566         do {
1567             dx = rn2(3) - 1; /* -1 .. 1 */
1568             x = u.ux + dx;
1569         } while (dx && (!isok(x, y) || !is_pool(x, y)));
1570         u.dy = 0;
1571         u.dx = dx;
1572         eff = TRUE;
1573     }
1574     if (eff)
1575 /*JP
1576         pline("Water turbulence affects your movements.");
1577 */
1578         pline("\90\85\82ÃŒ\97¬\82ê\82ª\82 \82È\82½\82ÃŒ\93®\82«\82É\89e\8b¿\82ð\97^\82¦\82½\81D");
1579 }
1580
1581 void
1582 save_waterlevel(fd, mode)
1583 int fd, mode;
1584 {
1585     struct bubble *b;
1586
1587     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1588         return;
1589
1590     if (perform_bwrite(mode)) {
1591         int n = 0;
1592         for (b = bbubbles; b; b = b->next)
1593             ++n;
1594         bwrite(fd, (genericptr_t) &n, sizeof(int));
1595         bwrite(fd, (genericptr_t) &xmin, sizeof(int));
1596         bwrite(fd, (genericptr_t) &ymin, sizeof(int));
1597         bwrite(fd, (genericptr_t) &xmax, sizeof(int));
1598         bwrite(fd, (genericptr_t) &ymax, sizeof(int));
1599         for (b = bbubbles; b; b = b->next)
1600             bwrite(fd, (genericptr_t) b, sizeof(struct bubble));
1601     }
1602     if (release_data(mode))
1603         unsetup_waterlevel();
1604 }
1605
1606 void
1607 restore_waterlevel(fd)
1608 int fd;
1609 {
1610     struct bubble *b = (struct bubble *) 0, *btmp;
1611     int i, n;
1612
1613     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1614         return;
1615
1616     set_wportal();
1617     mread(fd, (genericptr_t) &n, sizeof(int));
1618     mread(fd, (genericptr_t) &xmin, sizeof(int));
1619     mread(fd, (genericptr_t) &ymin, sizeof(int));
1620     mread(fd, (genericptr_t) &xmax, sizeof(int));
1621     mread(fd, (genericptr_t) &ymax, sizeof(int));
1622     for (i = 0; i < n; i++) {
1623         btmp = b;
1624         b = (struct bubble *) alloc(sizeof(struct bubble));
1625         mread(fd, (genericptr_t) b, sizeof(struct bubble));
1626         if (bbubbles) {
1627             btmp->next = b;
1628             b->prev = btmp;
1629         } else {
1630             bbubbles = b;
1631             b->prev = (struct bubble *) 0;
1632         }
1633         mv_bubble(b, 0, 0, TRUE);
1634     }
1635     ebubbles = b;
1636     b->next = (struct bubble *) 0;
1637     was_waterlevel = TRUE;
1638 }
1639
1640 const char *
1641 waterbody_name(x, y)
1642 xchar x, y;
1643 {
1644     struct rm *lev;
1645     schar ltyp;
1646
1647     if (!isok(x, y))
1648         return "drink"; /* should never happen */
1649     lev = &levl[x][y];
1650     ltyp = lev->typ;
1651     if (ltyp == DRAWBRIDGE_UP)
1652         ltyp = db_under_typ(lev->drawbridgemask);
1653
1654     if (ltyp == LAVAPOOL)
1655 /*JP
1656         return hliquid("lava");
1657 */
1658         return hliquid("\97n\8aâ");
1659     else if (ltyp == ICE)
1660 /*JP
1661         return "ice";
1662 */
1663         return "\95X";
1664     else if (ltyp == POOL)
1665 /*JP
1666         return "pool of water";
1667 */
1668         return "\90\85\82½\82Ãœ\82è";
1669     else if (ltyp == WATER || Is_waterlevel(&u.uz))
1670         ; /* fall through to default return value */
1671     else if (Is_juiblex_level(&u.uz))
1672 /*JP
1673         return "swamp";
1674 */
1675         return "\8fÀ";
1676     else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1677 /*JP
1678         return "moat";
1679 */
1680         return "\96x";
1681
1682 /*JP
1683     return hliquid("water");
1684 */
1685     return hliquid("\90\85\92\86");
1686 }
1687
1688 STATIC_OVL void
1689 set_wportal()
1690 {
1691     /* there better be only one magic portal on water level... */
1692     for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1693         if (wportal->ttyp == MAGIC_PORTAL)
1694             return;
1695     impossible("set_wportal(): no portal!");
1696 }
1697
1698 STATIC_OVL void
1699 setup_waterlevel()
1700 {
1701     int x, y;
1702     int xskip, yskip;
1703     int water_glyph = cmap_to_glyph(S_water),
1704         air_glyph = cmap_to_glyph(S_air);
1705
1706     /* ouch, hardcoded... */
1707
1708     xmin = 3;
1709     ymin = 1;
1710     xmax = 78;
1711     ymax = 20;
1712
1713     /* set hero's memory to water */
1714
1715     for (x = xmin; x <= xmax; x++)
1716         for (y = ymin; y <= ymax; y++)
1717             levl[x][y].glyph = Is_waterlevel(&u.uz) ? water_glyph : air_glyph;
1718
1719     /* make bubbles */
1720
1721     if (Is_waterlevel(&u.uz)) {
1722         xskip = 10 + rn2(10);
1723         yskip = 4 + rn2(4);
1724     } else {
1725         xskip = 6 + rn2(4);
1726         yskip = 3 + rn2(3);
1727     }
1728
1729     for (x = bxmin; x <= bxmax; x += xskip)
1730         for (y = bymin; y <= bymax; y += yskip)
1731             mk_bubble(x, y, rn2(7));
1732 }
1733
1734 STATIC_OVL void
1735 unsetup_waterlevel()
1736 {
1737     struct bubble *b, *bb;
1738
1739     /* free bubbles */
1740
1741     for (b = bbubbles; b; b = bb) {
1742         bb = b->next;
1743         free((genericptr_t) b);
1744     }
1745     bbubbles = ebubbles = (struct bubble *) 0;
1746 }
1747
1748 STATIC_OVL void
1749 mk_bubble(x, y, n)
1750 int x, y, n;
1751 {
1752     /*
1753      * These bit masks make visually pleasing bubbles on a normal aspect
1754      * 25x80 terminal, which naturally results in them being mathematically
1755      * anything but symmetric.  For this reason they cannot be computed
1756      * in situ, either.  The first two elements tell the dimensions of
1757      * the bubble's bounding box.
1758      */
1759     static uchar bm2[] = { 2, 1, 0x3 },
1760                  bm3[] = { 3, 2, 0x7, 0x7 },
1761                  bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1762                  bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1763                  bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1764                  bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1765                  bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1766                  *bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1767     struct bubble *b;
1768
1769     if (x >= bxmax || y >= bymax)
1770         return;
1771     if (n >= SIZE(bmask)) {
1772         impossible("n too large (mk_bubble)");
1773         n = SIZE(bmask) - 1;
1774     }
1775     if (bmask[n][1] > MAX_BMASK) {
1776         panic("bmask size is larger than MAX_BMASK");
1777     }
1778     b = (struct bubble *) alloc(sizeof(struct bubble));
1779     if ((x + (int) bmask[n][0] - 1) > bxmax)
1780         x = bxmax - bmask[n][0] + 1;
1781     if ((y + (int) bmask[n][1] - 1) > bymax)
1782         y = bymax - bmask[n][1] + 1;
1783     b->x = x;
1784     b->y = y;
1785     b->dx = 1 - rn2(3);
1786     b->dy = 1 - rn2(3);
1787     /* y dimension is the length of bitmap data - see bmask above */
1788     (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1789                   (bmask[n][1] + 2) * sizeof(b->bm[0]));
1790     b->cons = 0;
1791     if (!bbubbles)
1792         bbubbles = b;
1793     if (ebubbles) {
1794         ebubbles->next = b;
1795         b->prev = ebubbles;
1796     } else
1797         b->prev = (struct bubble *) 0;
1798     b->next = (struct bubble *) 0;
1799     ebubbles = b;
1800     mv_bubble(b, 0, 0, TRUE);
1801 }
1802
1803 /*
1804  * The player, the portal and all other objects and monsters
1805  * float along with their associated bubbles.  Bubbles may overlap
1806  * freely, and the contents may get associated with other bubbles in
1807  * the process.  Bubbles are "sticky", meaning that if the player is
1808  * in the immediate neighborhood of one, he/she may get sucked inside.
1809  * This property also makes leaving a bubble slightly difficult.
1810  */
1811 STATIC_OVL void
1812 mv_bubble(b, dx, dy, ini)
1813 struct bubble *b;
1814 int dx, dy;
1815 boolean ini;
1816 {
1817     int x, y, i, j, colli = 0;
1818     struct container *cons, *ctemp;
1819
1820     /* clouds move slowly */
1821     if (!Is_airlevel(&u.uz) || !rn2(6)) {
1822         /* move bubble */
1823         if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1824             /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1825             dx = sgn(dx);
1826             dy = sgn(dy);
1827         }
1828
1829         /*
1830          * collision with level borders?
1831          *      1 = horizontal border, 2 = vertical, 3 = corner
1832          */
1833         if (b->x <= bxmin)
1834             colli |= 2;
1835         if (b->y <= bymin)
1836             colli |= 1;
1837         if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1838             colli |= 2;
1839         if ((int) (b->y + b->bm[1] - 1) >= bymax)
1840             colli |= 1;
1841
1842         if (b->x < bxmin) {
1843             pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1844             b->x = bxmin;
1845         }
1846         if (b->y < bymin) {
1847             pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1848             b->y = bymin;
1849         }
1850         if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1851             pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1852                   bxmax);
1853             b->x = bxmax - b->bm[0] + 1;
1854         }
1855         if ((int) (b->y + b->bm[1] - 1) > bymax) {
1856             pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1857                   bymax);
1858             b->y = bymax - b->bm[1] + 1;
1859         }
1860
1861         /* bounce if we're trying to move off the border */
1862         if (b->x == bxmin && dx < 0)
1863             dx = -dx;
1864         if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1865             dx = -dx;
1866         if (b->y == bymin && dy < 0)
1867             dy = -dy;
1868         if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1869             dy = -dy;
1870
1871         b->x += dx;
1872         b->y += dy;
1873     }
1874
1875     /* draw the bubbles */
1876     for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1877         for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1878             if (b->bm[j + 2] & (1 << i)) {
1879                 if (Is_waterlevel(&u.uz)) {
1880                     levl[x][y].typ = AIR;
1881                     levl[x][y].lit = 1;
1882                     unblock_point(x, y);
1883                 } else if (Is_airlevel(&u.uz)) {
1884                     levl[x][y].typ = CLOUD;
1885                     levl[x][y].lit = 1;
1886                     block_point(x, y);
1887                 }
1888             }
1889
1890     if (Is_waterlevel(&u.uz)) {
1891         /* replace contents of bubble */
1892         for (cons = b->cons; cons; cons = ctemp) {
1893             ctemp = cons->next;
1894             cons->x += dx;
1895             cons->y += dy;
1896
1897             switch (cons->what) {
1898             case CONS_OBJ: {
1899                 struct obj *olist, *otmp;
1900
1901                 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1902                     otmp = olist->nexthere;
1903                     place_object(olist, cons->x, cons->y);
1904                 }
1905                 break;
1906             }
1907
1908             case CONS_MON: {
1909                 struct monst *mon = (struct monst *) cons->list;
1910
1911                 (void) mnearto(mon, cons->x, cons->y, TRUE);
1912                 break;
1913             }
1914
1915             case CONS_HERO: {
1916                 int ux0 = u.ux, uy0 = u.uy;
1917
1918                 /* change u.ux0 and u.uy0? */
1919                 u.ux = cons->x;
1920                 u.uy = cons->y;
1921                 newsym(ux0, uy0); /* clean up old position */
1922
1923                 if (MON_AT(cons->x, cons->y)) {
1924                     mnexto(m_at(cons->x, cons->y));
1925                 }
1926                 break;
1927             }
1928
1929             case CONS_TRAP: {
1930                 struct trap *btrap = (struct trap *) cons->list;
1931                 btrap->tx = cons->x;
1932                 btrap->ty = cons->y;
1933                 break;
1934             }
1935
1936             default:
1937                 impossible("mv_bubble: unknown bubble contents");
1938                 break;
1939             }
1940             free((genericptr_t) cons);
1941         }
1942         b->cons = 0;
1943     }
1944
1945     /* boing? */
1946     switch (colli) {
1947     case 1:
1948         b->dy = -b->dy;
1949         break;
1950     case 3:
1951         b->dy = -b->dy; /* fall through */
1952     case 2:
1953         b->dx = -b->dx;
1954         break;
1955     default:
1956         /* sometimes alter direction for fun anyway
1957            (higher probability for stationary bubbles) */
1958         if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
1959             b->dx = 1 - rn2(3);
1960             b->dy = 1 - rn2(3);
1961         }
1962     }
1963 }
1964
1965 /*mkmaze.c*/