OSDN Git Service

patch artifact
[jnethack/source.git] / src / mkmaze.c
1 /* NetHack 3.6  mkmaze.c        $NHDT-Date: 1449269918 2015/12/04 22:58:38 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.42 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 #include "hack.h"
11 #include "sp_lev.h"
12 #include "lev.h" /* save & restore info */
13
14 /* from sp_lev.c, for fixup_special() */
15 extern lev_region *lregions;
16 extern int num_lregions;
17
18 STATIC_DCL boolean FDECL(iswall, (int, int));
19 STATIC_DCL boolean FDECL(iswall_or_stone, (int, int));
20 STATIC_DCL boolean FDECL(is_solid, (int, int));
21 STATIC_DCL int FDECL(extend_spine, (int[3][3], int, int, int));
22 STATIC_DCL boolean FDECL(okay, (int, int, int));
23 STATIC_DCL void FDECL(maze0xy, (coord *));
24 STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
25                                             XCHAR_P, XCHAR_P, XCHAR_P,
26                                             XCHAR_P, BOOLEAN_P, d_level *));
27 STATIC_DCL void NDECL(fixup_special);
28 STATIC_DCL void NDECL(setup_waterlevel);
29 STATIC_DCL void NDECL(unsetup_waterlevel);
30
31 /* adjust a coordinate one step in the specified direction */
32 #define mz_move(X, Y, dir) \
33     do {                                                         \
34         switch (dir) {                                           \
35         case 0:  --(Y);  break;                                  \
36         case 1:  (X)++;  break;                                  \
37         case 2:  (Y)++;  break;                                  \
38         case 3:  --(X);  break;                                  \
39         default: panic("mz_move: bad direction %d", dir);        \
40         }                                                        \
41     } while (0)
42
43 STATIC_OVL boolean
44 iswall(x, y)
45 int x, y;
46 {
47     register int type;
48
49     if (!isok(x, y))
50         return FALSE;
51     type = levl[x][y].typ;
52     return (boolean) (IS_WALL(type) || IS_DOOR(type)
53                       || type == SDOOR || type == IRONBARS);
54 }
55
56 STATIC_OVL boolean
57 iswall_or_stone(x, y)
58 int x, y;
59 {
60     register int type;
61
62     /* out of bounds = stone */
63     if (!isok(x, y))
64         return TRUE;
65
66     type = levl[x][y].typ;
67     return (boolean) (type == STONE
68                       || IS_WALL(type) || IS_DOOR(type)
69                       || type == SDOOR || type == IRONBARS);
70 }
71
72 /* return TRUE if out of bounds, wall or rock */
73 STATIC_OVL boolean
74 is_solid(x, y)
75 int x, y;
76 {
77     return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
78 }
79
80 /*
81  * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
82  * a wall spine in the (dx,dy) direction.  Return 0 otherwise.
83  *
84  * To extend a wall spine in that direction, first there must be a wall there.
85  * Then, extend a spine unless the current position is surrounded by walls
86  * in the direction given by (dx,dy).  E.g. if 'x' is our location, 'W'
87  * a wall, '.' a room, 'a' anything (we don't care), and our direction is
88  * (0,1) - South or down - then:
89  *
90  *              a a a
91  *              W x W           This would not extend a spine from x down
92  *              W W W           (a corridor of walls is formed).
93  *
94  *              a a a
95  *              W x W           This would extend a spine from x down.
96  *              . W W
97  */
98 STATIC_OVL int
99 extend_spine(locale, wall_there, dx, dy)
100 int locale[3][3];
101 int wall_there, dx, dy;
102 {
103     int spine, nx, ny;
104
105     nx = 1 + dx;
106     ny = 1 + dy;
107
108     if (wall_there) { /* wall in that direction */
109         if (dx) {
110             if (locale[1][0] && locale[1][2]         /* EW are wall/stone */
111                 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
112                 spine = 0;
113             } else {
114                 spine = 1;
115             }
116         } else { /* dy */
117             if (locale[0][1] && locale[2][1]         /* NS are wall/stone */
118                 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
119                 spine = 0;
120             } else {
121                 spine = 1;
122             }
123         }
124     } else {
125         spine = 0;
126     }
127
128     return spine;
129 }
130
131 /*
132  * Wall cleanup.  This function has two purposes: (1) remove walls that
133  * are totally surrounded by stone - they are redundant.  (2) correct
134  * the types so that they extend and connect to each other.
135  */
136 void
137 wallification(x1, y1, x2, y2)
138 int x1, y1, x2, y2;
139 {
140     uchar type;
141     register int x, y;
142     struct rm *lev;
143     int bits;
144     int locale[3][3]; /* rock or wall status surrounding positions */
145     /*
146      * Value 0 represents a free-standing wall.  It could be anything,
147      * so even though this table says VWALL, we actually leave whatever
148      * typ was there alone.
149      */
150     static xchar spine_array[16] = { VWALL, HWALL,    HWALL,    HWALL,
151                                      VWALL, TRCORNER, TLCORNER, TDWALL,
152                                      VWALL, BRCORNER, BLCORNER, TUWALL,
153                                      VWALL, TLWALL,   TRWALL,   CROSSWALL };
154
155     /* sanity check on incoming variables */
156     if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
157         panic("wallification: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
158
159     /* Step 1: change walls surrounded by rock to rock. */
160     for (x = x1; x <= x2; x++)
161         for (y = y1; y <= y2; y++) {
162             lev = &levl[x][y];
163             type = lev->typ;
164             if (IS_WALL(type) && type != DBWALL) {
165                 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
166                     && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
167                     && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
168                     && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
169                     lev->typ = STONE;
170             }
171         }
172
173     /*
174      * Step 2: set the correct wall type.  We can't combine steps
175      * 1 and 2 into a single sweep because we depend on knowing if
176      * the surrounding positions are stone.
177      */
178     for (x = x1; x <= x2; x++)
179         for (y = y1; y <= y2; y++) {
180             lev = &levl[x][y];
181             type = lev->typ;
182             if (!(IS_WALL(type) && type != DBWALL))
183                 continue;
184
185             /* set the locations TRUE if rock or wall or out of bounds */
186             locale[0][0] = iswall_or_stone(x - 1, y - 1);
187             locale[1][0] = iswall_or_stone(x, y - 1);
188             locale[2][0] = iswall_or_stone(x + 1, y - 1);
189
190             locale[0][1] = iswall_or_stone(x - 1, y);
191             locale[2][1] = iswall_or_stone(x + 1, y);
192
193             locale[0][2] = iswall_or_stone(x - 1, y + 1);
194             locale[1][2] = iswall_or_stone(x, y + 1);
195             locale[2][2] = iswall_or_stone(x + 1, y + 1);
196
197             /* determine if wall should extend to each direction NSEW */
198             bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
199                    | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
200                    | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
201                    | extend_spine(locale, iswall(x - 1, y), -1, 0);
202
203             /* don't change typ if wall is free-standing */
204             if (bits)
205                 lev->typ = spine_array[bits];
206         }
207 }
208
209 STATIC_OVL boolean
210 okay(x, y, dir)
211 int x, y;
212 register int dir;
213 {
214     mz_move(x, y, dir);
215     mz_move(x, y, dir);
216     if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max
217         || levl[x][y].typ != 0)
218         return FALSE;
219     return TRUE;
220 }
221
222 /* find random starting point for maze generation */
223 STATIC_OVL void
224 maze0xy(cc)
225 coord *cc;
226 {
227     cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
228     cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
229     return;
230 }
231
232 /*
233  * Bad if:
234  *      pos is occupied OR
235  *      pos is inside restricted region (lx,ly,hx,hy) OR
236  *      NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
237  */
238 boolean
239 bad_location(x, y, lx, ly, hx, hy)
240 xchar x, y;
241 xchar lx, ly, hx, hy;
242 {
243     return (boolean) (occupied(x, y)
244                       || within_bounded_area(x, y, lx, ly, hx, hy)
245                       || !((levl[x][y].typ == CORR && level.flags.is_maze_lev)
246                            || levl[x][y].typ == ROOM
247                            || levl[x][y].typ == AIR));
248 }
249
250 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
251    and place something (based on rtype) in that region */
252 void
253 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
254 xchar lx, ly, hx, hy;
255 xchar nlx, nly, nhx, nhy;
256 xchar rtype;
257 d_level *lev;
258 {
259     int trycnt;
260     boolean oneshot;
261     xchar x, y;
262
263     if (!lx) { /* default to whole level */
264         /*
265          * if there are rooms and this a branch, let place_branch choose
266          * the branch location (to avoid putting branches in corridors).
267          */
268         if (rtype == LR_BRANCH && nroom) {
269             place_branch(Is_branchlev(&u.uz), 0, 0);
270             return;
271         }
272
273         lx = 1;
274         hx = COLNO - 1;
275         ly = 1;
276         hy = ROWNO - 1;
277     }
278
279     /* first a probabilistic approach */
280
281     oneshot = (lx == hx && ly == hy);
282     for (trycnt = 0; trycnt < 200; trycnt++) {
283         x = rn1((hx - lx) + 1, lx);
284         y = rn1((hy - ly) + 1, ly);
285         if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
286             return;
287     }
288
289     /* then a deterministic one */
290
291     oneshot = TRUE;
292     for (x = lx; x <= hx; x++)
293         for (y = ly; y <= hy; y++)
294             if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot,
295                                  lev))
296                 return;
297
298     impossible("Couldn't place lregion type %d!", rtype);
299 }
300
301 STATIC_OVL boolean
302 put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev)
303 xchar x, y;
304 xchar nlx, nly, nhx, nhy;
305 xchar rtype;
306 boolean oneshot;
307 d_level *lev;
308 {
309     if (bad_location(x, y, nlx, nly, nhx, nhy)) {
310         if (!oneshot) {
311             return FALSE; /* caller should try again */
312         } else {
313             /* Must make do with the only location possible;
314                avoid failure due to a misplaced trap.
315                It might still fail if there's a dungeon feature here. */
316             struct trap *t = t_at(x, y);
317
318             if (t && t->ttyp != MAGIC_PORTAL && t->ttyp != VIBRATING_SQUARE)
319                 deltrap(t);
320             if (bad_location(x, y, nlx, nly, nhx, nhy))
321                 return FALSE;
322         }
323     }
324     switch (rtype) {
325     case LR_TELE:
326     case LR_UPTELE:
327     case LR_DOWNTELE:
328         /* "something" means the player in this case */
329         if (MON_AT(x, y)) {
330             /* move the monster if no choice, or just try again */
331             if (oneshot)
332                 (void) rloc(m_at(x, y), FALSE);
333             else
334                 return FALSE;
335         }
336         u_on_newpos(x, y);
337         break;
338     case LR_PORTAL:
339         mkportal(x, y, lev->dnum, lev->dlevel);
340         break;
341     case LR_DOWNSTAIR:
342     case LR_UPSTAIR:
343         mkstairs(x, y, (char) rtype, (struct mkroom *) 0);
344         break;
345     case LR_BRANCH:
346         place_branch(Is_branchlev(&u.uz), x, y);
347         break;
348     }
349     return TRUE;
350 }
351
352 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
353
354 /* this is special stuff that the level compiler cannot (yet) handle */
355 STATIC_OVL void
356 fixup_special()
357 {
358     register lev_region *r = lregions;
359     struct d_level lev;
360     register int x, y;
361     struct mkroom *croom;
362     boolean added_branch = FALSE;
363
364     if (was_waterlevel) {
365         was_waterlevel = FALSE;
366         u.uinwater = 0;
367         unsetup_waterlevel();
368     }
369     if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
370         level.flags.hero_memory = 0;
371         was_waterlevel = TRUE;
372         /* water level is an odd beast - it has to be set up
373            before calling place_lregions etc. */
374         setup_waterlevel();
375     }
376     for (x = 0; x < num_lregions; x++, r++) {
377         switch (r->rtype) {
378         case LR_BRANCH:
379             added_branch = TRUE;
380             goto place_it;
381
382         case LR_PORTAL:
383             if (*r->rname.str >= '0' && *r->rname.str <= '9') {
384                 /* "chutes and ladders" */
385                 lev = u.uz;
386                 lev.dlevel = atoi(r->rname.str);
387             } else {
388                 s_level *sp = find_level(r->rname.str);
389                 lev = sp->dlevel;
390             }
391         /* fall into... */
392
393         case LR_UPSTAIR:
394         case LR_DOWNSTAIR:
395         place_it:
396             place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
397                           r->inarea.y2, r->delarea.x1, r->delarea.y1,
398                           r->delarea.x2, r->delarea.y2, r->rtype, &lev);
399             break;
400
401         case LR_TELE:
402         case LR_UPTELE:
403         case LR_DOWNTELE:
404             /* save the region outlines for goto_level() */
405             if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
406                 updest.lx = r->inarea.x1;
407                 updest.ly = r->inarea.y1;
408                 updest.hx = r->inarea.x2;
409                 updest.hy = r->inarea.y2;
410                 updest.nlx = r->delarea.x1;
411                 updest.nly = r->delarea.y1;
412                 updest.nhx = r->delarea.x2;
413                 updest.nhy = r->delarea.y2;
414             }
415             if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
416                 dndest.lx = r->inarea.x1;
417                 dndest.ly = r->inarea.y1;
418                 dndest.hx = r->inarea.x2;
419                 dndest.hy = r->inarea.y2;
420                 dndest.nlx = r->delarea.x1;
421                 dndest.nly = r->delarea.y1;
422                 dndest.nhx = r->delarea.x2;
423                 dndest.nhy = r->delarea.y2;
424             }
425             /* place_lregion gets called from goto_level() */
426             break;
427         }
428
429         if (r->rname.str)
430             free((genericptr_t) r->rname.str), r->rname.str = 0;
431     }
432
433     /* place dungeon branch if not placed above */
434     if (!added_branch && Is_branchlev(&u.uz)) {
435         place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
436     }
437
438     /* Still need to add some stuff to level file */
439     if (Is_medusa_level(&u.uz)) {
440         struct obj *otmp;
441         int tryct;
442
443         croom = &rooms[0]; /* only one room on the medusa level */
444         for (tryct = rnd(4); tryct; tryct--) {
445             x = somex(croom);
446             y = somey(croom);
447             if (goodpos(x, y, (struct monst *) 0, 0)) {
448                 otmp = mk_tt_object(STATUE, x, y);
449                 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm])
450                                 || pm_resistance(&mons[otmp->corpsenm],
451                                                  MR_STONE))) {
452                     /* set_corpsenm() handles weight too */
453                     set_corpsenm(otmp, rndmonnum());
454                 }
455             }
456         }
457
458         if (rn2(2))
459             otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
460         else /* Medusa statues don't contain books */
461             otmp =
462                 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
463                            somex(croom), somey(croom), CORPSTAT_NONE);
464         if (otmp) {
465             while (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
466                    || poly_when_stoned(&mons[otmp->corpsenm])) {
467                 /* set_corpsenm() handles weight too */
468                 set_corpsenm(otmp, rndmonnum());
469             }
470         }
471     } else if (Is_wiz1_level(&u.uz)) {
472         croom = search_special(MORGUE);
473
474         create_secret_door(croom, W_SOUTH | W_EAST | W_WEST);
475     } else if (Is_knox(&u.uz)) {
476         /* using an unfilled morgue for rm id */
477         croom = search_special(MORGUE);
478         /* avoid inappropriate morgue-related messages */
479         level.flags.graveyard = level.flags.has_morgue = 0;
480         croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
481         /* stock the main vault */
482         for (x = croom->lx; x <= croom->hx; x++)
483             for (y = croom->ly; y <= croom->hy; y++) {
484                 (void) mkgold((long) rn1(300, 600), x, y);
485                 if (!rn2(3) && !is_pool(x, y))
486                     (void) maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
487             }
488     } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
489         /* less chance for undead corpses (lured from lower morgues) */
490         level.flags.graveyard = 1;
491     } else if (Is_stronghold(&u.uz)) {
492         level.flags.graveyard = 1;
493     } else if (Is_sanctum(&u.uz)) {
494         croom = search_special(TEMPLE);
495
496         create_secret_door(croom, W_ANY);
497     } else if (on_level(&u.uz, &orcus_level)) {
498         register struct monst *mtmp, *mtmp2;
499
500         /* it's a ghost town, get rid of shopkeepers */
501         for (mtmp = fmon; mtmp; mtmp = mtmp2) {
502             mtmp2 = mtmp->nmon;
503             if (mtmp->isshk)
504                 mongone(mtmp);
505         }
506     }
507
508     if (lregions)
509         free((genericptr_t) lregions), lregions = 0;
510     num_lregions = 0;
511 }
512
513 void
514 makemaz(s)
515 register const char *s;
516 {
517     int x, y;
518     char protofile[20];
519     s_level *sp = Is_special(&u.uz);
520     coord mm;
521
522     if (*s) {
523         if (sp && sp->rndlevs)
524             Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
525         else
526             Strcpy(protofile, s);
527     } else if (*(dungeons[u.uz.dnum].proto)) {
528         if (dunlevs_in_dungeon(&u.uz) > 1) {
529             if (sp && sp->rndlevs)
530                 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
531                         dunlev(&u.uz), rnd((int) sp->rndlevs));
532             else
533                 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
534                         dunlev(&u.uz));
535         } else if (sp && sp->rndlevs) {
536             Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
537                     rnd((int) sp->rndlevs));
538         } else
539             Strcpy(protofile, dungeons[u.uz.dnum].proto);
540
541     } else
542         Strcpy(protofile, "");
543
544     /* SPLEVTYPE format is "level-choice,level-choice"... */
545     if (wizard && *protofile && sp && sp->rndlevs) {
546         char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
547         if (ep) {
548             /* rindex always succeeds due to code in prior block */
549             int len = (int) ((rindex(protofile, '-') - protofile) + 1);
550
551             while (ep && *ep) {
552                 if (!strncmp(ep, protofile, len)) {
553                     int pick = atoi(ep + len);
554                     /* use choice only if valid */
555                     if (pick > 0 && pick <= (int) sp->rndlevs)
556                         Sprintf(protofile + len, "%d", pick);
557                     break;
558                 } else {
559                     ep = index(ep, ',');
560                     if (ep)
561                         ++ep;
562                 }
563             }
564         }
565     }
566
567     if (*protofile) {
568         Strcat(protofile, LEV_EXT);
569         if (load_special(protofile)) {
570             fixup_special();
571             /* some levels can end up with monsters
572                on dead mon list, including light source monsters */
573             dmonsfree();
574             return; /* no mazification right now */
575         }
576         impossible("Couldn't load \"%s\" - making a maze.", protofile);
577     }
578
579     level.flags.is_maze_lev = TRUE;
580     level.flags.corrmaze = !rn2(3);
581
582     if (level.flags.corrmaze)
583         for (x = 2; x < x_maze_max; x++)
584             for (y = 2; y < y_maze_max; y++)
585                 levl[x][y].typ = STONE;
586     else
587         for (x = 2; x <= x_maze_max; x++)
588             for (y = 2; y <= y_maze_max; y++)
589                 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
590
591     maze0xy(&mm);
592     walkfrom((int) mm.x, (int) mm.y, 0);
593     /* put a boulder at the maze center */
594     (void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE, FALSE);
595
596     if (!level.flags.corrmaze)
597         wallification(2, 2, x_maze_max, y_maze_max);
598
599     mazexy(&mm);
600     mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
601     if (!Invocation_lev(&u.uz)) {
602         mazexy(&mm);
603         mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
604     } else { /* choose "vibrating square" location */
605 #define x_maze_min 2
606 #define y_maze_min 2
607 /*
608  * Pick a position where the stairs down to Moloch's Sanctum
609  * level will ultimately be created.  At that time, an area
610  * will be altered:  walls removed, moat and traps generated,
611  * boulders destroyed.  The position picked here must ensure
612  * that that invocation area won't extend off the map.
613  *
614  * We actually allow up to 2 squares around the usual edge of
615  * the area to get truncated; see mkinvokearea(mklev.c).
616  */
617 #define INVPOS_X_MARGIN (6 - 2)
618 #define INVPOS_Y_MARGIN (5 - 2)
619 #define INVPOS_DISTANCE 11
620         int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
621             y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
622
623         if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
624             || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
625             debugpline2("inv_pos: maze is too small! (%d x %d)",
626                         x_maze_max, y_maze_max);
627         }
628         inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
629         do {
630             x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
631             y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
632             /* we don't want it to be too near the stairs, nor
633                to be on a spot that's already in use (wall|trap) */
634         } while (x == xupstair || y == yupstair /*(direct line)*/
635                  || abs(x - xupstair) == abs(y - yupstair)
636                  || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
637                  || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
638         inv_pos.x = x;
639         inv_pos.y = y;
640         maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
641 #undef INVPOS_X_MARGIN
642 #undef INVPOS_Y_MARGIN
643 #undef INVPOS_DISTANCE
644 #undef x_maze_min
645 #undef y_maze_min
646     }
647
648     /* place branch stair or portal */
649     place_branch(Is_branchlev(&u.uz), 0, 0);
650
651     for (x = rn1(8, 11); x; x--) {
652         mazexy(&mm);
653         (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
654     }
655     for (x = rn1(10, 2); x; x--) {
656         mazexy(&mm);
657         (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
658     }
659     for (x = rn2(3); x; x--) {
660         mazexy(&mm);
661         (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
662     }
663     for (x = rn1(5, 7); x; x--) {
664         mazexy(&mm);
665         (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
666     }
667     for (x = rn1(6, 7); x; x--) {
668         mazexy(&mm);
669         (void) mkgold(0L, mm.x, mm.y);
670     }
671     for (x = rn1(6, 7); x; x--)
672         mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
673 }
674
675 #ifdef MICRO
676 /* Make the mazewalk iterative by faking a stack.  This is needed to
677  * ensure the mazewalk is successful in the limited stack space of
678  * the program.  This iterative version uses the minimum amount of stack
679  * that is totally safe.
680  */
681 void
682 walkfrom(x, y, typ)
683 int x, y;
684 schar typ;
685 {
686 #define CELLS (ROWNO * COLNO) / 4            /* a maze cell is 4 squares */
687     char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
688     int q, a, dir, pos;
689     int dirs[4];
690
691     if (!typ) {
692         if (level.flags.corrmaze)
693             typ = CORR;
694         else
695             typ = ROOM;
696     }
697
698     pos = 1;
699     mazex[pos] = (char) x;
700     mazey[pos] = (char) y;
701     while (pos) {
702         x = (int) mazex[pos];
703         y = (int) mazey[pos];
704         if (!IS_DOOR(levl[x][y].typ)) {
705             /* might still be on edge of MAP, so don't overwrite */
706             levl[x][y].typ = typ;
707             levl[x][y].flags = 0;
708         }
709         q = 0;
710         for (a = 0; a < 4; a++)
711             if (okay(x, y, a))
712                 dirs[q++] = a;
713         if (!q)
714             pos--;
715         else {
716             dir = dirs[rn2(q)];
717             mz_move(x, y, dir);
718             levl[x][y].typ = typ;
719             mz_move(x, y, dir);
720             pos++;
721             if (pos > CELLS)
722                 panic("Overflow in walkfrom");
723             mazex[pos] = (char) x;
724             mazey[pos] = (char) y;
725         }
726     }
727 }
728 #else /* !MICRO */
729
730 void
731 walkfrom(x, y, typ)
732 int x, y;
733 schar typ;
734 {
735     register int q, a, dir;
736     int dirs[4];
737
738     if (!typ) {
739         if (level.flags.corrmaze)
740             typ = CORR;
741         else
742             typ = ROOM;
743     }
744
745     if (!IS_DOOR(levl[x][y].typ)) {
746         /* might still be on edge of MAP, so don't overwrite */
747         levl[x][y].typ = typ;
748         levl[x][y].flags = 0;
749     }
750
751     while (1) {
752         q = 0;
753         for (a = 0; a < 4; a++)
754             if (okay(x, y, a))
755                 dirs[q++] = a;
756         if (!q)
757             return;
758         dir = dirs[rn2(q)];
759         mz_move(x, y, dir);
760         levl[x][y].typ = typ;
761         mz_move(x, y, dir);
762         walkfrom(x, y, typ);
763     }
764 }
765 #endif /* ?MICRO */
766
767 /* find random point in generated corridors,
768    so we don't create items in moats, bunkers, or walls */
769 void
770 mazexy(cc)
771 coord *cc;
772 {
773     int cpt = 0;
774
775     do {
776         cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
777         cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
778         cpt++;
779     } while (cpt < 100
780              && levl[cc->x][cc->y].typ
781                     != (level.flags.corrmaze ? CORR : ROOM));
782     if (cpt >= 100) {
783         register int x, y;
784         /* last try */
785         for (x = 0; x < (x_maze_max >> 1) - 1; x++)
786             for (y = 0; y < (y_maze_max >> 1) - 1; y++) {
787                 cc->x = 3 + 2 * x;
788                 cc->y = 3 + 2 * y;
789                 if (levl[cc->x][cc->y].typ
790                     == (level.flags.corrmaze ? CORR : ROOM))
791                     return;
792             }
793         panic("mazexy: can't find a place!");
794     }
795     return;
796 }
797
798 /* put a non-diggable boundary around the initial portion of a level map.
799  * assumes that no level will initially put things beyond the isok() range.
800  *
801  * we can't bound unconditionally on the last line with something in it,
802  * because that something might be a niche which was already reachable,
803  * so the boundary would be breached
804  *
805  * we can't bound unconditionally on one beyond the last line, because
806  * that provides a window of abuse for wallified special levels
807  */
808 void
809 bound_digging()
810 {
811     register int x, y;
812     register unsigned typ;
813     register struct rm *lev;
814     boolean found, nonwall;
815     int xmin, xmax, ymin, ymax;
816
817     if (Is_earthlevel(&u.uz))
818         return; /* everything diggable here */
819
820     found = nonwall = FALSE;
821     for (xmin = 0; !found && xmin <= COLNO; xmin++) {
822         lev = &levl[xmin][0];
823         for (y = 0; y <= ROWNO - 1; y++, lev++) {
824             typ = lev->typ;
825             if (typ != STONE) {
826                 found = TRUE;
827                 if (!IS_WALL(typ))
828                     nonwall = TRUE;
829             }
830         }
831     }
832     xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
833     if (xmin < 0)
834         xmin = 0;
835
836     found = nonwall = FALSE;
837     for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
838         lev = &levl[xmax][0];
839         for (y = 0; y <= ROWNO - 1; y++, lev++) {
840             typ = lev->typ;
841             if (typ != STONE) {
842                 found = TRUE;
843                 if (!IS_WALL(typ))
844                     nonwall = TRUE;
845             }
846         }
847     }
848     xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
849     if (xmax >= COLNO)
850         xmax = COLNO - 1;
851
852     found = nonwall = FALSE;
853     for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
854         lev = &levl[xmin][ymin];
855         for (x = xmin; x <= xmax; x++, lev += ROWNO) {
856             typ = lev->typ;
857             if (typ != STONE) {
858                 found = TRUE;
859                 if (!IS_WALL(typ))
860                     nonwall = TRUE;
861             }
862         }
863     }
864     ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
865
866     found = nonwall = FALSE;
867     for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
868         lev = &levl[xmin][ymax];
869         for (x = xmin; x <= xmax; x++, lev += ROWNO) {
870             typ = lev->typ;
871             if (typ != STONE) {
872                 found = TRUE;
873                 if (!IS_WALL(typ))
874                     nonwall = TRUE;
875             }
876         }
877     }
878     ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
879
880     for (x = 0; x < COLNO; x++)
881         for (y = 0; y < ROWNO; y++)
882             if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
883 #ifdef DCC30_BUG
884                 lev = &levl[x][y];
885                 lev->wall_info |= W_NONDIGGABLE;
886 #else
887                 levl[x][y].wall_info |= W_NONDIGGABLE;
888 #endif
889             }
890 }
891
892 void
893 mkportal(x, y, todnum, todlevel)
894 xchar x, y, todnum, todlevel;
895 {
896     /* a portal "trap" must be matched by a
897        portal in the destination dungeon/dlevel */
898     struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
899
900     if (!ttmp) {
901         impossible("portal on top of portal??");
902         return;
903     }
904     debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
905                 dungeons[todnum].dname, todlevel);
906     ttmp->dst.dnum = todnum;
907     ttmp->dst.dlevel = todlevel;
908     return;
909 }
910
911 void
912 fumaroles()
913 {
914     xchar n;
915     boolean snd = FALSE, loud = FALSE;
916
917     for (n = rn2(3) + 2; n; n--) {
918         xchar x = rn1(COLNO - 4, 3);
919         xchar y = rn1(ROWNO - 4, 3);
920         if (levl[x][y].typ == LAVAPOOL) {
921             NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
922             clear_heros_fault(r);
923             snd = TRUE;
924             if (distu(x, y) < 15)
925                 loud = TRUE;
926         }
927     }
928     if (snd && !Deaf)
929         Norep("You hear a %swhoosh!", loud ? "loud " : "");
930 }
931
932 /*
933  * Special waterlevel stuff in endgame (TH).
934  *
935  * Some of these functions would probably logically belong to some
936  * other source files, but they are all so nicely encapsulated here.
937  */
938
939 #ifdef DEBUG
940 /* to ease the work of debuggers at this stage */
941 #define register
942 #endif
943
944 #define CONS_OBJ 0
945 #define CONS_MON 1
946 #define CONS_HERO 2
947 #define CONS_TRAP 3
948
949 static struct bubble *bbubbles, *ebubbles;
950
951 static struct trap *wportal;
952 static int xmin, ymin, xmax, ymax; /* level boundaries */
953 /* bubble movement boundaries */
954 #define bxmin (xmin + 1)
955 #define bymin (ymin + 1)
956 #define bxmax (xmax - 1)
957 #define bymax (ymax - 1)
958
959 STATIC_DCL void NDECL(set_wportal);
960 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
961 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
962
963 void
964 movebubbles()
965 {
966     static boolean up;
967     register struct bubble *b;
968     register int x, y, i, j;
969     struct trap *btrap;
970     static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
971                                          0, 0, 0, 0, 0, 0 };
972     static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
973                                        1, 0, 0, 0, 0 };
974
975     /* set up the portal the first time bubbles are moved */
976     if (!wportal)
977         set_wportal();
978
979     vision_recalc(2);
980
981     if (Is_waterlevel(&u.uz)) {
982         /* keep attached ball&chain separate from bubble objects */
983         if (Punished)
984             unplacebc();
985
986         /*
987          * Pick up everything inside of a bubble then fill all bubble
988          * locations.
989          */
990         for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
991             if (b->cons)
992                 panic("movebubbles: cons != null");
993             for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
994                 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
995                     if (b->bm[j + 2] & (1 << i)) {
996                         if (!isok(x, y)) {
997                             impossible("movebubbles: bad pos (%d,%d)", x, y);
998                             continue;
999                         }
1000
1001                         /* pick up objects, monsters, hero, and traps */
1002                         if (OBJ_AT(x, y)) {
1003                             struct obj *olist = (struct obj *) 0, *otmp;
1004                             struct container *cons =
1005                                 (struct container *) alloc(
1006                                     sizeof(struct container));
1007
1008                             while ((otmp = level.objects[x][y]) != 0) {
1009                                 remove_object(otmp);
1010                                 otmp->ox = otmp->oy = 0;
1011                                 otmp->nexthere = olist;
1012                                 olist = otmp;
1013                             }
1014
1015                             cons->x = x;
1016                             cons->y = y;
1017                             cons->what = CONS_OBJ;
1018                             cons->list = (genericptr_t) olist;
1019                             cons->next = b->cons;
1020                             b->cons = cons;
1021                         }
1022                         if (MON_AT(x, y)) {
1023                             struct monst *mon = m_at(x, y);
1024                             struct container *cons =
1025                                 (struct container *) alloc(
1026                                     sizeof(struct container));
1027
1028                             cons->x = x;
1029                             cons->y = y;
1030                             cons->what = CONS_MON;
1031                             cons->list = (genericptr_t) mon;
1032
1033                             cons->next = b->cons;
1034                             b->cons = cons;
1035
1036                             if (mon->wormno)
1037                                 remove_worm(mon);
1038                             else
1039                                 remove_monster(x, y);
1040
1041                             newsym(x, y); /* clean up old position */
1042                             mon->mx = mon->my = 0;
1043                         }
1044                         if (!u.uswallow && x == u.ux && y == u.uy) {
1045                             struct container *cons =
1046                                 (struct container *) alloc(
1047                                     sizeof(struct container));
1048
1049                             cons->x = x;
1050                             cons->y = y;
1051                             cons->what = CONS_HERO;
1052                             cons->list = (genericptr_t) 0;
1053
1054                             cons->next = b->cons;
1055                             b->cons = cons;
1056                         }
1057                         if ((btrap = t_at(x, y)) != 0) {
1058                             struct container *cons =
1059                                 (struct container *) alloc(
1060                                     sizeof(struct container));
1061
1062                             cons->x = x;
1063                             cons->y = y;
1064                             cons->what = CONS_TRAP;
1065                             cons->list = (genericptr_t) btrap;
1066
1067                             cons->next = b->cons;
1068                             b->cons = cons;
1069                         }
1070
1071                         levl[x][y] = water_pos;
1072                         block_point(x, y);
1073                     }
1074         }
1075     } else if (Is_airlevel(&u.uz)) {
1076         for (x = 0; x < COLNO; x++)
1077             for (y = 0; y < ROWNO; y++) {
1078                 levl[x][y] = air_pos;
1079                 unblock_point(x, y);
1080             }
1081     }
1082
1083     /*
1084      * Every second time traverse down.  This is because otherwise
1085      * all the junk that changes owners when bubbles overlap
1086      * would eventually end up in the last bubble in the chain.
1087      */
1088     up = !up;
1089     for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1090         register int rx = rn2(3), ry = rn2(3);
1091
1092         mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1093                   b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1094     }
1095
1096     /* put attached ball&chain back */
1097     if (Is_waterlevel(&u.uz) && Punished)
1098         placebc();
1099     vision_full_recalc = 1;
1100 }
1101
1102 /* when moving in water, possibly (1 in 3) alter the intended destination */
1103 void
1104 water_friction()
1105 {
1106     register int x, y, dx, dy;
1107     register boolean eff = FALSE;
1108
1109     if (Swimming && rn2(4))
1110         return; /* natural swimmers have advantage */
1111
1112     if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1113         /* cancel delta x and choose an arbitrary delta y value */
1114         x = u.ux;
1115         do {
1116             dy = rn2(3) - 1; /* -1, 0, 1 */
1117             y = u.uy + dy;
1118         } while (dy && (!isok(x, y) || !is_pool(x, y)));
1119         u.dx = 0;
1120         u.dy = dy;
1121         eff = TRUE;
1122     } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1123         /* cancel delta y and choose an arbitrary delta x value */
1124         y = u.uy;
1125         do {
1126             dx = rn2(3) - 1; /* -1 .. 1 */
1127             x = u.ux + dx;
1128         } while (dx && (!isok(x, y) || !is_pool(x, y)));
1129         u.dy = 0;
1130         u.dx = dx;
1131         eff = TRUE;
1132     }
1133     if (eff)
1134 /*JP
1135         pline("Water turbulence affects your movements.");
1136 */
1137         pline("\90\85\82ÃŒ\97¬\82ê\82ª\82 \82È\82½\82ÃŒ\93®\82«\82É\89e\8b¿\82ð\97^\82¦\82½\81D");
1138 }
1139
1140 void
1141 save_waterlevel(fd, mode)
1142 int fd, mode;
1143 {
1144     register struct bubble *b;
1145
1146     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1147         return;
1148
1149     if (perform_bwrite(mode)) {
1150         int n = 0;
1151         for (b = bbubbles; b; b = b->next)
1152             ++n;
1153         bwrite(fd, (genericptr_t) &n, sizeof(int));
1154         bwrite(fd, (genericptr_t) &xmin, sizeof(int));
1155         bwrite(fd, (genericptr_t) &ymin, sizeof(int));
1156         bwrite(fd, (genericptr_t) &xmax, sizeof(int));
1157         bwrite(fd, (genericptr_t) &ymax, sizeof(int));
1158         for (b = bbubbles; b; b = b->next)
1159             bwrite(fd, (genericptr_t) b, sizeof(struct bubble));
1160     }
1161     if (release_data(mode))
1162         unsetup_waterlevel();
1163 }
1164
1165 void
1166 restore_waterlevel(fd)
1167 register int fd;
1168 {
1169     register struct bubble *b = (struct bubble *) 0, *btmp;
1170     register int i;
1171     int n;
1172
1173     if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1174         return;
1175
1176     set_wportal();
1177     mread(fd, (genericptr_t) &n, sizeof(int));
1178     mread(fd, (genericptr_t) &xmin, sizeof(int));
1179     mread(fd, (genericptr_t) &ymin, sizeof(int));
1180     mread(fd, (genericptr_t) &xmax, sizeof(int));
1181     mread(fd, (genericptr_t) &ymax, sizeof(int));
1182     for (i = 0; i < n; i++) {
1183         btmp = b;
1184         b = (struct bubble *) alloc(sizeof(struct bubble));
1185         mread(fd, (genericptr_t) b, sizeof(struct bubble));
1186         if (bbubbles) {
1187             btmp->next = b;
1188             b->prev = btmp;
1189         } else {
1190             bbubbles = b;
1191             b->prev = (struct bubble *) 0;
1192         }
1193         mv_bubble(b, 0, 0, TRUE);
1194     }
1195     ebubbles = b;
1196     b->next = (struct bubble *) 0;
1197     was_waterlevel = TRUE;
1198 }
1199
1200 const char *
1201 waterbody_name(x, y)
1202 xchar x, y;
1203 {
1204     register struct rm *lev;
1205     schar ltyp;
1206
1207     if (!isok(x, y))
1208         return "drink"; /* should never happen */
1209     lev = &levl[x][y];
1210     ltyp = lev->typ;
1211     if (ltyp == DRAWBRIDGE_UP)
1212         ltyp = db_under_typ(lev->drawbridgemask);
1213
1214     if (ltyp == LAVAPOOL)
1215 /*JP
1216         return "lava";
1217 */
1218         return "\97n\8aâ";
1219     else if (ltyp == ICE)
1220 /*JP
1221         return "ice";
1222 */
1223         return "\95X";
1224     else if (ltyp == POOL)
1225 /*JP
1226         return "pool of water";
1227 */
1228         return "\90\85\82½\82Ãœ\82è";
1229     else if (ltyp == WATER || Is_waterlevel(&u.uz))
1230         ; /* fall through to default return value */
1231     else if (Is_juiblex_level(&u.uz))
1232 /*JP
1233         return "swamp";
1234 */
1235         return "\8fÀ";
1236     else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1237 /*JP
1238         return "moat";
1239 */
1240         return "\96x";
1241
1242 /*JP
1243     return "water";
1244 */
1245     return "\90\85\92\86";
1246 }
1247
1248 STATIC_OVL void
1249 set_wportal()
1250 {
1251     /* there better be only one magic portal on water level... */
1252     for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1253         if (wportal->ttyp == MAGIC_PORTAL)
1254             return;
1255     impossible("set_wportal(): no portal!");
1256 }
1257
1258 STATIC_OVL void
1259 setup_waterlevel()
1260 {
1261     register int x, y;
1262     register int xskip, yskip;
1263     register int water_glyph = cmap_to_glyph(S_water);
1264     register int air_glyph = cmap_to_glyph(S_air);
1265
1266     /* ouch, hardcoded... */
1267
1268     xmin = 3;
1269     ymin = 1;
1270     xmax = 78;
1271     ymax = 20;
1272
1273     /* set hero's memory to water */
1274
1275     for (x = xmin; x <= xmax; x++)
1276         for (y = ymin; y <= ymax; y++)
1277             levl[x][y].glyph = Is_waterlevel(&u.uz) ? water_glyph : air_glyph;
1278
1279     /* make bubbles */
1280
1281     if (Is_waterlevel(&u.uz)) {
1282         xskip = 10 + rn2(10);
1283         yskip = 4 + rn2(4);
1284     } else {
1285         xskip = 6 + rn2(4);
1286         yskip = 3 + rn2(3);
1287     }
1288
1289     for (x = bxmin; x <= bxmax; x += xskip)
1290         for (y = bymin; y <= bymax; y += yskip)
1291             mk_bubble(x, y, rn2(7));
1292 }
1293
1294 STATIC_OVL void
1295 unsetup_waterlevel()
1296 {
1297     register struct bubble *b, *bb;
1298
1299     /* free bubbles */
1300
1301     for (b = bbubbles; b; b = bb) {
1302         bb = b->next;
1303         free((genericptr_t) b);
1304     }
1305     bbubbles = ebubbles = (struct bubble *) 0;
1306 }
1307
1308 STATIC_OVL void
1309 mk_bubble(x, y, n)
1310 register int x, y, n;
1311 {
1312     /*
1313      * These bit masks make visually pleasing bubbles on a normal aspect
1314      * 25x80 terminal, which naturally results in them being mathematically
1315      * anything but symmetric.  For this reason they cannot be computed
1316      * in situ, either.  The first two elements tell the dimensions of
1317      * the bubble's bounding box.
1318      */
1319     static uchar bm2[] = { 2, 1, 0x3 }, bm3[] = { 3, 2, 0x7, 0x7 },
1320                  bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1321                  bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1322                  bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1323                  bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1324                  bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1325                  *bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1326     register struct bubble *b;
1327
1328     if (x >= bxmax || y >= bymax)
1329         return;
1330     if (n >= SIZE(bmask)) {
1331         impossible("n too large (mk_bubble)");
1332         n = SIZE(bmask) - 1;
1333     }
1334     if (bmask[n][1] > MAX_BMASK) {
1335         panic("bmask size is larger than MAX_BMASK");
1336     }
1337     b = (struct bubble *) alloc(sizeof(struct bubble));
1338     if ((x + (int) bmask[n][0] - 1) > bxmax)
1339         x = bxmax - bmask[n][0] + 1;
1340     if ((y + (int) bmask[n][1] - 1) > bymax)
1341         y = bymax - bmask[n][1] + 1;
1342     b->x = x;
1343     b->y = y;
1344     b->dx = 1 - rn2(3);
1345     b->dy = 1 - rn2(3);
1346     /* y dimension is the length of bitmap data - see bmask above */
1347     (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1348                   (bmask[n][1] + 2) * sizeof(b->bm[0]));
1349     b->cons = 0;
1350     if (!bbubbles)
1351         bbubbles = b;
1352     if (ebubbles) {
1353         ebubbles->next = b;
1354         b->prev = ebubbles;
1355     } else
1356         b->prev = (struct bubble *) 0;
1357     b->next = (struct bubble *) 0;
1358     ebubbles = b;
1359     mv_bubble(b, 0, 0, TRUE);
1360 }
1361
1362 /*
1363  * The player, the portal and all other objects and monsters
1364  * float along with their associated bubbles.  Bubbles may overlap
1365  * freely, and the contents may get associated with other bubbles in
1366  * the process.  Bubbles are "sticky", meaning that if the player is
1367  * in the immediate neighborhood of one, he/she may get sucked inside.
1368  * This property also makes leaving a bubble slightly difficult.
1369  */
1370 STATIC_OVL void
1371 mv_bubble(b, dx, dy, ini)
1372 register struct bubble *b;
1373 register int dx, dy;
1374 register boolean ini;
1375 {
1376     register int x, y, i, j, colli = 0;
1377     struct container *cons, *ctemp;
1378
1379     /* clouds move slowly */
1380     if (!Is_airlevel(&u.uz) || !rn2(6)) {
1381         /* move bubble */
1382         if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1383             /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1384             dx = sgn(dx);
1385             dy = sgn(dy);
1386         }
1387
1388         /*
1389          * collision with level borders?
1390          *      1 = horizontal border, 2 = vertical, 3 = corner
1391          */
1392         if (b->x <= bxmin)
1393             colli |= 2;
1394         if (b->y <= bymin)
1395             colli |= 1;
1396         if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1397             colli |= 2;
1398         if ((int) (b->y + b->bm[1] - 1) >= bymax)
1399             colli |= 1;
1400
1401         if (b->x < bxmin) {
1402             pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1403             b->x = bxmin;
1404         }
1405         if (b->y < bymin) {
1406             pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1407             b->y = bymin;
1408         }
1409         if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1410             pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1411                   bxmax);
1412             b->x = bxmax - b->bm[0] + 1;
1413         }
1414         if ((int) (b->y + b->bm[1] - 1) > bymax) {
1415             pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1416                   bymax);
1417             b->y = bymax - b->bm[1] + 1;
1418         }
1419
1420         /* bounce if we're trying to move off the border */
1421         if (b->x == bxmin && dx < 0)
1422             dx = -dx;
1423         if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1424             dx = -dx;
1425         if (b->y == bymin && dy < 0)
1426             dy = -dy;
1427         if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1428             dy = -dy;
1429
1430         b->x += dx;
1431         b->y += dy;
1432     }
1433
1434     /* draw the bubbles */
1435     for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1436         for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1437             if (b->bm[j + 2] & (1 << i)) {
1438                 if (Is_waterlevel(&u.uz)) {
1439                     levl[x][y].typ = AIR;
1440                     levl[x][y].lit = 1;
1441                     unblock_point(x, y);
1442                 } else if (Is_airlevel(&u.uz)) {
1443                     levl[x][y].typ = CLOUD;
1444                     levl[x][y].lit = 1;
1445                     block_point(x, y);
1446                 }
1447             }
1448
1449     if (Is_waterlevel(&u.uz)) {
1450         /* replace contents of bubble */
1451         for (cons = b->cons; cons; cons = ctemp) {
1452             ctemp = cons->next;
1453             cons->x += dx;
1454             cons->y += dy;
1455
1456             switch (cons->what) {
1457             case CONS_OBJ: {
1458                 struct obj *olist, *otmp;
1459
1460                 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1461                     otmp = olist->nexthere;
1462                     place_object(olist, cons->x, cons->y);
1463                 }
1464                 break;
1465             }
1466
1467             case CONS_MON: {
1468                 struct monst *mon = (struct monst *) cons->list;
1469                 (void) mnearto(mon, cons->x, cons->y, TRUE);
1470                 break;
1471             }
1472
1473             case CONS_HERO: {
1474                 int ux0 = u.ux, uy0 = u.uy;
1475
1476                 /* change u.ux0 and u.uy0? */
1477                 u.ux = cons->x;
1478                 u.uy = cons->y;
1479                 newsym(ux0, uy0); /* clean up old position */
1480
1481                 if (MON_AT(cons->x, cons->y)) {
1482                     mnexto(m_at(cons->x, cons->y));
1483                 }
1484                 break;
1485             }
1486
1487             case CONS_TRAP: {
1488                 struct trap *btrap = (struct trap *) cons->list;
1489                 btrap->tx = cons->x;
1490                 btrap->ty = cons->y;
1491                 break;
1492             }
1493
1494             default:
1495                 impossible("mv_bubble: unknown bubble contents");
1496                 break;
1497             }
1498             free((genericptr_t) cons);
1499         }
1500         b->cons = 0;
1501     }
1502
1503     /* boing? */
1504     switch (colli) {
1505     case 1:
1506         b->dy = -b->dy;
1507         break;
1508     case 3:
1509         b->dy = -b->dy; /* fall through */
1510     case 2:
1511         b->dx = -b->dx;
1512         break;
1513     default:
1514         /* sometimes alter direction for fun anyway
1515            (higher probability for stationary bubbles) */
1516         if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
1517             b->dx = 1 - rn2(3);
1518             b->dy = 1 - rn2(3);
1519         }
1520     }
1521 }
1522
1523 /*mkmaze.c*/