use of #terrain while underwater
[aNetHack.git] / src / mkmaze.c
blob03d4b07679eaedd4a194243fc53b16ca60ce335a
1 /* NetHack 3.6 mkmaze.c $NHDT-Date: 1461571093 2016/04/25 07:58:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.47 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "sp_lev.h"
7 #include "lev.h" /* save & restore info */
9 /* from sp_lev.c, for fixup_special() */
10 extern lev_region *lregions;
11 extern int num_lregions;
12 /* for preserving the insect legs when wallifying baalz level */
13 static lev_region bughack = { {COLNO, ROWNO, 0, 0}, {COLNO, ROWNO, 0, 0} };
15 STATIC_DCL int FDECL(iswall, (int, int));
16 STATIC_DCL int FDECL(iswall_or_stone, (int, int));
17 STATIC_DCL boolean FDECL(is_solid, (int, int));
18 STATIC_DCL int FDECL(extend_spine, (int[3][3], int, int, int));
19 STATIC_DCL boolean FDECL(okay, (int, int, int));
20 STATIC_DCL void FDECL(maze0xy, (coord *));
21 STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
22 XCHAR_P, XCHAR_P, XCHAR_P,
23 XCHAR_P, BOOLEAN_P, d_level *));
24 STATIC_DCL void NDECL(baalz_fixup);
25 STATIC_DCL void NDECL(setup_waterlevel);
26 STATIC_DCL void NDECL(unsetup_waterlevel);
28 /* adjust a coordinate one step in the specified direction */
29 #define mz_move(X, Y, dir) \
30 do { \
31 switch (dir) { \
32 case 0: --(Y); break; \
33 case 1: (X)++; break; \
34 case 2: (Y)++; break; \
35 case 3: --(X); break; \
36 default: panic("mz_move: bad direction %d", dir); \
37 } \
38 } while (0)
40 STATIC_OVL int
41 iswall(x, y)
42 int x, y;
44 register int type;
46 if (!isok(x, y))
47 return 0;
48 type = levl[x][y].typ;
49 return (IS_WALL(type) || IS_DOOR(type)
50 || type == SDOOR || type == IRONBARS);
53 STATIC_OVL int
54 iswall_or_stone(x, y)
55 int x, y;
57 /* out of bounds = stone */
58 if (!isok(x, y))
59 return 1;
61 return (levl[x][y].typ == STONE || iswall(x, y));
64 /* return TRUE if out of bounds, wall or rock */
65 STATIC_OVL boolean
66 is_solid(x, y)
67 int x, y;
69 return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
73 * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
74 * a wall spine in the (dx,dy) direction. Return 0 otherwise.
76 * To extend a wall spine in that direction, first there must be a wall there.
77 * Then, extend a spine unless the current position is surrounded by walls
78 * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W'
79 * a wall, '.' a room, 'a' anything (we don't care), and our direction is
80 * (0,1) - South or down - then:
82 * a a a
83 * W x W This would not extend a spine from x down
84 * W W W (a corridor of walls is formed).
86 * a a a
87 * W x W This would extend a spine from x down.
88 * . W W
90 STATIC_OVL int
91 extend_spine(locale, wall_there, dx, dy)
92 int locale[3][3];
93 int wall_there, dx, dy;
95 int spine, nx, ny;
97 nx = 1 + dx;
98 ny = 1 + dy;
100 if (wall_there) { /* wall in that direction */
101 if (dx) {
102 if (locale[1][0] && locale[1][2] /* EW are wall/stone */
103 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
104 spine = 0;
105 } else {
106 spine = 1;
108 } else { /* dy */
109 if (locale[0][1] && locale[2][1] /* NS are wall/stone */
110 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
111 spine = 0;
112 } else {
113 spine = 1;
116 } else {
117 spine = 0;
120 return spine;
123 /* Remove walls totally surrounded by stone */
124 void
125 wall_cleanup(x1, y1, x2, y2)
126 int x1, y1, x2, y2;
128 uchar type;
129 register int x, y;
130 struct rm *lev;
132 /* sanity check on incoming variables */
133 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
134 panic("wall_cleanup: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
136 /* change walls surrounded by rock to rock. */
137 for (x = x1; x <= x2; x++)
138 for (y = y1; y <= y2; y++) {
139 if (within_bounded_area(x, y,
140 bughack.inarea.x1, bughack.inarea.y1,
141 bughack.inarea.x2, bughack.inarea.y2))
142 continue;
143 lev = &levl[x][y];
144 type = lev->typ;
145 if (IS_WALL(type) && type != DBWALL) {
146 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
147 && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
148 && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
149 && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
150 lev->typ = STONE;
155 /* Correct wall types so they extend and connect to each other */
156 void
157 fix_wall_spines(x1, y1, x2, y2)
158 int x1, y1, x2, y2;
160 uchar type;
161 register int x,y;
162 struct rm *lev;
163 int FDECL((*loc_f), (int, int));
164 int bits;
165 int locale[3][3]; /* rock or wall status surrounding positions */
168 * Value 0 represents a free-standing wall. It could be anything,
169 * so even though this table says VWALL, we actually leave whatever
170 * typ was there alone.
172 static xchar spine_array[16] = { VWALL, HWALL, HWALL, HWALL,
173 VWALL, TRCORNER, TLCORNER, TDWALL,
174 VWALL, BRCORNER, BLCORNER, TUWALL,
175 VWALL, TLWALL, TRWALL, CROSSWALL };
177 /* sanity check on incoming variables */
178 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
179 panic("wall_extends: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
181 /* set the correct wall type. */
182 for (x = x1; x <= x2; x++)
183 for (y = y1; y <= y2; y++) {
184 lev = &levl[x][y];
185 type = lev->typ;
186 if (!(IS_WALL(type) && type != DBWALL))
187 continue;
189 /* set the locations TRUE if rock or wall or out of bounds */
190 loc_f = within_bounded_area(x, y, /* for baalz insect */
191 bughack.inarea.x1, bughack.inarea.y1,
192 bughack.inarea.x2, bughack.inarea.y2)
193 ? iswall
194 : iswall_or_stone;
195 locale[0][0] = (*loc_f)(x - 1, y - 1);
196 locale[1][0] = (*loc_f)(x, y - 1);
197 locale[2][0] = (*loc_f)(x + 1, y - 1);
199 locale[0][1] = (*loc_f)(x - 1, y);
200 locale[2][1] = (*loc_f)(x + 1, y);
202 locale[0][2] = (*loc_f)(x - 1, y + 1);
203 locale[1][2] = (*loc_f)(x, y + 1);
204 locale[2][2] = (*loc_f)(x + 1, y + 1);
206 /* determine if wall should extend to each direction NSEW */
207 bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
208 | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
209 | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
210 | extend_spine(locale, iswall(x - 1, y), -1, 0);
212 /* don't change typ if wall is free-standing */
213 if (bits)
214 lev->typ = spine_array[bits];
218 void
219 wallification(x1, y1, x2, y2)
220 int x1, y1, x2, y2;
222 wall_cleanup(x1, y1, x2, y2);
223 fix_wall_spines(x1, y1, x2, y2);
226 STATIC_OVL boolean
227 okay(x, y, dir)
228 int x, y;
229 register int dir;
231 mz_move(x, y, dir);
232 mz_move(x, y, dir);
233 if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max
234 || levl[x][y].typ != 0)
235 return FALSE;
236 return TRUE;
239 /* find random starting point for maze generation */
240 STATIC_OVL void
241 maze0xy(cc)
242 coord *cc;
244 cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
245 cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
246 return;
250 * Bad if:
251 * pos is occupied OR
252 * pos is inside restricted region (lx,ly,hx,hy) OR
253 * NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
255 boolean
256 bad_location(x, y, lx, ly, hx, hy)
257 xchar x, y;
258 xchar lx, ly, hx, hy;
260 return (boolean) (occupied(x, y)
261 || within_bounded_area(x, y, lx, ly, hx, hy)
262 || !((levl[x][y].typ == CORR && level.flags.is_maze_lev)
263 || levl[x][y].typ == ROOM
264 || levl[x][y].typ == AIR));
267 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
268 and place something (based on rtype) in that region */
269 void
270 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
271 xchar lx, ly, hx, hy;
272 xchar nlx, nly, nhx, nhy;
273 xchar rtype;
274 d_level *lev;
276 int trycnt;
277 boolean oneshot;
278 xchar x, y;
280 if (!lx) { /* default to whole level */
282 * if there are rooms and this a branch, let place_branch choose
283 * the branch location (to avoid putting branches in corridors).
285 if (rtype == LR_BRANCH && nroom) {
286 place_branch(Is_branchlev(&u.uz), 0, 0);
287 return;
290 lx = 1;
291 hx = COLNO - 1;
292 ly = 1;
293 hy = ROWNO - 1;
296 /* first a probabilistic approach */
298 oneshot = (lx == hx && ly == hy);
299 for (trycnt = 0; trycnt < 200; trycnt++) {
300 x = rn1((hx - lx) + 1, lx);
301 y = rn1((hy - ly) + 1, ly);
302 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
303 return;
306 /* then a deterministic one */
308 oneshot = TRUE;
309 for (x = lx; x <= hx; x++)
310 for (y = ly; y <= hy; y++)
311 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot,
312 lev))
313 return;
315 impossible("Couldn't place lregion type %d!", rtype);
318 STATIC_OVL boolean
319 put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev)
320 xchar x, y;
321 xchar nlx, nly, nhx, nhy;
322 xchar rtype;
323 boolean oneshot;
324 d_level *lev;
326 if (bad_location(x, y, nlx, nly, nhx, nhy)) {
327 if (!oneshot) {
328 return FALSE; /* caller should try again */
329 } else {
330 /* Must make do with the only location possible;
331 avoid failure due to a misplaced trap.
332 It might still fail if there's a dungeon feature here. */
333 struct trap *t = t_at(x, y);
335 if (t && t->ttyp != MAGIC_PORTAL && t->ttyp != VIBRATING_SQUARE)
336 deltrap(t);
337 if (bad_location(x, y, nlx, nly, nhx, nhy))
338 return FALSE;
341 switch (rtype) {
342 case LR_TELE:
343 case LR_UPTELE:
344 case LR_DOWNTELE:
345 /* "something" means the player in this case */
346 if (MON_AT(x, y)) {
347 /* move the monster if no choice, or just try again */
348 if (oneshot)
349 (void) rloc(m_at(x, y), FALSE);
350 else
351 return FALSE;
353 u_on_newpos(x, y);
354 break;
355 case LR_PORTAL:
356 mkportal(x, y, lev->dnum, lev->dlevel);
357 break;
358 case LR_DOWNSTAIR:
359 case LR_UPSTAIR:
360 mkstairs(x, y, (char) rtype, (struct mkroom *) 0);
361 break;
362 case LR_BRANCH:
363 place_branch(Is_branchlev(&u.uz), x, y);
364 break;
366 return TRUE;
369 /* fix up Baalzebub's lair, which depicts a level-sized beetle;
370 its legs are walls within solid rock--regular wallification
371 classifies them as superfluous and gets rid of them */
372 STATIC_OVL void
373 baalz_fixup()
375 struct monst *mtmp;
376 int x, y, lastx, lasty;
379 * baalz level's nondiggable region surrounds the "insect" and rooms.
380 * The outermost perimeter of that region is subject to wall cleanup
381 * (hence 'x + 1' and 'y + 1' for starting don't-clean column and row,
382 * 'lastx - 1' and 'lasty - 1' for ending don't-clean column and row)
383 * and the interior is protected against that (in wall_cleanup()).
385 * Assumes level.flags.corrmaze is True, otherwise the bug legs will
386 * have already been "cleaned" away by general wallification.
389 /* find low and high x for to-be-wallified portion of level */
390 y = ROWNO / 2;
391 for (lastx = x = 0; x < COLNO; ++x)
392 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
393 if (!lastx)
394 bughack.inarea.x1 = x + 1;
395 lastx = x;
397 bughack.inarea.x2 = ((lastx > bughack.inarea.x1) ? lastx : x) - 1;
398 /* find low and high y for to-be-wallified portion of level */
399 x = bughack.inarea.x1;
400 for (lasty = y = 0; y < ROWNO; ++y)
401 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
402 if (!lasty)
403 bughack.inarea.y1 = y + 1;
404 lasty = y;
406 bughack.inarea.y2 = ((lasty > bughack.inarea.y1) ? lasty : y) - 1;
407 /* two pools mark where special post-wallify fix-ups are needed */
408 for (x = bughack.inarea.x1; x <= bughack.inarea.x2; ++x)
409 for (y = bughack.inarea.y1; y <= bughack.inarea.y2; ++y)
410 if (levl[x][y].typ == POOL) {
411 levl[x][y].typ = HWALL;
412 if (bughack.delarea.x1 == COLNO)
413 bughack.delarea.x1 = x, bughack.delarea.y1 = y;
414 else
415 bughack.delarea.x2 = x, bughack.delarea.y2 = y;
416 } else if (levl[x][y].typ == IRONBARS) {
417 /* novelty effect; allowing digging in front of 'eyes' */
418 levl[x - 1][y].wall_info &= ~W_NONDIGGABLE;
419 if (isok(x - 2, y))
420 levl[x - 2][y].wall_info &= ~W_NONDIGGABLE;
423 wallification(max(bughack.inarea.x1 - 2, 1),
424 max(bughack.inarea.y1 - 2, 0),
425 min(bughack.inarea.x2 + 2, COLNO - 1),
426 min(bughack.inarea.y2 + 2, ROWNO - 1));
428 /* bughack hack for rear-most legs on baalz level; first joint on
429 both top and bottom gets a bogus extra connection to room area,
430 producing unwanted rectangles; change back to separated legs */
431 x = bughack.delarea.x1, y = bughack.delarea.y1;
432 if (isok(x, y) && levl[x][y].typ == TLWALL
433 && isok(x, y + 1) && levl[x][y + 1].typ == TUWALL) {
434 levl[x][y].typ = BRCORNER;
435 levl[x][y + 1].typ = HWALL;
436 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
437 (void) rloc(mtmp, FALSE);
439 x = bughack.delarea.x2, y = bughack.delarea.y2;
440 if (isok(x, y) && levl[x][y].typ == TLWALL
441 && isok(x, y - 1) && levl[x][y - 1].typ == TDWALL) {
442 levl[x][y].typ = TRCORNER;
443 levl[x][y - 1].typ = HWALL;
444 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
445 (void) rloc(mtmp, FALSE);
448 /* reset bughack region; set low end to <COLNO,ROWNO> so that
449 within_bounded_region() in fix_wall_spines() will fail
450 most quickly--on its first test--when loading other levels */
451 bughack.inarea.x1 = bughack.delarea.x1 = COLNO;
452 bughack.inarea.y1 = bughack.delarea.y1 = ROWNO;
453 bughack.inarea.x2 = bughack.delarea.x2 = 0;
454 bughack.inarea.y2 = bughack.delarea.y2 = 0;
457 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
459 /* this is special stuff that the level compiler cannot (yet) handle */
460 void
461 fixup_special()
463 register lev_region *r = lregions;
464 struct d_level lev;
465 register int x, y;
466 struct mkroom *croom;
467 boolean added_branch = FALSE;
469 if (was_waterlevel) {
470 was_waterlevel = FALSE;
471 u.uinwater = 0;
472 unsetup_waterlevel();
474 if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
475 level.flags.hero_memory = 0;
476 was_waterlevel = TRUE;
477 /* water level is an odd beast - it has to be set up
478 before calling place_lregions etc. */
479 setup_waterlevel();
481 for (x = 0; x < num_lregions; x++, r++) {
482 switch (r->rtype) {
483 case LR_BRANCH:
484 added_branch = TRUE;
485 goto place_it;
487 case LR_PORTAL:
488 if (*r->rname.str >= '0' && *r->rname.str <= '9') {
489 /* "chutes and ladders" */
490 lev = u.uz;
491 lev.dlevel = atoi(r->rname.str);
492 } else {
493 s_level *sp = find_level(r->rname.str);
494 lev = sp->dlevel;
496 /* fall into... */
498 case LR_UPSTAIR:
499 case LR_DOWNSTAIR:
500 place_it:
501 place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
502 r->inarea.y2, r->delarea.x1, r->delarea.y1,
503 r->delarea.x2, r->delarea.y2, r->rtype, &lev);
504 break;
506 case LR_TELE:
507 case LR_UPTELE:
508 case LR_DOWNTELE:
509 /* save the region outlines for goto_level() */
510 if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
511 updest.lx = r->inarea.x1;
512 updest.ly = r->inarea.y1;
513 updest.hx = r->inarea.x2;
514 updest.hy = r->inarea.y2;
515 updest.nlx = r->delarea.x1;
516 updest.nly = r->delarea.y1;
517 updest.nhx = r->delarea.x2;
518 updest.nhy = r->delarea.y2;
520 if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
521 dndest.lx = r->inarea.x1;
522 dndest.ly = r->inarea.y1;
523 dndest.hx = r->inarea.x2;
524 dndest.hy = r->inarea.y2;
525 dndest.nlx = r->delarea.x1;
526 dndest.nly = r->delarea.y1;
527 dndest.nhx = r->delarea.x2;
528 dndest.nhy = r->delarea.y2;
530 /* place_lregion gets called from goto_level() */
531 break;
534 if (r->rname.str)
535 free((genericptr_t) r->rname.str), r->rname.str = 0;
538 /* place dungeon branch if not placed above */
539 if (!added_branch && Is_branchlev(&u.uz)) {
540 place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
543 /* Still need to add some stuff to level file */
544 if (Is_medusa_level(&u.uz)) {
545 struct obj *otmp;
546 int tryct;
548 croom = &rooms[0]; /* only one room on the medusa level */
549 for (tryct = rnd(4); tryct; tryct--) {
550 x = somex(croom);
551 y = somey(croom);
552 if (goodpos(x, y, (struct monst *) 0, 0)) {
553 otmp = mk_tt_object(STATUE, x, y);
554 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm])
555 || pm_resistance(&mons[otmp->corpsenm],
556 MR_STONE))) {
557 /* set_corpsenm() handles weight too */
558 set_corpsenm(otmp, rndmonnum());
563 if (rn2(2))
564 otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
565 else /* Medusa statues don't contain books */
566 otmp =
567 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
568 somex(croom), somey(croom), CORPSTAT_NONE);
569 if (otmp) {
570 while (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
571 || poly_when_stoned(&mons[otmp->corpsenm])) {
572 /* set_corpsenm() handles weight too */
573 set_corpsenm(otmp, rndmonnum());
576 } else if (Is_wiz1_level(&u.uz)) {
577 croom = search_special(MORGUE);
579 create_secret_door(croom, W_SOUTH | W_EAST | W_WEST);
580 } else if (Is_knox(&u.uz)) {
581 /* using an unfilled morgue for rm id */
582 croom = search_special(MORGUE);
583 /* avoid inappropriate morgue-related messages */
584 level.flags.graveyard = level.flags.has_morgue = 0;
585 croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
586 /* stock the main vault */
587 for (x = croom->lx; x <= croom->hx; x++)
588 for (y = croom->ly; y <= croom->hy; y++) {
589 (void) mkgold((long) rn1(300, 600), x, y);
590 if (!rn2(3) && !is_pool(x, y))
591 (void) maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
593 } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
594 /* less chance for undead corpses (lured from lower morgues) */
595 level.flags.graveyard = 1;
596 } else if (Is_stronghold(&u.uz)) {
597 level.flags.graveyard = 1;
598 } else if (Is_sanctum(&u.uz)) {
599 croom = search_special(TEMPLE);
601 create_secret_door(croom, W_ANY);
602 } else if (on_level(&u.uz, &orcus_level)) {
603 register struct monst *mtmp, *mtmp2;
605 /* it's a ghost town, get rid of shopkeepers */
606 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
607 mtmp2 = mtmp->nmon;
608 if (mtmp->isshk)
609 mongone(mtmp);
611 } else if (on_level(&u.uz, &baalzebub_level)) {
612 /* custom wallify the "beetle" potion of the level */
613 baalz_fixup();
616 if (lregions)
617 free((genericptr_t) lregions), lregions = 0;
618 num_lregions = 0;
621 void
622 makemaz(s)
623 register const char *s;
625 int x, y;
626 char protofile[20];
627 s_level *sp = Is_special(&u.uz);
628 coord mm;
630 if (*s) {
631 if (sp && sp->rndlevs)
632 Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
633 else
634 Strcpy(protofile, s);
635 } else if (*(dungeons[u.uz.dnum].proto)) {
636 if (dunlevs_in_dungeon(&u.uz) > 1) {
637 if (sp && sp->rndlevs)
638 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
639 dunlev(&u.uz), rnd((int) sp->rndlevs));
640 else
641 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
642 dunlev(&u.uz));
643 } else if (sp && sp->rndlevs) {
644 Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
645 rnd((int) sp->rndlevs));
646 } else
647 Strcpy(protofile, dungeons[u.uz.dnum].proto);
649 } else
650 Strcpy(protofile, "");
652 /* SPLEVTYPE format is "level-choice,level-choice"... */
653 if (wizard && *protofile && sp && sp->rndlevs) {
654 char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
655 if (ep) {
656 /* rindex always succeeds due to code in prior block */
657 int len = (int) ((rindex(protofile, '-') - protofile) + 1);
659 while (ep && *ep) {
660 if (!strncmp(ep, protofile, len)) {
661 int pick = atoi(ep + len);
662 /* use choice only if valid */
663 if (pick > 0 && pick <= (int) sp->rndlevs)
664 Sprintf(protofile + len, "%d", pick);
665 break;
666 } else {
667 ep = index(ep, ',');
668 if (ep)
669 ++ep;
675 if (*protofile) {
676 Strcat(protofile, LEV_EXT);
677 if (load_special(protofile)) {
678 /* some levels can end up with monsters
679 on dead mon list, including light source monsters */
680 dmonsfree();
681 return; /* no mazification right now */
683 impossible("Couldn't load \"%s\" - making a maze.", protofile);
686 level.flags.is_maze_lev = TRUE;
687 level.flags.corrmaze = !rn2(3);
689 if (level.flags.corrmaze)
690 for (x = 2; x < x_maze_max; x++)
691 for (y = 2; y < y_maze_max; y++)
692 levl[x][y].typ = STONE;
693 else
694 for (x = 2; x <= x_maze_max; x++)
695 for (y = 2; y <= y_maze_max; y++)
696 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
698 maze0xy(&mm);
699 walkfrom((int) mm.x, (int) mm.y, 0);
700 /* put a boulder at the maze center */
701 (void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE, FALSE);
703 if (!level.flags.corrmaze)
704 wallification(2, 2, x_maze_max, y_maze_max);
706 mazexy(&mm);
707 mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
708 if (!Invocation_lev(&u.uz)) {
709 mazexy(&mm);
710 mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
711 } else { /* choose "vibrating square" location */
712 #define x_maze_min 2
713 #define y_maze_min 2
715 * Pick a position where the stairs down to Moloch's Sanctum
716 * level will ultimately be created. At that time, an area
717 * will be altered: walls removed, moat and traps generated,
718 * boulders destroyed. The position picked here must ensure
719 * that that invocation area won't extend off the map.
721 * We actually allow up to 2 squares around the usual edge of
722 * the area to get truncated; see mkinvokearea(mklev.c).
724 #define INVPOS_X_MARGIN (6 - 2)
725 #define INVPOS_Y_MARGIN (5 - 2)
726 #define INVPOS_DISTANCE 11
727 int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
728 y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
730 if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
731 || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
732 debugpline2("inv_pos: maze is too small! (%d x %d)",
733 x_maze_max, y_maze_max);
735 inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
736 do {
737 x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
738 y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
739 /* we don't want it to be too near the stairs, nor
740 to be on a spot that's already in use (wall|trap) */
741 } while (x == xupstair || y == yupstair /*(direct line)*/
742 || abs(x - xupstair) == abs(y - yupstair)
743 || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
744 || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
745 inv_pos.x = x;
746 inv_pos.y = y;
747 maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
748 #undef INVPOS_X_MARGIN
749 #undef INVPOS_Y_MARGIN
750 #undef INVPOS_DISTANCE
751 #undef x_maze_min
752 #undef y_maze_min
755 /* place branch stair or portal */
756 place_branch(Is_branchlev(&u.uz), 0, 0);
758 for (x = rn1(8, 11); x; x--) {
759 mazexy(&mm);
760 (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
762 for (x = rn1(10, 2); x; x--) {
763 mazexy(&mm);
764 (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
766 for (x = rn2(3); x; x--) {
767 mazexy(&mm);
768 (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
770 for (x = rn1(5, 7); x; x--) {
771 mazexy(&mm);
772 (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
774 for (x = rn1(6, 7); x; x--) {
775 mazexy(&mm);
776 (void) mkgold(0L, mm.x, mm.y);
778 for (x = rn1(6, 7); x; x--)
779 mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
782 #ifdef MICRO
783 /* Make the mazewalk iterative by faking a stack. This is needed to
784 * ensure the mazewalk is successful in the limited stack space of
785 * the program. This iterative version uses the minimum amount of stack
786 * that is totally safe.
788 void
789 walkfrom(x, y, typ)
790 int x, y;
791 schar typ;
793 #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */
794 char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
795 int q, a, dir, pos;
796 int dirs[4];
798 if (!typ) {
799 if (level.flags.corrmaze)
800 typ = CORR;
801 else
802 typ = ROOM;
805 pos = 1;
806 mazex[pos] = (char) x;
807 mazey[pos] = (char) y;
808 while (pos) {
809 x = (int) mazex[pos];
810 y = (int) mazey[pos];
811 if (!IS_DOOR(levl[x][y].typ)) {
812 /* might still be on edge of MAP, so don't overwrite */
813 levl[x][y].typ = typ;
814 levl[x][y].flags = 0;
816 q = 0;
817 for (a = 0; a < 4; a++)
818 if (okay(x, y, a))
819 dirs[q++] = a;
820 if (!q)
821 pos--;
822 else {
823 dir = dirs[rn2(q)];
824 mz_move(x, y, dir);
825 levl[x][y].typ = typ;
826 mz_move(x, y, dir);
827 pos++;
828 if (pos > CELLS)
829 panic("Overflow in walkfrom");
830 mazex[pos] = (char) x;
831 mazey[pos] = (char) y;
835 #else /* !MICRO */
837 void
838 walkfrom(x, y, typ)
839 int x, y;
840 schar typ;
842 register int q, a, dir;
843 int dirs[4];
845 if (!typ) {
846 if (level.flags.corrmaze)
847 typ = CORR;
848 else
849 typ = ROOM;
852 if (!IS_DOOR(levl[x][y].typ)) {
853 /* might still be on edge of MAP, so don't overwrite */
854 levl[x][y].typ = typ;
855 levl[x][y].flags = 0;
858 while (1) {
859 q = 0;
860 for (a = 0; a < 4; a++)
861 if (okay(x, y, a))
862 dirs[q++] = a;
863 if (!q)
864 return;
865 dir = dirs[rn2(q)];
866 mz_move(x, y, dir);
867 levl[x][y].typ = typ;
868 mz_move(x, y, dir);
869 walkfrom(x, y, typ);
872 #endif /* ?MICRO */
874 /* find random point in generated corridors,
875 so we don't create items in moats, bunkers, or walls */
876 void
877 mazexy(cc)
878 coord *cc;
880 int cpt = 0;
882 do {
883 cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
884 cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
885 cpt++;
886 } while (cpt < 100
887 && levl[cc->x][cc->y].typ
888 != (level.flags.corrmaze ? CORR : ROOM));
889 if (cpt >= 100) {
890 register int x, y;
891 /* last try */
892 for (x = 0; x < (x_maze_max >> 1) - 1; x++)
893 for (y = 0; y < (y_maze_max >> 1) - 1; y++) {
894 cc->x = 3 + 2 * x;
895 cc->y = 3 + 2 * y;
896 if (levl[cc->x][cc->y].typ
897 == (level.flags.corrmaze ? CORR : ROOM))
898 return;
900 panic("mazexy: can't find a place!");
902 return;
905 /* put a non-diggable boundary around the initial portion of a level map.
906 * assumes that no level will initially put things beyond the isok() range.
908 * we can't bound unconditionally on the last line with something in it,
909 * because that something might be a niche which was already reachable,
910 * so the boundary would be breached
912 * we can't bound unconditionally on one beyond the last line, because
913 * that provides a window of abuse for wallified special levels
915 void
916 bound_digging()
918 register int x, y;
919 register unsigned typ;
920 register struct rm *lev;
921 boolean found, nonwall;
922 int xmin, xmax, ymin, ymax;
924 if (Is_earthlevel(&u.uz))
925 return; /* everything diggable here */
927 found = nonwall = FALSE;
928 for (xmin = 0; !found && xmin <= COLNO; xmin++) {
929 lev = &levl[xmin][0];
930 for (y = 0; y <= ROWNO - 1; y++, lev++) {
931 typ = lev->typ;
932 if (typ != STONE) {
933 found = TRUE;
934 if (!IS_WALL(typ))
935 nonwall = TRUE;
939 xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
940 if (xmin < 0)
941 xmin = 0;
943 found = nonwall = FALSE;
944 for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
945 lev = &levl[xmax][0];
946 for (y = 0; y <= ROWNO - 1; y++, lev++) {
947 typ = lev->typ;
948 if (typ != STONE) {
949 found = TRUE;
950 if (!IS_WALL(typ))
951 nonwall = TRUE;
955 xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
956 if (xmax >= COLNO)
957 xmax = COLNO - 1;
959 found = nonwall = FALSE;
960 for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
961 lev = &levl[xmin][ymin];
962 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
963 typ = lev->typ;
964 if (typ != STONE) {
965 found = TRUE;
966 if (!IS_WALL(typ))
967 nonwall = TRUE;
971 ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
973 found = nonwall = FALSE;
974 for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
975 lev = &levl[xmin][ymax];
976 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
977 typ = lev->typ;
978 if (typ != STONE) {
979 found = TRUE;
980 if (!IS_WALL(typ))
981 nonwall = TRUE;
985 ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
987 for (x = 0; x < COLNO; x++)
988 for (y = 0; y < ROWNO; y++)
989 if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
990 #ifdef DCC30_BUG
991 lev = &levl[x][y];
992 lev->wall_info |= W_NONDIGGABLE;
993 #else
994 levl[x][y].wall_info |= W_NONDIGGABLE;
995 #endif
999 void
1000 mkportal(x, y, todnum, todlevel)
1001 xchar x, y, todnum, todlevel;
1003 /* a portal "trap" must be matched by a
1004 portal in the destination dungeon/dlevel */
1005 struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
1007 if (!ttmp) {
1008 impossible("portal on top of portal??");
1009 return;
1011 debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
1012 dungeons[todnum].dname, todlevel);
1013 ttmp->dst.dnum = todnum;
1014 ttmp->dst.dlevel = todlevel;
1015 return;
1018 void
1019 fumaroles()
1021 xchar n;
1022 boolean snd = FALSE, loud = FALSE;
1024 for (n = rn2(3) + 2; n; n--) {
1025 xchar x = rn1(COLNO - 4, 3);
1026 xchar y = rn1(ROWNO - 4, 3);
1027 if (levl[x][y].typ == LAVAPOOL) {
1028 NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
1029 clear_heros_fault(r);
1030 snd = TRUE;
1031 if (distu(x, y) < 15)
1032 loud = TRUE;
1035 if (snd && !Deaf)
1036 Norep("You hear a %swhoosh!", loud ? "loud " : "");
1040 * Special waterlevel stuff in endgame (TH).
1042 * Some of these functions would probably logically belong to some
1043 * other source files, but they are all so nicely encapsulated here.
1046 #ifdef DEBUG
1047 /* to ease the work of debuggers at this stage */
1048 #define register
1049 #endif
1051 #define CONS_OBJ 0
1052 #define CONS_MON 1
1053 #define CONS_HERO 2
1054 #define CONS_TRAP 3
1056 static struct bubble *bbubbles, *ebubbles;
1058 static struct trap *wportal;
1059 static int xmin, ymin, xmax, ymax; /* level boundaries */
1060 /* bubble movement boundaries */
1061 #define bxmin (xmin + 1)
1062 #define bymin (ymin + 1)
1063 #define bxmax (xmax - 1)
1064 #define bymax (ymax - 1)
1066 STATIC_DCL void NDECL(set_wportal);
1067 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
1068 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
1070 void
1071 movebubbles()
1073 static boolean up;
1074 register struct bubble *b;
1075 register int x, y, i, j;
1076 struct trap *btrap;
1077 static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
1078 0, 0, 0, 0, 0, 0 };
1079 static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
1080 1, 0, 0, 0, 0 };
1082 /* set up the portal the first time bubbles are moved */
1083 if (!wportal)
1084 set_wportal();
1086 vision_recalc(2);
1088 if (Is_waterlevel(&u.uz)) {
1089 /* keep attached ball&chain separate from bubble objects */
1090 if (Punished)
1091 unplacebc();
1094 * Pick up everything inside of a bubble then fill all bubble
1095 * locations.
1097 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1098 if (b->cons)
1099 panic("movebubbles: cons != null");
1100 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1101 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1102 if (b->bm[j + 2] & (1 << i)) {
1103 if (!isok(x, y)) {
1104 impossible("movebubbles: bad pos (%d,%d)", x, y);
1105 continue;
1108 /* pick up objects, monsters, hero, and traps */
1109 if (OBJ_AT(x, y)) {
1110 struct obj *olist = (struct obj *) 0, *otmp;
1111 struct container *cons =
1112 (struct container *) alloc(
1113 sizeof(struct container));
1115 while ((otmp = level.objects[x][y]) != 0) {
1116 remove_object(otmp);
1117 otmp->ox = otmp->oy = 0;
1118 otmp->nexthere = olist;
1119 olist = otmp;
1122 cons->x = x;
1123 cons->y = y;
1124 cons->what = CONS_OBJ;
1125 cons->list = (genericptr_t) olist;
1126 cons->next = b->cons;
1127 b->cons = cons;
1129 if (MON_AT(x, y)) {
1130 struct monst *mon = m_at(x, y);
1131 struct container *cons =
1132 (struct container *) alloc(
1133 sizeof(struct container));
1135 cons->x = x;
1136 cons->y = y;
1137 cons->what = CONS_MON;
1138 cons->list = (genericptr_t) mon;
1140 cons->next = b->cons;
1141 b->cons = cons;
1143 if (mon->wormno)
1144 remove_worm(mon);
1145 else
1146 remove_monster(x, y);
1148 newsym(x, y); /* clean up old position */
1149 mon->mx = mon->my = 0;
1151 if (!u.uswallow && x == u.ux && y == u.uy) {
1152 struct container *cons =
1153 (struct container *) alloc(
1154 sizeof(struct container));
1156 cons->x = x;
1157 cons->y = y;
1158 cons->what = CONS_HERO;
1159 cons->list = (genericptr_t) 0;
1161 cons->next = b->cons;
1162 b->cons = cons;
1164 if ((btrap = t_at(x, y)) != 0) {
1165 struct container *cons =
1166 (struct container *) alloc(
1167 sizeof(struct container));
1169 cons->x = x;
1170 cons->y = y;
1171 cons->what = CONS_TRAP;
1172 cons->list = (genericptr_t) btrap;
1174 cons->next = b->cons;
1175 b->cons = cons;
1178 levl[x][y] = water_pos;
1179 block_point(x, y);
1182 } else if (Is_airlevel(&u.uz)) {
1183 for (x = 0; x < COLNO; x++)
1184 for (y = 0; y < ROWNO; y++) {
1185 levl[x][y] = air_pos;
1186 unblock_point(x, y);
1191 * Every second time traverse down. This is because otherwise
1192 * all the junk that changes owners when bubbles overlap
1193 * would eventually end up in the last bubble in the chain.
1195 up = !up;
1196 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1197 register int rx = rn2(3), ry = rn2(3);
1199 mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1200 b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1203 /* put attached ball&chain back */
1204 if (Is_waterlevel(&u.uz) && Punished)
1205 placebc();
1206 vision_full_recalc = 1;
1209 /* when moving in water, possibly (1 in 3) alter the intended destination */
1210 void
1211 water_friction()
1213 register int x, y, dx, dy;
1214 register boolean eff = FALSE;
1216 if (Swimming && rn2(4))
1217 return; /* natural swimmers have advantage */
1219 if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1220 /* cancel delta x and choose an arbitrary delta y value */
1221 x = u.ux;
1222 do {
1223 dy = rn2(3) - 1; /* -1, 0, 1 */
1224 y = u.uy + dy;
1225 } while (dy && (!isok(x, y) || !is_pool(x, y)));
1226 u.dx = 0;
1227 u.dy = dy;
1228 eff = TRUE;
1229 } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1230 /* cancel delta y and choose an arbitrary delta x value */
1231 y = u.uy;
1232 do {
1233 dx = rn2(3) - 1; /* -1 .. 1 */
1234 x = u.ux + dx;
1235 } while (dx && (!isok(x, y) || !is_pool(x, y)));
1236 u.dy = 0;
1237 u.dx = dx;
1238 eff = TRUE;
1240 if (eff)
1241 pline("Water turbulence affects your movements.");
1244 void
1245 save_waterlevel(fd, mode)
1246 int fd, mode;
1248 register struct bubble *b;
1250 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1251 return;
1253 if (perform_bwrite(mode)) {
1254 int n = 0;
1255 for (b = bbubbles; b; b = b->next)
1256 ++n;
1257 bwrite(fd, (genericptr_t) &n, sizeof(int));
1258 bwrite(fd, (genericptr_t) &xmin, sizeof(int));
1259 bwrite(fd, (genericptr_t) &ymin, sizeof(int));
1260 bwrite(fd, (genericptr_t) &xmax, sizeof(int));
1261 bwrite(fd, (genericptr_t) &ymax, sizeof(int));
1262 for (b = bbubbles; b; b = b->next)
1263 bwrite(fd, (genericptr_t) b, sizeof(struct bubble));
1265 if (release_data(mode))
1266 unsetup_waterlevel();
1269 void
1270 restore_waterlevel(fd)
1271 register int fd;
1273 register struct bubble *b = (struct bubble *) 0, *btmp;
1274 register int i;
1275 int n;
1277 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1278 return;
1280 set_wportal();
1281 mread(fd, (genericptr_t) &n, sizeof(int));
1282 mread(fd, (genericptr_t) &xmin, sizeof(int));
1283 mread(fd, (genericptr_t) &ymin, sizeof(int));
1284 mread(fd, (genericptr_t) &xmax, sizeof(int));
1285 mread(fd, (genericptr_t) &ymax, sizeof(int));
1286 for (i = 0; i < n; i++) {
1287 btmp = b;
1288 b = (struct bubble *) alloc(sizeof(struct bubble));
1289 mread(fd, (genericptr_t) b, sizeof(struct bubble));
1290 if (bbubbles) {
1291 btmp->next = b;
1292 b->prev = btmp;
1293 } else {
1294 bbubbles = b;
1295 b->prev = (struct bubble *) 0;
1297 mv_bubble(b, 0, 0, TRUE);
1299 ebubbles = b;
1300 b->next = (struct bubble *) 0;
1301 was_waterlevel = TRUE;
1304 const char *
1305 waterbody_name(x, y)
1306 xchar x, y;
1308 register struct rm *lev;
1309 schar ltyp;
1311 if (!isok(x, y))
1312 return "drink"; /* should never happen */
1313 lev = &levl[x][y];
1314 ltyp = lev->typ;
1315 if (ltyp == DRAWBRIDGE_UP)
1316 ltyp = db_under_typ(lev->drawbridgemask);
1318 if (ltyp == LAVAPOOL)
1319 return "lava";
1320 else if (ltyp == ICE)
1321 return "ice";
1322 else if (ltyp == POOL)
1323 return "pool of water";
1324 else if (ltyp == WATER || Is_waterlevel(&u.uz))
1325 ; /* fall through to default return value */
1326 else if (Is_juiblex_level(&u.uz))
1327 return "swamp";
1328 else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1329 return "moat";
1331 return "water";
1334 STATIC_OVL void
1335 set_wportal()
1337 /* there better be only one magic portal on water level... */
1338 for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1339 if (wportal->ttyp == MAGIC_PORTAL)
1340 return;
1341 impossible("set_wportal(): no portal!");
1344 STATIC_OVL void
1345 setup_waterlevel()
1347 register int x, y;
1348 register int xskip, yskip;
1349 register int water_glyph = cmap_to_glyph(S_water);
1350 register int air_glyph = cmap_to_glyph(S_air);
1352 /* ouch, hardcoded... */
1354 xmin = 3;
1355 ymin = 1;
1356 xmax = 78;
1357 ymax = 20;
1359 /* set hero's memory to water */
1361 for (x = xmin; x <= xmax; x++)
1362 for (y = ymin; y <= ymax; y++)
1363 levl[x][y].glyph = Is_waterlevel(&u.uz) ? water_glyph : air_glyph;
1365 /* make bubbles */
1367 if (Is_waterlevel(&u.uz)) {
1368 xskip = 10 + rn2(10);
1369 yskip = 4 + rn2(4);
1370 } else {
1371 xskip = 6 + rn2(4);
1372 yskip = 3 + rn2(3);
1375 for (x = bxmin; x <= bxmax; x += xskip)
1376 for (y = bymin; y <= bymax; y += yskip)
1377 mk_bubble(x, y, rn2(7));
1380 STATIC_OVL void
1381 unsetup_waterlevel()
1383 register struct bubble *b, *bb;
1385 /* free bubbles */
1387 for (b = bbubbles; b; b = bb) {
1388 bb = b->next;
1389 free((genericptr_t) b);
1391 bbubbles = ebubbles = (struct bubble *) 0;
1394 STATIC_OVL void
1395 mk_bubble(x, y, n)
1396 register int x, y, n;
1399 * These bit masks make visually pleasing bubbles on a normal aspect
1400 * 25x80 terminal, which naturally results in them being mathematically
1401 * anything but symmetric. For this reason they cannot be computed
1402 * in situ, either. The first two elements tell the dimensions of
1403 * the bubble's bounding box.
1405 static uchar bm2[] = { 2, 1, 0x3 }, bm3[] = { 3, 2, 0x7, 0x7 },
1406 bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1407 bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1408 bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1409 bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1410 bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1411 *bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1412 register struct bubble *b;
1414 if (x >= bxmax || y >= bymax)
1415 return;
1416 if (n >= SIZE(bmask)) {
1417 impossible("n too large (mk_bubble)");
1418 n = SIZE(bmask) - 1;
1420 if (bmask[n][1] > MAX_BMASK) {
1421 panic("bmask size is larger than MAX_BMASK");
1423 b = (struct bubble *) alloc(sizeof(struct bubble));
1424 if ((x + (int) bmask[n][0] - 1) > bxmax)
1425 x = bxmax - bmask[n][0] + 1;
1426 if ((y + (int) bmask[n][1] - 1) > bymax)
1427 y = bymax - bmask[n][1] + 1;
1428 b->x = x;
1429 b->y = y;
1430 b->dx = 1 - rn2(3);
1431 b->dy = 1 - rn2(3);
1432 /* y dimension is the length of bitmap data - see bmask above */
1433 (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1434 (bmask[n][1] + 2) * sizeof(b->bm[0]));
1435 b->cons = 0;
1436 if (!bbubbles)
1437 bbubbles = b;
1438 if (ebubbles) {
1439 ebubbles->next = b;
1440 b->prev = ebubbles;
1441 } else
1442 b->prev = (struct bubble *) 0;
1443 b->next = (struct bubble *) 0;
1444 ebubbles = b;
1445 mv_bubble(b, 0, 0, TRUE);
1449 * The player, the portal and all other objects and monsters
1450 * float along with their associated bubbles. Bubbles may overlap
1451 * freely, and the contents may get associated with other bubbles in
1452 * the process. Bubbles are "sticky", meaning that if the player is
1453 * in the immediate neighborhood of one, he/she may get sucked inside.
1454 * This property also makes leaving a bubble slightly difficult.
1456 STATIC_OVL void
1457 mv_bubble(b, dx, dy, ini)
1458 register struct bubble *b;
1459 register int dx, dy;
1460 register boolean ini;
1462 register int x, y, i, j, colli = 0;
1463 struct container *cons, *ctemp;
1465 /* clouds move slowly */
1466 if (!Is_airlevel(&u.uz) || !rn2(6)) {
1467 /* move bubble */
1468 if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1469 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1470 dx = sgn(dx);
1471 dy = sgn(dy);
1475 * collision with level borders?
1476 * 1 = horizontal border, 2 = vertical, 3 = corner
1478 if (b->x <= bxmin)
1479 colli |= 2;
1480 if (b->y <= bymin)
1481 colli |= 1;
1482 if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1483 colli |= 2;
1484 if ((int) (b->y + b->bm[1] - 1) >= bymax)
1485 colli |= 1;
1487 if (b->x < bxmin) {
1488 pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1489 b->x = bxmin;
1491 if (b->y < bymin) {
1492 pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1493 b->y = bymin;
1495 if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1496 pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1497 bxmax);
1498 b->x = bxmax - b->bm[0] + 1;
1500 if ((int) (b->y + b->bm[1] - 1) > bymax) {
1501 pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1502 bymax);
1503 b->y = bymax - b->bm[1] + 1;
1506 /* bounce if we're trying to move off the border */
1507 if (b->x == bxmin && dx < 0)
1508 dx = -dx;
1509 if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1510 dx = -dx;
1511 if (b->y == bymin && dy < 0)
1512 dy = -dy;
1513 if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1514 dy = -dy;
1516 b->x += dx;
1517 b->y += dy;
1520 /* draw the bubbles */
1521 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1522 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1523 if (b->bm[j + 2] & (1 << i)) {
1524 if (Is_waterlevel(&u.uz)) {
1525 levl[x][y].typ = AIR;
1526 levl[x][y].lit = 1;
1527 unblock_point(x, y);
1528 } else if (Is_airlevel(&u.uz)) {
1529 levl[x][y].typ = CLOUD;
1530 levl[x][y].lit = 1;
1531 block_point(x, y);
1535 if (Is_waterlevel(&u.uz)) {
1536 /* replace contents of bubble */
1537 for (cons = b->cons; cons; cons = ctemp) {
1538 ctemp = cons->next;
1539 cons->x += dx;
1540 cons->y += dy;
1542 switch (cons->what) {
1543 case CONS_OBJ: {
1544 struct obj *olist, *otmp;
1546 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1547 otmp = olist->nexthere;
1548 place_object(olist, cons->x, cons->y);
1550 break;
1553 case CONS_MON: {
1554 struct monst *mon = (struct monst *) cons->list;
1555 (void) mnearto(mon, cons->x, cons->y, TRUE);
1556 break;
1559 case CONS_HERO: {
1560 int ux0 = u.ux, uy0 = u.uy;
1562 /* change u.ux0 and u.uy0? */
1563 u.ux = cons->x;
1564 u.uy = cons->y;
1565 newsym(ux0, uy0); /* clean up old position */
1567 if (MON_AT(cons->x, cons->y)) {
1568 mnexto(m_at(cons->x, cons->y));
1570 break;
1573 case CONS_TRAP: {
1574 struct trap *btrap = (struct trap *) cons->list;
1575 btrap->tx = cons->x;
1576 btrap->ty = cons->y;
1577 break;
1580 default:
1581 impossible("mv_bubble: unknown bubble contents");
1582 break;
1584 free((genericptr_t) cons);
1586 b->cons = 0;
1589 /* boing? */
1590 switch (colli) {
1591 case 1:
1592 b->dy = -b->dy;
1593 break;
1594 case 3:
1595 b->dy = -b->dy; /* fall through */
1596 case 2:
1597 b->dx = -b->dx;
1598 break;
1599 default:
1600 /* sometimes alter direction for fun anyway
1601 (higher probability for stationary bubbles) */
1602 if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {
1603 b->dx = 1 - rn2(3);
1604 b->dy = 1 - rn2(3);
1609 /*mkmaze.c*/