1 /* NetHack 3.6 mkmaze.c $NHDT-Date: 1559422240 2019/06/01 20:50:40 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.74 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Pasi Kallinen, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
9 /* JNetHack may be freely redistributed. See license for details. */
13 #include "lev.h" /* save & restore info */
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} };
21 STATIC_DCL int FDECL(iswall, (int, int));
22 STATIC_DCL int FDECL(iswall_or_stone, (int, int));
23 STATIC_DCL boolean FDECL(is_solid, (int, int));
24 STATIC_DCL int FDECL(extend_spine, (int[3][3], int, int, int));
25 STATIC_DCL boolean FDECL(okay, (int, int, int));
26 STATIC_DCL void FDECL(maze0xy, (coord *));
27 STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
28 XCHAR_P, XCHAR_P, XCHAR_P,
29 XCHAR_P, BOOLEAN_P, d_level *));
30 STATIC_DCL void NDECL(baalz_fixup);
31 STATIC_DCL void NDECL(setup_waterlevel);
32 STATIC_DCL void NDECL(unsetup_waterlevel);
33 STATIC_DCL void FDECL(check_ransacked, (char *));
34 STATIC_DCL void FDECL(migr_booty_item, (int, const char *));
35 STATIC_DCL void FDECL(migrate_orc, (struct monst *, unsigned long));
36 STATIC_DCL void NDECL(stolen_booty);
38 /* adjust a coordinate one step in the specified direction */
39 #define mz_move(X, Y, dir) \
42 case 0: --(Y); break; \
43 case 1: (X)++; break; \
44 case 2: (Y)++; break; \
45 case 3: --(X); break; \
46 default: panic("mz_move: bad direction %d", dir); \
58 type = levl[x][y].typ;
59 return (IS_WALL(type) || IS_DOOR(type)
60 || type == SDOOR || type == IRONBARS);
67 /* out of bounds = stone */
71 return (levl[x][y].typ == STONE || iswall(x, y));
74 /* return TRUE if out of bounds, wall or rock */
79 return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
83 * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
84 * a wall spine in the (dx,dy) direction. Return 0 otherwise.
86 * To extend a wall spine in that direction, first there must be a wall there.
87 * Then, extend a spine unless the current position is surrounded by walls
88 * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W'
89 * a wall, '.' a room, 'a' anything (we don't care), and our direction is
90 * (0,1) - South or down - then:
93 * W x W This would not extend a spine from x down
94 * W W W (a corridor of walls is formed).
97 * W x W This would extend a spine from x down.
101 extend_spine(locale, wall_there, dx, dy)
103 int wall_there, dx, dy;
110 if (wall_there) { /* wall in that direction */
112 if (locale[1][0] && locale[1][2] /* EW are wall/stone */
113 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
119 if (locale[0][1] && locale[2][1] /* NS are wall/stone */
120 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
133 /* Remove walls totally surrounded by stone */
135 wall_cleanup(x1, y1, x2, y2)
142 /* sanity check on incoming variables */
143 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
144 panic("wall_cleanup: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
146 /* change walls surrounded by rock to rock. */
147 for (x = x1; x <= x2; x++)
148 for (y = y1; y <= y2; y++) {
149 if (within_bounded_area(x, y,
150 bughack.inarea.x1, bughack.inarea.y1,
151 bughack.inarea.x2, bughack.inarea.y2))
155 if (IS_WALL(type) && type != DBWALL) {
156 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
157 && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
158 && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
159 && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
165 /* Correct wall types so they extend and connect to each other */
167 fix_wall_spines(x1, y1, x2, y2)
173 int FDECL((*loc_f), (int, int));
175 int locale[3][3]; /* rock or wall status surrounding positions */
178 * Value 0 represents a free-standing wall. It could be anything,
179 * so even though this table says VWALL, we actually leave whatever
180 * typ was there alone.
182 static xchar spine_array[16] = { VWALL, HWALL, HWALL, HWALL,
183 VWALL, TRCORNER, TLCORNER, TDWALL,
184 VWALL, BRCORNER, BLCORNER, TUWALL,
185 VWALL, TLWALL, TRWALL, CROSSWALL };
187 /* sanity check on incoming variables */
188 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
189 panic("wall_extends: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
191 /* set the correct wall type. */
192 for (x = x1; x <= x2; x++)
193 for (y = y1; y <= y2; y++) {
196 if (!(IS_WALL(type) && type != DBWALL))
199 /* set the locations TRUE if rock or wall or out of bounds */
200 loc_f = within_bounded_area(x, y, /* for baalz insect */
201 bughack.inarea.x1, bughack.inarea.y1,
202 bughack.inarea.x2, bughack.inarea.y2)
205 locale[0][0] = (*loc_f)(x - 1, y - 1);
206 locale[1][0] = (*loc_f)(x, y - 1);
207 locale[2][0] = (*loc_f)(x + 1, y - 1);
209 locale[0][1] = (*loc_f)(x - 1, y);
210 locale[2][1] = (*loc_f)(x + 1, y);
212 locale[0][2] = (*loc_f)(x - 1, y + 1);
213 locale[1][2] = (*loc_f)(x, y + 1);
214 locale[2][2] = (*loc_f)(x + 1, y + 1);
216 /* determine if wall should extend to each direction NSEW */
217 bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
218 | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
219 | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
220 | extend_spine(locale, iswall(x - 1, y), -1, 0);
222 /* don't change typ if wall is free-standing */
224 lev->typ = spine_array[bits];
229 wallification(x1, y1, x2, y2)
232 wall_cleanup(x1, y1, x2, y2);
233 fix_wall_spines(x1, y1, x2, y2);
243 if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max
244 || levl[x][y].typ != STONE)
249 /* find random starting point for maze generation */
254 cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
255 cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
262 * pos is inside restricted region (lx,ly,hx,hy) OR
263 * NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
266 bad_location(x, y, lx, ly, hx, hy)
268 xchar lx, ly, hx, hy;
270 return (boolean) (occupied(x, y)
271 || within_bounded_area(x, y, lx, ly, hx, hy)
272 || !((levl[x][y].typ == CORR && level.flags.is_maze_lev)
273 || levl[x][y].typ == ROOM
274 || levl[x][y].typ == AIR));
277 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
278 and place something (based on rtype) in that region */
280 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
281 xchar lx, ly, hx, hy;
282 xchar nlx, nly, nhx, nhy;
290 if (!lx) { /* default to whole level */
292 * if there are rooms and this a branch, let place_branch choose
293 * the branch location (to avoid putting branches in corridors).
295 if (rtype == LR_BRANCH && nroom) {
296 place_branch(Is_branchlev(&u.uz), 0, 0);
300 lx = 1; /* column 0 is not used */
302 ly = 0; /* 3.6.0 and earlier erroneously had 1 here */
306 /* first a probabilistic approach */
308 oneshot = (lx == hx && ly == hy);
309 for (trycnt = 0; trycnt < 200; trycnt++) {
310 x = rn1((hx - lx) + 1, lx);
311 y = rn1((hy - ly) + 1, ly);
312 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
316 /* then a deterministic one */
318 for (x = lx; x <= hx; x++)
319 for (y = ly; y <= hy; y++)
320 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, TRUE, lev))
323 impossible("Couldn't place lregion type %d!", rtype);
327 put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev)
329 xchar nlx, nly, nhx, nhy;
336 if (bad_location(x, y, nlx, nly, nhx, nhy)) {
338 return FALSE; /* caller should try again */
340 /* Must make do with the only location possible;
341 avoid failure due to a misplaced trap.
342 It might still fail if there's a dungeon feature here. */
343 struct trap *t = t_at(x, y);
345 if (t && t->ttyp != MAGIC_PORTAL && t->ttyp != VIBRATING_SQUARE)
347 if (bad_location(x, y, nlx, nly, nhx, nhy))
355 /* "something" means the player in this case */
356 if ((mtmp = m_at(x, y)) != 0) {
357 /* move the monster if no choice, or just try again */
359 if (!rloc(mtmp, TRUE))
367 mkportal(x, y, lev->dnum, lev->dlevel);
371 mkstairs(x, y, (char) rtype, (struct mkroom *) 0);
374 place_branch(Is_branchlev(&u.uz), x, y);
380 /* fix up Baalzebub's lair, which depicts a level-sized beetle;
381 its legs are walls within solid rock--regular wallification
382 classifies them as superfluous and gets rid of them */
387 int x, y, lastx, lasty;
390 * baalz level's nondiggable region surrounds the "insect" and rooms.
391 * The outermost perimeter of that region is subject to wall cleanup
392 * (hence 'x + 1' and 'y + 1' for starting don't-clean column and row,
393 * 'lastx - 1' and 'lasty - 1' for ending don't-clean column and row)
394 * and the interior is protected against that (in wall_cleanup()).
396 * Assumes level.flags.corrmaze is True, otherwise the bug legs will
397 * have already been "cleaned" away by general wallification.
400 /* find low and high x for to-be-wallified portion of level */
402 for (lastx = x = 0; x < COLNO; ++x)
403 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
405 bughack.inarea.x1 = x + 1;
408 bughack.inarea.x2 = ((lastx > bughack.inarea.x1) ? lastx : x) - 1;
409 /* find low and high y for to-be-wallified portion of level */
410 x = bughack.inarea.x1;
411 for (lasty = y = 0; y < ROWNO; ++y)
412 if ((levl[x][y].wall_info & W_NONDIGGABLE) != 0) {
414 bughack.inarea.y1 = y + 1;
417 bughack.inarea.y2 = ((lasty > bughack.inarea.y1) ? lasty : y) - 1;
418 /* two pools mark where special post-wallify fix-ups are needed */
419 for (x = bughack.inarea.x1; x <= bughack.inarea.x2; ++x)
420 for (y = bughack.inarea.y1; y <= bughack.inarea.y2; ++y)
421 if (levl[x][y].typ == POOL) {
422 levl[x][y].typ = HWALL;
423 if (bughack.delarea.x1 == COLNO)
424 bughack.delarea.x1 = x, bughack.delarea.y1 = y;
426 bughack.delarea.x2 = x, bughack.delarea.y2 = y;
427 } else if (levl[x][y].typ == IRONBARS) {
428 /* novelty effect; allowing digging in front of 'eyes' */
429 levl[x - 1][y].wall_info &= ~W_NONDIGGABLE;
431 levl[x - 2][y].wall_info &= ~W_NONDIGGABLE;
434 wallification(max(bughack.inarea.x1 - 2, 1),
435 max(bughack.inarea.y1 - 2, 0),
436 min(bughack.inarea.x2 + 2, COLNO - 1),
437 min(bughack.inarea.y2 + 2, ROWNO - 1));
439 /* bughack hack for rear-most legs on baalz level; first joint on
440 both top and bottom gets a bogus extra connection to room area,
441 producing unwanted rectangles; change back to separated legs */
442 x = bughack.delarea.x1, y = bughack.delarea.y1;
443 if (isok(x, y) && levl[x][y].typ == TLWALL
444 && isok(x, y + 1) && levl[x][y + 1].typ == TUWALL) {
445 levl[x][y].typ = BRCORNER;
446 levl[x][y + 1].typ = HWALL;
447 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
448 (void) rloc(mtmp, FALSE);
450 x = bughack.delarea.x2, y = bughack.delarea.y2;
451 if (isok(x, y) && levl[x][y].typ == TLWALL
452 && isok(x, y - 1) && levl[x][y - 1].typ == TDWALL) {
453 levl[x][y].typ = TRCORNER;
454 levl[x][y - 1].typ = HWALL;
455 if ((mtmp = m_at(x, y)) != 0) /* something at temporary pool... */
456 (void) rloc(mtmp, FALSE);
459 /* reset bughack region; set low end to <COLNO,ROWNO> so that
460 within_bounded_region() in fix_wall_spines() will fail
461 most quickly--on its first test--when loading other levels */
462 bughack.inarea.x1 = bughack.delarea.x1 = COLNO;
463 bughack.inarea.y1 = bughack.delarea.y1 = ROWNO;
464 bughack.inarea.x2 = bughack.delarea.x2 = 0;
465 bughack.inarea.y2 = bughack.delarea.y2 = 0;
468 /* this is special stuff that the level compiler cannot (yet) handle */
472 lev_region *r = lregions;
475 struct mkroom *croom;
476 boolean added_branch = FALSE;
478 if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
479 level.flags.hero_memory = 0;
480 /* water level is an odd beast - it has to be set up
481 before calling place_lregions etc. */
484 for (x = 0; x < num_lregions; x++, r++) {
491 if (*r->rname.str >= '0' && *r->rname.str <= '9') {
492 /* "chutes and ladders" */
494 lev.dlevel = atoi(r->rname.str);
496 s_level *sp = find_level(r->rname.str);
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);
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;
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;
534 /* place_lregion gets called from goto_level() */
539 free((genericptr_t) r->rname.str), r->rname.str = 0;
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);
547 /* Still need to add some stuff to level file */
548 if (Is_medusa_level(&u.uz)) {
552 croom = &rooms[0]; /* only one room on the medusa level */
553 for (tryct = rnd(4); tryct; tryct--) {
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],
561 /* set_corpsenm() handles weight too */
562 set_corpsenm(otmp, rndmonnum());
568 otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
569 else /* Medusa statues don't contain books */
571 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
572 somex(croom), somey(croom), CORPSTAT_NONE);
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());
580 } else if (Is_wiz1_level(&u.uz)) {
581 croom = search_special(MORGUE);
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);
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);
605 create_secret_door(croom, W_ANY);
606 } else if (on_level(&u.uz, &orcus_level)) {
607 struct monst *mtmp, *mtmp2;
609 /* it's a ghost town, get rid of shopkeepers */
610 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
615 } else if (on_level(&u.uz, &baalzebub_level)) {
616 /* custom wallify the "beetle" potion of the level */
618 } else if (u.uz.dnum == mines_dnum && ransacked) {
623 free((genericptr_t) lregions), lregions = 0;
631 /* this kludge only works as long as orctown is minetn-1 */
632 ransacked = (u.uz.dnum == mines_dnum && !strcmp(s, "minetn-1"));
636 static const char *orcfruit[] = { "paddle cactus", "dwarven root" };
639 migrate_orc(mtmp, mflags)
641 unsigned long mflags;
643 int nlev, max_depth, cur_depth;
646 cur_depth = (int) depth(&u.uz);
647 max_depth = dunlevs_in_dungeon(&u.uz)
648 + (dungeons[u.uz.dnum].depth_start - 1);
649 if (mflags == ORC_LEADER) {
650 /* Note that the orc leader will take possession of any
651 * remaining stuff not already delivered to other
652 * orcs between here and the bottom of the mines.
655 /* once in a blue moon, he won't be at the very bottom */
658 mtmp->mspare1 |= MIGR_LEFTOVERS;
660 nlev = rn2((max_depth - cur_depth) + 1) + cur_depth;
661 if (nlev == cur_depth)
663 if (nlev > max_depth)
665 mtmp->mspare1 = (mtmp->mspare1 & ~MIGR_LEFTOVERS);
667 get_level(&dest, nlev);
668 migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0);
672 shiny_orc_stuff(mtmp)
675 int gemprob, goldprob, otyp;
677 boolean is_captain = (mtmp->data == &mons[PM_ORC_CAPTAIN]);
680 goldprob = is_captain ? 600 : 300;
681 gemprob = goldprob / 4;
682 if (rn2(1000) < goldprob) {
683 if ((otmp = mksobj(GOLD_PIECE, TRUE, FALSE)) != 0) {
684 otmp->quan = 1L + rnd(goldprob);
685 otmp->owt = weight(otmp);
686 add_to_minv(mtmp, otmp);
689 if (rn2(1000) < gemprob) {
690 if ((otmp = mkobj(GEM_CLASS, FALSE)) != 0) {
691 if (otmp->otyp == ROCK)
694 add_to_minv(mtmp, otmp);
697 if (is_captain || !rn2(8)) {
698 otyp = shiny_obj(RING_CLASS);
699 if (otyp != STRANGE_OBJECT && (otmp = mksobj(otyp, TRUE, FALSE)) != 0)
700 add_to_minv(mtmp, otmp);
704 migr_booty_item(otyp, gang)
710 otmp = mksobj_migr_to_species(otyp, (unsigned long) M2_ORC, TRUE, FALSE);
712 new_oname(otmp, strlen(gang) + 1); /* removes old name if present */
713 Strcpy(ONAME(otmp), gang);
714 if (otyp >= TRIPE_RATION && otyp <= TIN) {
715 if (otyp == SLIME_MOLD)
716 otmp->spe = fruitadd((char *) orcfruit[rn2(SIZE(orcfruit))],
718 otmp->quan += (long) rn2(3);
719 otmp->owt = weight(otmp);
725 stolen_booty(VOID_ARGS)
727 char *gang, gang_name[BUFSZ];
732 * --------------------------------------------------------
735 * A tragic accident has occurred in Frontier Town...
736 * It has been overrun by orcs.
738 * The booty that the orcs took from the town is now
739 * in the possession of the orcs that did this and
740 * have long since fled the level.
741 * --------------------------------------------------------
744 gang = rndorcname(gang_name);
745 /* create the stuff that the gang took */
747 for (i = 0; i < cnt; ++i)
748 migr_booty_item(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, gang);
750 for (i = 0; i < cnt; ++i)
751 migr_booty_item(SKELETON_KEY, gang);
752 otyp = rn2((GAUNTLETS_OF_DEXTERITY - LEATHER_GLOVES) + 1) + LEATHER_GLOVES;
753 migr_booty_item(otyp, gang);
755 for (i = 0; i < cnt; ++i) {
756 /* Food items - but no lembas! (or some other weird things) */
757 otyp = rn2((TIN - TRIPE_RATION) + 1) + TRIPE_RATION;
758 if (otyp != LEMBAS_WAFER && otyp != GLOB_OF_GRAY_OOZE
759 && otyp != GLOB_OF_BROWN_PUDDING && otyp != GLOB_OF_GREEN_SLIME
760 && otyp != GLOB_OF_BLACK_PUDDING && otyp != MEAT_STICK
761 && otyp != MEATBALL && otyp != MEAT_STICK && otyp != MEAT_RING
762 && otyp != HUGE_CHUNK_OF_MEAT && otyp != CORPSE)
763 migr_booty_item(otyp, gang);
765 migr_booty_item(rn2(2) ? LONG_SWORD : SILVER_SABER, gang);
766 /* create the leader of the orc gang */
767 mtmp = makemon(&mons[PM_ORC_CAPTAIN], 0, 0, MM_NONAME);
769 mtmp = christen_monst(mtmp, upstart(gang));
771 shiny_orc_stuff(mtmp);
772 migrate_orc(mtmp, ORC_LEADER);
774 /* Make most of the orcs on the level be part of the invading gang */
775 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
776 if (DEADMONSTER(mtmp))
779 if (is_orc(mtmp->data) && !has_mname(mtmp) && rn2(10)) {
781 * We'll consider the orc captain from the level
782 * .des file to be the captain of a rival orc horde
783 * who is there to see what has transpired, and to
784 * contemplate future action.
786 * Don't christen the orc captain as a subordinate
787 * member of the main orc horde.
789 if (mtmp->data != &mons[PM_ORC_CAPTAIN])
790 mtmp = christen_orc(mtmp, upstart(gang), "");
793 /* Lastly, ensure there's several more orcs from the gang along the way.
794 * The mechanics are such that they aren't actually identified as
795 * members of the invading gang until they get their spoils assigned
796 * to the inventory; handled during that assignment.
799 for (i = 0; i < cnt; ++i) {
802 mtyp = rn2((PM_ORC_SHAMAN - PM_ORC) + 1) + PM_ORC;
803 mtmp = makemon(&mons[mtyp], 0, 0, MM_NONAME);
805 shiny_orc_stuff(mtmp);
806 migrate_orc(mtmp, 0UL);
818 return (x >= 2 && y >= 2
819 && x < x_maze_max && y < y_maze_max && isok(x, y));
823 maze_remove_deadends(typ)
827 int x, y, dir, idx, idx2, dx, dy, dx2, dy2;
829 dirok[0] = 0; /* lint suppression */
830 for (x = 2; x < x_maze_max; x++)
831 for (y = 2; y < y_maze_max; y++)
832 if (ACCESSIBLE(levl[x][y].typ) && (x % 2) && (y % 2)) {
834 for (dir = 0; dir < 4; dir++) {
835 /* note: mz_move() is a macro which modifies
836 one of its first two parameters */
839 mz_move(dx, dy, dir);
840 if (!maze_inbounds(dx, dy)) {
844 mz_move(dx2, dy2, dir);
845 mz_move(dx2, dy2, dir);
846 if (!maze_inbounds(dx2, dy2)) {
850 if (!ACCESSIBLE(levl[dx][dy].typ)
851 && ACCESSIBLE(levl[dx2][dy2].typ)) {
856 if (idx2 >= 3 && idx > 0) {
859 dir = dirok[rn2(idx)];
860 mz_move(dx, dy, dir);
861 levl[dx][dy].typ = typ;
866 /* Create a maze with specified corridor width and wall thickness
867 * TODO: rewrite walkfrom so it works on temp space, not levl
870 create_maze(corrwid, wallthick)
876 int tmp_xmax = x_maze_max;
877 int tmp_ymax = y_maze_max;
884 else if (wallthick > 5)
889 else if (corrwid > 5)
892 scale = corrwid + wallthick;
893 rdx = (x_maze_max / scale);
894 rdy = (y_maze_max / scale);
896 if (level.flags.corrmaze)
897 for (x = 2; x < (rdx * 2); x++)
898 for (y = 2; y < (rdy * 2); y++)
899 levl[x][y].typ = STONE;
901 for (x = 2; x <= (rdx * 2); x++)
902 for (y = 2; y <= (rdy * 2); y++)
903 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
905 /* set upper bounds for maze0xy and walkfrom */
906 x_maze_max = (rdx * 2);
907 y_maze_max = (rdy * 2);
911 walkfrom((int) mm.x, (int) mm.y, 0);
914 maze_remove_deadends((level.flags.corrmaze) ? CORR : ROOM);
917 x_maze_max = tmp_xmax;
918 y_maze_max = tmp_ymax;
920 /* scale maze up if needed */
922 char tmpmap[COLNO][ROWNO];
925 /* back up the existing smaller maze */
926 for (x = 1; x < x_maze_max; x++)
927 for (y = 1; y < y_maze_max; y++) {
928 tmpmap[x][y] = levl[x][y].typ;
933 while (rx < x_maze_max) {
934 int mx = (x % 2) ? corrwid
935 : ((x == 2 || x == (rdx * 2)) ? 1
938 while (ry < y_maze_max) {
940 int my = (y % 2) ? corrwid
941 : ((y == 2 || y == (rdy * 2)) ? 1
943 for (dx = 0; dx < mx; dx++)
944 for (dy = 0; dy < my; dy++) {
945 if (rx+dx >= x_maze_max
946 || ry+dy >= y_maze_max)
948 levl[rx + dx][ry + dy].typ = tmpmap[x][y];
967 s_level *sp = Is_special(&u.uz);
971 if (sp && sp->rndlevs)
972 Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
974 Strcpy(protofile, s);
975 } else if (*(dungeons[u.uz.dnum].proto)) {
976 if (dunlevs_in_dungeon(&u.uz) > 1) {
977 if (sp && sp->rndlevs)
978 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
979 dunlev(&u.uz), rnd((int) sp->rndlevs));
981 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
983 } else if (sp && sp->rndlevs) {
984 Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
985 rnd((int) sp->rndlevs));
987 Strcpy(protofile, dungeons[u.uz.dnum].proto);
990 Strcpy(protofile, "");
992 /* SPLEVTYPE format is "level-choice,level-choice"... */
993 if (wizard && *protofile && sp && sp->rndlevs) {
994 char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
996 /* rindex always succeeds due to code in prior block */
997 int len = (int) ((rindex(protofile, '-') - protofile) + 1);
1000 if (!strncmp(ep, protofile, len)) {
1001 int pick = atoi(ep + len);
1002 /* use choice only if valid */
1003 if (pick > 0 && pick <= (int) sp->rndlevs)
1004 Sprintf(protofile + len, "%d", pick);
1007 ep = index(ep, ',');
1016 check_ransacked(protofile);
1017 Strcat(protofile, LEV_EXT);
1018 if (load_special(protofile)) {
1019 /* some levels can end up with monsters
1020 on dead mon list, including light source monsters */
1022 return; /* no mazification right now */
1024 impossible("Couldn't load \"%s\" - making a maze.", protofile);
1027 level.flags.is_maze_lev = TRUE;
1028 level.flags.corrmaze = !rn2(3);
1030 if (!Invocation_lev(&u.uz) && rn2(2)) {
1031 int corrscale = rnd(4);
1032 create_maze(corrscale,rnd(4)-corrscale);
1037 if (!level.flags.corrmaze)
1038 wallification(2, 2, x_maze_max, y_maze_max);
1041 mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
1042 if (!Invocation_lev(&u.uz)) {
1044 mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
1045 } else { /* choose "vibrating square" location */
1046 #define x_maze_min 2
1047 #define y_maze_min 2
1049 * Pick a position where the stairs down to Moloch's Sanctum
1050 * level will ultimately be created. At that time, an area
1051 * will be altered: walls removed, moat and traps generated,
1052 * boulders destroyed. The position picked here must ensure
1053 * that that invocation area won't extend off the map.
1055 * We actually allow up to 2 squares around the usual edge of
1056 * the area to get truncated; see mkinvokearea(mklev.c).
1058 #define INVPOS_X_MARGIN (6 - 2)
1059 #define INVPOS_Y_MARGIN (5 - 2)
1060 #define INVPOS_DISTANCE 11
1061 int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
1062 y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
1064 if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
1065 || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
1066 debugpline2("inv_pos: maze is too small! (%d x %d)",
1067 x_maze_max, y_maze_max);
1069 inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
1071 x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
1072 y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
1073 /* we don't want it to be too near the stairs, nor
1074 to be on a spot that's already in use (wall|trap) */
1075 } while (x == xupstair || y == yupstair /*(direct line)*/
1076 || abs(x - xupstair) == abs(y - yupstair)
1077 || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
1078 || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
1081 maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
1082 #undef INVPOS_X_MARGIN
1083 #undef INVPOS_Y_MARGIN
1084 #undef INVPOS_DISTANCE
1089 /* place branch stair or portal */
1090 place_branch(Is_branchlev(&u.uz), 0, 0);
1092 for (x = rn1(8, 11); x; x--) {
1094 (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
1096 for (x = rn1(10, 2); x; x--) {
1098 (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
1100 for (x = rn2(3); x; x--) {
1102 (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
1104 for (x = rn1(5, 7); x; x--) {
1106 (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
1108 for (x = rn1(6, 7); x; x--) {
1110 (void) mkgold(0L, mm.x, mm.y);
1112 for (x = rn1(6, 7); x; x--)
1113 mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
1117 /* Make the mazewalk iterative by faking a stack. This is needed to
1118 * ensure the mazewalk is successful in the limited stack space of
1119 * the program. This iterative version uses the minimum amount of stack
1120 * that is totally safe.
1127 #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */
1128 char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
1133 if (level.flags.corrmaze)
1140 mazex[pos] = (char) x;
1141 mazey[pos] = (char) y;
1143 x = (int) mazex[pos];
1144 y = (int) mazey[pos];
1145 if (!IS_DOOR(levl[x][y].typ)) {
1146 /* might still be on edge of MAP, so don't overwrite */
1147 levl[x][y].typ = typ;
1148 levl[x][y].flags = 0;
1151 for (a = 0; a < 4; a++)
1159 levl[x][y].typ = typ;
1163 panic("Overflow in walkfrom");
1164 mazex[pos] = (char) x;
1165 mazey[pos] = (char) y;
1180 if (level.flags.corrmaze)
1186 if (!IS_DOOR(levl[x][y].typ)) {
1187 /* might still be on edge of MAP, so don't overwrite */
1188 levl[x][y].typ = typ;
1189 levl[x][y].flags = 0;
1194 for (a = 0; a < 4; a++)
1201 levl[x][y].typ = typ;
1203 walkfrom(x, y, typ);
1208 /* find random point in generated corridors,
1209 so we don't create items in moats, bunkers, or walls */
1217 cc->x = 1 + rn2(x_maze_max);
1218 cc->y = 1 + rn2(y_maze_max);
1221 && levl[cc->x][cc->y].typ
1222 != (level.flags.corrmaze ? CORR : ROOM));
1227 for (x = 1; x < x_maze_max; x++)
1228 for (y = 1; y < y_maze_max; y++) {
1231 if (levl[cc->x][cc->y].typ
1232 == (level.flags.corrmaze ? CORR : ROOM))
1235 panic("mazexy: can't find a place!");
1240 /* put a non-diggable boundary around the initial portion of a level map.
1241 * assumes that no level will initially put things beyond the isok() range.
1243 * we can't bound unconditionally on the last line with something in it,
1244 * because that something might be a niche which was already reachable,
1245 * so the boundary would be breached
1247 * we can't bound unconditionally on one beyond the last line, because
1248 * that provides a window of abuse for wallified special levels
1256 boolean found, nonwall;
1257 int xmin, xmax, ymin, ymax;
1259 if (Is_earthlevel(&u.uz))
1260 return; /* everything diggable here */
1262 found = nonwall = FALSE;
1263 for (xmin = 0; !found && xmin <= COLNO; xmin++) {
1264 lev = &levl[xmin][0];
1265 for (y = 0; y <= ROWNO - 1; y++, lev++) {
1274 xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1278 found = nonwall = FALSE;
1279 for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
1280 lev = &levl[xmax][0];
1281 for (y = 0; y <= ROWNO - 1; y++, lev++) {
1290 xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1294 found = nonwall = FALSE;
1295 for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
1296 lev = &levl[xmin][ymin];
1297 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1306 ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1308 found = nonwall = FALSE;
1309 for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
1310 lev = &levl[xmin][ymax];
1311 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
1320 ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
1322 for (x = 0; x < COLNO; x++)
1323 for (y = 0; y < ROWNO; y++)
1324 if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
1327 lev->wall_info |= W_NONDIGGABLE;
1329 levl[x][y].wall_info |= W_NONDIGGABLE;
1335 mkportal(x, y, todnum, todlevel)
1336 xchar x, y, todnum, todlevel;
1338 /* a portal "trap" must be matched by a
1339 portal in the destination dungeon/dlevel */
1340 struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
1343 impossible("portal on top of portal??");
1346 debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
1347 dungeons[todnum].dname, todlevel);
1348 ttmp->dst.dnum = todnum;
1349 ttmp->dst.dlevel = todlevel;
1357 boolean snd = FALSE, loud = FALSE;
1359 for (n = rn2(3) + 2; n; n--) {
1360 xchar x = rn1(COLNO - 4, 3);
1361 xchar y = rn1(ROWNO - 4, 3);
1363 if (levl[x][y].typ == LAVAPOOL) {
1364 NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
1366 clear_heros_fault(r);
1368 if (distu(x, y) < 15)
1374 Norep("You hear a %swhoosh!", loud ? "loud " : ""); /* Deaf-aware */
1376 Norep("%s
\83V
\83\85\81[
\82Æ
\82¢
\82¤
\89¹
\82ð
\95·
\82¢
\82½
\81I", loud ? "
\91Ã¥
\82«
\82È" : ""); /* Deaf-aware */
1381 * Special waterlevel stuff in endgame (TH).
1383 * Some of these functions would probably logically belong to some
1384 * other source files, but they are all so nicely encapsulated here.
1387 static struct bubble *bbubbles, *ebubbles;
1389 static struct trap *wportal;
1390 static int xmin, ymin, xmax, ymax; /* level boundaries */
1391 /* bubble movement boundaries */
1392 #define bxmin (xmin + 1)
1393 #define bymin (ymin + 1)
1394 #define bxmax (xmax - 1)
1395 #define bymax (ymax - 1)
1397 STATIC_DCL void NDECL(set_wportal);
1398 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
1399 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
1404 static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
1406 static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
1408 static boolean up = FALSE;
1410 struct container *cons;
1412 int x, y, i, j, bcpin = 0;
1414 /* set up the portal the first time bubbles are moved */
1420 if (Is_waterlevel(&u.uz)) {
1421 /* keep attached ball&chain separate from bubble objects */
1423 bcpin = unplacebc_and_covet_placebc();
1426 * Pick up everything inside of a bubble then fill all bubble
1429 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1431 panic("movebubbles: cons != null");
1432 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1433 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1434 if (b->bm[j + 2] & (1 << i)) {
1436 impossible("movebubbles: bad pos (%d,%d)", x, y);
1440 /* pick up objects, monsters, hero, and traps */
1442 struct obj *olist = (struct obj *) 0, *otmp;
1444 while ((otmp = level.objects[x][y]) != 0) {
1445 remove_object(otmp);
1446 otmp->ox = otmp->oy = 0;
1447 otmp->nexthere = olist;
1451 cons = (struct container *) alloc(sizeof *cons);
1454 cons->what = CONS_OBJ;
1455 cons->list = (genericptr_t) olist;
1456 cons->next = b->cons;
1460 struct monst *mon = m_at(x, y);
1462 cons = (struct container *) alloc(sizeof *cons);
1465 cons->what = CONS_MON;
1466 cons->list = (genericptr_t) mon;
1468 cons->next = b->cons;
1474 remove_monster(x, y);
1476 newsym(x, y); /* clean up old position */
1477 mon->mx = mon->my = 0;
1478 mon->mstate |= MON_BUBBLEMOVE;
1480 if (!u.uswallow && x == u.ux && y == u.uy) {
1481 cons = (struct container *) alloc(sizeof *cons);
1484 cons->what = CONS_HERO;
1485 cons->list = (genericptr_t) 0;
1487 cons->next = b->cons;
1490 if ((btrap = t_at(x, y)) != 0) {
1491 cons = (struct container *) alloc(sizeof *cons);
1494 cons->what = CONS_TRAP;
1495 cons->list = (genericptr_t) btrap;
1497 cons->next = b->cons;
1501 levl[x][y] = water_pos;
1505 } else if (Is_airlevel(&u.uz)) {
1506 boolean xedge, yedge;
1508 for (x = 1; x <= (COLNO - 1); x++)
1509 for (y = 0; y <= (ROWNO - 1); y++) {
1510 levl[x][y] = air_pos;
1511 unblock_point(x, y);
1512 /* all air or all cloud around the perimeter of the Air
1513 level tends to look strange; break up the pattern */
1514 xedge = (boolean) (x < bxmin || x > bxmax);
1515 yedge = (boolean) (y < bymin || y > bymax);
1516 if (xedge || yedge) {
1517 if (!rn2(xedge ? 3 : 5)) {
1518 levl[x][y].typ = CLOUD;
1526 * Every second time traverse down. This is because otherwise
1527 * all the junk that changes owners when bubbles overlap
1528 * would eventually end up in the last bubble in the chain.
1531 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1532 int rx = rn2(3), ry = rn2(3);
1534 mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1535 b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1538 /* put attached ball&chain back */
1539 if (Is_waterlevel(&u.uz) && Punished)
1540 lift_covet_and_placebc(bcpin);
1541 vision_full_recalc = 1;
1544 /* when moving in water, possibly (1 in 3) alter the intended destination */
1549 boolean eff = FALSE;
1551 if (Swimming && rn2(4))
1552 return; /* natural swimmers have advantage */
1554 if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1555 /* cancel delta x and choose an arbitrary delta y value */
1558 dy = rn2(3) - 1; /* -1, 0, 1 */
1560 } while (dy && (!isok(x, y) || !is_pool(x, y)));
1564 } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1565 /* cancel delta y and choose an arbitrary delta x value */
1568 dx = rn2(3) - 1; /* -1 .. 1 */
1570 } while (dx && (!isok(x, y) || !is_pool(x, y)));
1577 pline("Water turbulence affects your movements.");
1579 pline("
\90\85\82Ì
\97¬
\82ê
\82ª
\82Â
\82È
\82½
\82Ì
\93®
\82«
\82É
\89e
\8b¿
\82ð
\97^
\82¦
\82½
\81D");
1583 save_waterlevel(fd, mode)
1588 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1591 if (perform_bwrite(mode)) {
1593 for (b = bbubbles; b; b = b->next)
1595 bwrite(fd, (genericptr_t) &n, sizeof n);
1596 bwrite(fd, (genericptr_t) &xmin, sizeof xmin);
1597 bwrite(fd, (genericptr_t) &ymin, sizeof ymin);
1598 bwrite(fd, (genericptr_t) &xmax, sizeof xmax);
1599 bwrite(fd, (genericptr_t) &ymax, sizeof ymax);
1600 for (b = bbubbles; b; b = b->next)
1601 bwrite(fd, (genericptr_t) b, sizeof *b);
1603 if (release_data(mode))
1604 unsetup_waterlevel();
1608 restore_waterlevel(fd)
1611 struct bubble *b = (struct bubble *) 0, *btmp;
1614 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1617 if (fd == -1) { /* special handling for restore in goto_level() */
1619 impossible("restore_waterlevel: returning to %s?",
1620 Is_waterlevel(&u.uz) ? "Water" : "Air");
1626 mread(fd, (genericptr_t) &n, sizeof n);
1627 mread(fd, (genericptr_t) &xmin, sizeof xmin);
1628 mread(fd, (genericptr_t) &ymin, sizeof ymin);
1629 mread(fd, (genericptr_t) &xmax, sizeof xmax);
1630 mread(fd, (genericptr_t) &ymax, sizeof ymax);
1631 for (i = 0; i < n; i++) {
1633 b = (struct bubble *) alloc(sizeof *b);
1634 mread(fd, (genericptr_t) b, sizeof *b);
1640 b->prev = (struct bubble *) 0;
1642 mv_bubble(b, 0, 0, TRUE);
1645 b->next = (struct bubble *) 0;
1649 waterbody_name(x, y)
1656 return "drink"; /* should never happen */
1659 if (ltyp == DRAWBRIDGE_UP)
1660 ltyp = db_under_typ(lev->drawbridgemask);
1662 if (ltyp == LAVAPOOL)
1664 return hliquid("lava");
1666 return hliquid("
\97n
\8aâ");
1667 else if (ltyp == ICE)
1672 else if (ltyp == POOL)
1674 return "pool of water";
1676 return "
\90\85\82½
\82Ü
\82è";
1677 else if (ltyp == WATER || Is_waterlevel(&u.uz))
1678 ; /* fall through to default return value */
1679 else if (Is_juiblex_level(&u.uz))
1684 else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1691 return hliquid("water");
1693 return hliquid("
\90\85\92\86");
1699 /* there better be only one magic portal on water level... */
1700 for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1701 if (wportal->ttyp == MAGIC_PORTAL)
1703 impossible("set_wportal(): no portal!");
1709 int x, y, xskip, yskip, typ, glyph;
1711 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1712 panic("setup_waterlevel(): [%d:%d] neither 'Water' nor 'Air'",
1713 (int) u.uz.dnum, (int) u.uz.dlevel);
1715 /* ouch, hardcoded... (file scope statics and used in bxmin,bymax,&c) */
1718 /* use separate statements so that compiler won't complain about min()
1719 comparing two constants; the alternative is to do this in the
1720 preprocessor: #if (20 > ROWNO-1) ymax=ROWNO-1 #else ymax=20 #endif */
1722 xmax = min(xmax, (COLNO - 1) - 1);
1724 ymax = min(ymax, (ROWNO - 1));
1726 /* entire level is remembered as one glyph and any unspecified portion
1727 should default to level's base element rather than to usual stone */
1728 glyph = cmap_to_glyph(Is_waterlevel(&u.uz) ? S_water : S_air);
1729 typ = Is_waterlevel(&u.uz) ? WATER : AIR;
1731 /* set unspecified terrain (stone) and hero's memory to water or air */
1732 for (x = 1; x <= COLNO - 1; x++)
1733 for (y = 0; y <= ROWNO - 1; y++) {
1734 levl[x][y].glyph = glyph;
1735 if (levl[x][y].typ == STONE)
1736 levl[x][y].typ = typ;
1740 if (Is_waterlevel(&u.uz)) {
1741 xskip = 10 + rn2(10);
1748 for (x = bxmin; x <= bxmax; x += xskip)
1749 for (y = bymin; y <= bymax; y += yskip)
1750 mk_bubble(x, y, rn2(7));
1754 unsetup_waterlevel()
1756 struct bubble *b, *bb;
1759 for (b = bbubbles; b; b = bb) {
1761 free((genericptr_t) b);
1763 bbubbles = ebubbles = (struct bubble *) 0;
1771 * These bit masks make visually pleasing bubbles on a normal aspect
1772 * 25x80 terminal, which naturally results in them being mathematically
1773 * anything but symmetric. For this reason they cannot be computed
1774 * in situ, either. The first two elements tell the dimensions of
1775 * the bubble's bounding box.
1778 bm2[] = { 2, 1, 0x3 },
1779 bm3[] = { 3, 2, 0x7, 0x7 },
1780 bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1781 bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1782 bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1783 bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1784 bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1785 *const bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1788 if (x >= bxmax || y >= bymax)
1790 if (n >= SIZE(bmask)) {
1791 impossible("n too large (mk_bubble)");
1792 n = SIZE(bmask) - 1;
1794 if (bmask[n][1] > MAX_BMASK) {
1795 panic("bmask size is larger than MAX_BMASK");
1797 b = (struct bubble *) alloc(sizeof *b);
1798 if ((x + (int) bmask[n][0] - 1) > bxmax)
1799 x = bxmax - bmask[n][0] + 1;
1800 if ((y + (int) bmask[n][1] - 1) > bymax)
1801 y = bymax - bmask[n][1] + 1;
1806 /* y dimension is the length of bitmap data - see bmask above */
1807 (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1808 (bmask[n][1] + 2) * sizeof (b->bm[0]));
1816 b->prev = (struct bubble *) 0;
1817 b->next = (struct bubble *) 0;
1819 mv_bubble(b, 0, 0, TRUE);
1823 * The player, the portal and all other objects and monsters
1824 * float along with their associated bubbles. Bubbles may overlap
1825 * freely, and the contents may get associated with other bubbles in
1826 * the process. Bubbles are "sticky", meaning that if the player is
1827 * in the immediate neighborhood of one, he/she may get sucked inside.
1828 * This property also makes leaving a bubble slightly difficult.
1831 mv_bubble(b, dx, dy, ini)
1836 int x, y, i, j, colli = 0;
1837 struct container *cons, *ctemp;
1839 /* clouds move slowly */
1840 if (!Is_airlevel(&u.uz) || !rn2(6)) {
1842 if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1843 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1849 * collision with level borders?
1850 * 1 = horizontal border, 2 = vertical, 3 = corner
1856 if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1858 if ((int) (b->y + b->bm[1] - 1) >= bymax)
1862 pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1866 pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1869 if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1870 pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1872 b->x = bxmax - b->bm[0] + 1;
1874 if ((int) (b->y + b->bm[1] - 1) > bymax) {
1875 pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1877 b->y = bymax - b->bm[1] + 1;
1880 /* bounce if we're trying to move off the border */
1881 if (b->x == bxmin && dx < 0)
1883 if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1885 if (b->y == bymin && dy < 0)
1887 if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1894 /* draw the bubbles */
1895 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1896 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1897 if (b->bm[j + 2] & (1 << i)) {
1898 if (Is_waterlevel(&u.uz)) {
1899 levl[x][y].typ = AIR;
1901 unblock_point(x, y);
1902 } else if (Is_airlevel(&u.uz)) {
1903 levl[x][y].typ = CLOUD;
1909 if (Is_waterlevel(&u.uz)) {
1910 /* replace contents of bubble */
1911 for (cons = b->cons; cons; cons = ctemp) {
1916 switch (cons->what) {
1918 struct obj *olist, *otmp;
1920 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1921 otmp = olist->nexthere;
1922 place_object(olist, cons->x, cons->y);
1929 struct monst *mon = (struct monst *) cons->list;
1931 /* mnearto() might fail. We can jump right to elemental_clog
1932 from here rather than deal_with_overcrowding() */
1933 if (!mnearto(mon, cons->x, cons->y, TRUE))
1934 elemental_clog(mon);
1939 struct monst *mtmp = m_at(cons->x, cons->y);
1940 int ux0 = u.ux, uy0 = u.uy;
1942 u_on_newpos(cons->x, cons->y);
1943 newsym(ux0, uy0); /* clean up old position */
1952 struct trap *btrap = (struct trap *) cons->list;
1954 btrap->tx = cons->x;
1955 btrap->ty = cons->y;
1960 impossible("mv_bubble: unknown bubble contents");
1963 free((genericptr_t) cons);
1980 /* sometimes alter direction for fun anyway
1981 (higher probability for stationary bubbles) */
1982 if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {