OSDN Git Service

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