1 /* NetHack 3.6 mkmaze.c $NHDT-Date: 1449269918 2015/12/04 22:58:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.42 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
8 /* JNetHack may be freely redistributed. See license for details. */
12 #include "lev.h" /* save & restore info */
14 /* from sp_lev.c, for fixup_special() */
15 extern lev_region *lregions;
16 extern int num_lregions;
18 STATIC_DCL boolean FDECL(iswall, (int, int));
19 STATIC_DCL boolean FDECL(iswall_or_stone, (int, int));
20 STATIC_DCL boolean FDECL(is_solid, (int, int));
21 STATIC_DCL int FDECL(extend_spine, (int[3][3], int, int, int));
22 STATIC_DCL boolean FDECL(okay, (int, int, int));
23 STATIC_DCL void FDECL(maze0xy, (coord *));
24 STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
25 XCHAR_P, XCHAR_P, XCHAR_P,
26 XCHAR_P, BOOLEAN_P, d_level *));
27 STATIC_DCL void NDECL(fixup_special);
28 STATIC_DCL void NDECL(setup_waterlevel);
29 STATIC_DCL void NDECL(unsetup_waterlevel);
31 /* adjust a coordinate one step in the specified direction */
32 #define mz_move(X, Y, dir) \
35 case 0: --(Y); break; \
36 case 1: (X)++; break; \
37 case 2: (Y)++; break; \
38 case 3: --(X); break; \
39 default: panic("mz_move: bad direction %d", dir); \
51 type = levl[x][y].typ;
52 return (boolean) (IS_WALL(type) || IS_DOOR(type)
53 || type == SDOOR || type == IRONBARS);
62 /* out of bounds = stone */
66 type = levl[x][y].typ;
67 return (boolean) (type == STONE
68 || IS_WALL(type) || IS_DOOR(type)
69 || type == SDOOR || type == IRONBARS);
72 /* return TRUE if out of bounds, wall or rock */
77 return (boolean) (!isok(x, y) || IS_STWALL(levl[x][y].typ));
81 * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
82 * a wall spine in the (dx,dy) direction. Return 0 otherwise.
84 * To extend a wall spine in that direction, first there must be a wall there.
85 * Then, extend a spine unless the current position is surrounded by walls
86 * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W'
87 * a wall, '.' a room, 'a' anything (we don't care), and our direction is
88 * (0,1) - South or down - then:
91 * W x W This would not extend a spine from x down
92 * W W W (a corridor of walls is formed).
95 * W x W This would extend a spine from x down.
99 extend_spine(locale, wall_there, dx, dy)
101 int wall_there, dx, dy;
108 if (wall_there) { /* wall in that direction */
110 if (locale[1][0] && locale[1][2] /* EW are wall/stone */
111 && locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
117 if (locale[0][1] && locale[2][1] /* NS are wall/stone */
118 && locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
132 * Wall cleanup. This function has two purposes: (1) remove walls that
133 * are totally surrounded by stone - they are redundant. (2) correct
134 * the types so that they extend and connect to each other.
137 wallification(x1, y1, x2, y2)
144 int locale[3][3]; /* rock or wall status surrounding positions */
146 * Value 0 represents a free-standing wall. It could be anything,
147 * so even though this table says VWALL, we actually leave whatever
148 * typ was there alone.
150 static xchar spine_array[16] = { VWALL, HWALL, HWALL, HWALL,
151 VWALL, TRCORNER, TLCORNER, TDWALL,
152 VWALL, BRCORNER, BLCORNER, TUWALL,
153 VWALL, TLWALL, TRWALL, CROSSWALL };
155 /* sanity check on incoming variables */
156 if (x1 < 0 || x2 >= COLNO || x1 > x2 || y1 < 0 || y2 >= ROWNO || y1 > y2)
157 panic("wallification: bad bounds (%d,%d) to (%d,%d)", x1, y1, x2, y2);
159 /* Step 1: change walls surrounded by rock to rock. */
160 for (x = x1; x <= x2; x++)
161 for (y = y1; y <= y2; y++) {
164 if (IS_WALL(type) && type != DBWALL) {
165 if (is_solid(x - 1, y - 1) && is_solid(x - 1, y)
166 && is_solid(x - 1, y + 1) && is_solid(x, y - 1)
167 && is_solid(x, y + 1) && is_solid(x + 1, y - 1)
168 && is_solid(x + 1, y) && is_solid(x + 1, y + 1))
174 * Step 2: set the correct wall type. We can't combine steps
175 * 1 and 2 into a single sweep because we depend on knowing if
176 * the surrounding positions are stone.
178 for (x = x1; x <= x2; x++)
179 for (y = y1; y <= y2; y++) {
182 if (!(IS_WALL(type) && type != DBWALL))
185 /* set the locations TRUE if rock or wall or out of bounds */
186 locale[0][0] = iswall_or_stone(x - 1, y - 1);
187 locale[1][0] = iswall_or_stone(x, y - 1);
188 locale[2][0] = iswall_or_stone(x + 1, y - 1);
190 locale[0][1] = iswall_or_stone(x - 1, y);
191 locale[2][1] = iswall_or_stone(x + 1, y);
193 locale[0][2] = iswall_or_stone(x - 1, y + 1);
194 locale[1][2] = iswall_or_stone(x, y + 1);
195 locale[2][2] = iswall_or_stone(x + 1, y + 1);
197 /* determine if wall should extend to each direction NSEW */
198 bits = (extend_spine(locale, iswall(x, y - 1), 0, -1) << 3)
199 | (extend_spine(locale, iswall(x, y + 1), 0, 1) << 2)
200 | (extend_spine(locale, iswall(x + 1, y), 1, 0) << 1)
201 | extend_spine(locale, iswall(x - 1, y), -1, 0);
203 /* don't change typ if wall is free-standing */
205 lev->typ = spine_array[bits];
216 if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max
217 || levl[x][y].typ != 0)
222 /* find random starting point for maze generation */
227 cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
228 cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
235 * pos is inside restricted region (lx,ly,hx,hy) OR
236 * NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
239 bad_location(x, y, lx, ly, hx, hy)
241 xchar lx, ly, hx, hy;
243 return (boolean) (occupied(x, y)
244 || within_bounded_area(x, y, lx, ly, hx, hy)
245 || !((levl[x][y].typ == CORR && level.flags.is_maze_lev)
246 || levl[x][y].typ == ROOM
247 || levl[x][y].typ == AIR));
250 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy)
251 and place something (based on rtype) in that region */
253 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
254 xchar lx, ly, hx, hy;
255 xchar nlx, nly, nhx, nhy;
263 if (!lx) { /* default to whole level */
265 * if there are rooms and this a branch, let place_branch choose
266 * the branch location (to avoid putting branches in corridors).
268 if (rtype == LR_BRANCH && nroom) {
269 place_branch(Is_branchlev(&u.uz), 0, 0);
279 /* first a probabilistic approach */
281 oneshot = (lx == hx && ly == hy);
282 for (trycnt = 0; trycnt < 200; trycnt++) {
283 x = rn1((hx - lx) + 1, lx);
284 y = rn1((hy - ly) + 1, ly);
285 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev))
289 /* then a deterministic one */
292 for (x = lx; x <= hx; x++)
293 for (y = ly; y <= hy; y++)
294 if (put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot,
298 impossible("Couldn't place lregion type %d!", rtype);
302 put_lregion_here(x, y, nlx, nly, nhx, nhy, rtype, oneshot, lev)
304 xchar nlx, nly, nhx, nhy;
309 if (bad_location(x, y, nlx, nly, nhx, nhy)) {
311 return FALSE; /* caller should try again */
313 /* Must make do with the only location possible;
314 avoid failure due to a misplaced trap.
315 It might still fail if there's a dungeon feature here. */
316 struct trap *t = t_at(x, y);
318 if (t && t->ttyp != MAGIC_PORTAL && t->ttyp != VIBRATING_SQUARE)
320 if (bad_location(x, y, nlx, nly, nhx, nhy))
328 /* "something" means the player in this case */
330 /* move the monster if no choice, or just try again */
332 (void) rloc(m_at(x, y), FALSE);
339 mkportal(x, y, lev->dnum, lev->dlevel);
343 mkstairs(x, y, (char) rtype, (struct mkroom *) 0);
346 place_branch(Is_branchlev(&u.uz), x, y);
352 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
354 /* this is special stuff that the level compiler cannot (yet) handle */
358 register lev_region *r = lregions;
361 struct mkroom *croom;
362 boolean added_branch = FALSE;
364 if (was_waterlevel) {
365 was_waterlevel = FALSE;
367 unsetup_waterlevel();
369 if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
370 level.flags.hero_memory = 0;
371 was_waterlevel = TRUE;
372 /* water level is an odd beast - it has to be set up
373 before calling place_lregions etc. */
376 for (x = 0; x < num_lregions; x++, r++) {
383 if (*r->rname.str >= '0' && *r->rname.str <= '9') {
384 /* "chutes and ladders" */
386 lev.dlevel = atoi(r->rname.str);
388 s_level *sp = find_level(r->rname.str);
396 place_lregion(r->inarea.x1, r->inarea.y1, r->inarea.x2,
397 r->inarea.y2, r->delarea.x1, r->delarea.y1,
398 r->delarea.x2, r->delarea.y2, r->rtype, &lev);
404 /* save the region outlines for goto_level() */
405 if (r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
406 updest.lx = r->inarea.x1;
407 updest.ly = r->inarea.y1;
408 updest.hx = r->inarea.x2;
409 updest.hy = r->inarea.y2;
410 updest.nlx = r->delarea.x1;
411 updest.nly = r->delarea.y1;
412 updest.nhx = r->delarea.x2;
413 updest.nhy = r->delarea.y2;
415 if (r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
416 dndest.lx = r->inarea.x1;
417 dndest.ly = r->inarea.y1;
418 dndest.hx = r->inarea.x2;
419 dndest.hy = r->inarea.y2;
420 dndest.nlx = r->delarea.x1;
421 dndest.nly = r->delarea.y1;
422 dndest.nhx = r->delarea.x2;
423 dndest.nhy = r->delarea.y2;
425 /* place_lregion gets called from goto_level() */
430 free((genericptr_t) r->rname.str), r->rname.str = 0;
433 /* place dungeon branch if not placed above */
434 if (!added_branch && Is_branchlev(&u.uz)) {
435 place_lregion(0, 0, 0, 0, 0, 0, 0, 0, LR_BRANCH, (d_level *) 0);
438 /* Still need to add some stuff to level file */
439 if (Is_medusa_level(&u.uz)) {
443 croom = &rooms[0]; /* only one room on the medusa level */
444 for (tryct = rnd(4); tryct; tryct--) {
447 if (goodpos(x, y, (struct monst *) 0, 0)) {
448 otmp = mk_tt_object(STATUE, x, y);
449 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm])
450 || pm_resistance(&mons[otmp->corpsenm],
452 /* set_corpsenm() handles weight too */
453 set_corpsenm(otmp, rndmonnum());
459 otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
460 else /* Medusa statues don't contain books */
462 mkcorpstat(STATUE, (struct monst *) 0, (struct permonst *) 0,
463 somex(croom), somey(croom), CORPSTAT_NONE);
465 while (pm_resistance(&mons[otmp->corpsenm], MR_STONE)
466 || poly_when_stoned(&mons[otmp->corpsenm])) {
467 /* set_corpsenm() handles weight too */
468 set_corpsenm(otmp, rndmonnum());
471 } else if (Is_wiz1_level(&u.uz)) {
472 croom = search_special(MORGUE);
474 create_secret_door(croom, W_SOUTH | W_EAST | W_WEST);
475 } else if (Is_knox(&u.uz)) {
476 /* using an unfilled morgue for rm id */
477 croom = search_special(MORGUE);
478 /* avoid inappropriate morgue-related messages */
479 level.flags.graveyard = level.flags.has_morgue = 0;
480 croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
481 /* stock the main vault */
482 for (x = croom->lx; x <= croom->hx; x++)
483 for (y = croom->ly; y <= croom->hy; y++) {
484 (void) mkgold((long) rn1(300, 600), x, y);
485 if (!rn2(3) && !is_pool(x, y))
486 (void) maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
488 } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
489 /* less chance for undead corpses (lured from lower morgues) */
490 level.flags.graveyard = 1;
491 } else if (Is_stronghold(&u.uz)) {
492 level.flags.graveyard = 1;
493 } else if (Is_sanctum(&u.uz)) {
494 croom = search_special(TEMPLE);
496 create_secret_door(croom, W_ANY);
497 } else if (on_level(&u.uz, &orcus_level)) {
498 register struct monst *mtmp, *mtmp2;
500 /* it's a ghost town, get rid of shopkeepers */
501 for (mtmp = fmon; mtmp; mtmp = mtmp2) {
509 free((genericptr_t) lregions), lregions = 0;
515 register const char *s;
519 s_level *sp = Is_special(&u.uz);
523 if (sp && sp->rndlevs)
524 Sprintf(protofile, "%s-%d", s, rnd((int) sp->rndlevs));
526 Strcpy(protofile, s);
527 } else if (*(dungeons[u.uz.dnum].proto)) {
528 if (dunlevs_in_dungeon(&u.uz) > 1) {
529 if (sp && sp->rndlevs)
530 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
531 dunlev(&u.uz), rnd((int) sp->rndlevs));
533 Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
535 } else if (sp && sp->rndlevs) {
536 Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
537 rnd((int) sp->rndlevs));
539 Strcpy(protofile, dungeons[u.uz.dnum].proto);
542 Strcpy(protofile, "");
544 /* SPLEVTYPE format is "level-choice,level-choice"... */
545 if (wizard && *protofile && sp && sp->rndlevs) {
546 char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
548 /* rindex always succeeds due to code in prior block */
549 int len = (int) ((rindex(protofile, '-') - protofile) + 1);
552 if (!strncmp(ep, protofile, len)) {
553 int pick = atoi(ep + len);
554 /* use choice only if valid */
555 if (pick > 0 && pick <= (int) sp->rndlevs)
556 Sprintf(protofile + len, "%d", pick);
568 Strcat(protofile, LEV_EXT);
569 if (load_special(protofile)) {
571 /* some levels can end up with monsters
572 on dead mon list, including light source monsters */
574 return; /* no mazification right now */
576 impossible("Couldn't load \"%s\" - making a maze.", protofile);
579 level.flags.is_maze_lev = TRUE;
580 level.flags.corrmaze = !rn2(3);
582 if (level.flags.corrmaze)
583 for (x = 2; x < x_maze_max; x++)
584 for (y = 2; y < y_maze_max; y++)
585 levl[x][y].typ = STONE;
587 for (x = 2; x <= x_maze_max; x++)
588 for (y = 2; y <= y_maze_max; y++)
589 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
592 walkfrom((int) mm.x, (int) mm.y, 0);
593 /* put a boulder at the maze center */
594 (void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE, FALSE);
596 if (!level.flags.corrmaze)
597 wallification(2, 2, x_maze_max, y_maze_max);
600 mkstairs(mm.x, mm.y, 1, (struct mkroom *) 0); /* up */
601 if (!Invocation_lev(&u.uz)) {
603 mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
604 } else { /* choose "vibrating square" location */
608 * Pick a position where the stairs down to Moloch's Sanctum
609 * level will ultimately be created. At that time, an area
610 * will be altered: walls removed, moat and traps generated,
611 * boulders destroyed. The position picked here must ensure
612 * that that invocation area won't extend off the map.
614 * We actually allow up to 2 squares around the usual edge of
615 * the area to get truncated; see mkinvokearea(mklev.c).
617 #define INVPOS_X_MARGIN (6 - 2)
618 #define INVPOS_Y_MARGIN (5 - 2)
619 #define INVPOS_DISTANCE 11
620 int x_range = x_maze_max - x_maze_min - 2 * INVPOS_X_MARGIN - 1,
621 y_range = y_maze_max - y_maze_min - 2 * INVPOS_Y_MARGIN - 1;
623 if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN
624 || (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) {
625 debugpline2("inv_pos: maze is too small! (%d x %d)",
626 x_maze_max, y_maze_max);
628 inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
630 x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
631 y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
632 /* we don't want it to be too near the stairs, nor
633 to be on a spot that's already in use (wall|trap) */
634 } while (x == xupstair || y == yupstair /*(direct line)*/
635 || abs(x - xupstair) == abs(y - yupstair)
636 || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
637 || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
640 maketrap(inv_pos.x, inv_pos.y, VIBRATING_SQUARE);
641 #undef INVPOS_X_MARGIN
642 #undef INVPOS_Y_MARGIN
643 #undef INVPOS_DISTANCE
648 /* place branch stair or portal */
649 place_branch(Is_branchlev(&u.uz), 0, 0);
651 for (x = rn1(8, 11); x; x--) {
653 (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
655 for (x = rn1(10, 2); x; x--) {
657 (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
659 for (x = rn2(3); x; x--) {
661 (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
663 for (x = rn1(5, 7); x; x--) {
665 (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
667 for (x = rn1(6, 7); x; x--) {
669 (void) mkgold(0L, mm.x, mm.y);
671 for (x = rn1(6, 7); x; x--)
672 mktrap(0, 1, (struct mkroom *) 0, (coord *) 0);
676 /* Make the mazewalk iterative by faking a stack. This is needed to
677 * ensure the mazewalk is successful in the limited stack space of
678 * the program. This iterative version uses the minimum amount of stack
679 * that is totally safe.
686 #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */
687 char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
692 if (level.flags.corrmaze)
699 mazex[pos] = (char) x;
700 mazey[pos] = (char) y;
702 x = (int) mazex[pos];
703 y = (int) mazey[pos];
704 if (!IS_DOOR(levl[x][y].typ)) {
705 /* might still be on edge of MAP, so don't overwrite */
706 levl[x][y].typ = typ;
707 levl[x][y].flags = 0;
710 for (a = 0; a < 4; a++)
718 levl[x][y].typ = typ;
722 panic("Overflow in walkfrom");
723 mazex[pos] = (char) x;
724 mazey[pos] = (char) y;
735 register int q, a, dir;
739 if (level.flags.corrmaze)
745 if (!IS_DOOR(levl[x][y].typ)) {
746 /* might still be on edge of MAP, so don't overwrite */
747 levl[x][y].typ = typ;
748 levl[x][y].flags = 0;
753 for (a = 0; a < 4; a++)
760 levl[x][y].typ = typ;
767 /* find random point in generated corridors,
768 so we don't create items in moats, bunkers, or walls */
776 cc->x = 3 + 2 * rn2((x_maze_max >> 1) - 1);
777 cc->y = 3 + 2 * rn2((y_maze_max >> 1) - 1);
780 && levl[cc->x][cc->y].typ
781 != (level.flags.corrmaze ? CORR : ROOM));
785 for (x = 0; x < (x_maze_max >> 1) - 1; x++)
786 for (y = 0; y < (y_maze_max >> 1) - 1; y++) {
789 if (levl[cc->x][cc->y].typ
790 == (level.flags.corrmaze ? CORR : ROOM))
793 panic("mazexy: can't find a place!");
798 /* put a non-diggable boundary around the initial portion of a level map.
799 * assumes that no level will initially put things beyond the isok() range.
801 * we can't bound unconditionally on the last line with something in it,
802 * because that something might be a niche which was already reachable,
803 * so the boundary would be breached
805 * we can't bound unconditionally on one beyond the last line, because
806 * that provides a window of abuse for wallified special levels
812 register unsigned typ;
813 register struct rm *lev;
814 boolean found, nonwall;
815 int xmin, xmax, ymin, ymax;
817 if (Is_earthlevel(&u.uz))
818 return; /* everything diggable here */
820 found = nonwall = FALSE;
821 for (xmin = 0; !found && xmin <= COLNO; xmin++) {
822 lev = &levl[xmin][0];
823 for (y = 0; y <= ROWNO - 1; y++, lev++) {
832 xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
836 found = nonwall = FALSE;
837 for (xmax = COLNO - 1; !found && xmax >= 0; xmax--) {
838 lev = &levl[xmax][0];
839 for (y = 0; y <= ROWNO - 1; y++, lev++) {
848 xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
852 found = nonwall = FALSE;
853 for (ymin = 0; !found && ymin <= ROWNO; ymin++) {
854 lev = &levl[xmin][ymin];
855 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
864 ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
866 found = nonwall = FALSE;
867 for (ymax = ROWNO - 1; !found && ymax >= 0; ymax--) {
868 lev = &levl[xmin][ymax];
869 for (x = xmin; x <= xmax; x++, lev += ROWNO) {
878 ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
880 for (x = 0; x < COLNO; x++)
881 for (y = 0; y < ROWNO; y++)
882 if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
885 lev->wall_info |= W_NONDIGGABLE;
887 levl[x][y].wall_info |= W_NONDIGGABLE;
893 mkportal(x, y, todnum, todlevel)
894 xchar x, y, todnum, todlevel;
896 /* a portal "trap" must be matched by a
897 portal in the destination dungeon/dlevel */
898 struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
901 impossible("portal on top of portal??");
904 debugpline4("mkportal: at <%d,%d>, to %s, level %d", x, y,
905 dungeons[todnum].dname, todlevel);
906 ttmp->dst.dnum = todnum;
907 ttmp->dst.dlevel = todlevel;
915 boolean snd = FALSE, loud = FALSE;
917 for (n = rn2(3) + 2; n; n--) {
918 xchar x = rn1(COLNO - 4, 3);
919 xchar y = rn1(ROWNO - 4, 3);
920 if (levl[x][y].typ == LAVAPOOL) {
921 NhRegion *r = create_gas_cloud(x, y, 4 + rn2(5), rn1(10, 5));
922 clear_heros_fault(r);
924 if (distu(x, y) < 15)
929 Norep("You hear a %swhoosh!", loud ? "loud " : "");
933 * Special waterlevel stuff in endgame (TH).
935 * Some of these functions would probably logically belong to some
936 * other source files, but they are all so nicely encapsulated here.
940 /* to ease the work of debuggers at this stage */
949 static struct bubble *bbubbles, *ebubbles;
951 static struct trap *wportal;
952 static int xmin, ymin, xmax, ymax; /* level boundaries */
953 /* bubble movement boundaries */
954 #define bxmin (xmin + 1)
955 #define bymin (ymin + 1)
956 #define bxmax (xmax - 1)
957 #define bymax (ymax - 1)
959 STATIC_DCL void NDECL(set_wportal);
960 STATIC_DCL void FDECL(mk_bubble, (int, int, int));
961 STATIC_DCL void FDECL(mv_bubble, (struct bubble *, int, int, BOOLEAN_P));
967 register struct bubble *b;
968 register int x, y, i, j;
970 static const struct rm water_pos = { cmap_to_glyph(S_water), WATER, 0, 0,
972 static const struct rm air_pos = { cmap_to_glyph(S_cloud), AIR, 0, 0, 0,
975 /* set up the portal the first time bubbles are moved */
981 if (Is_waterlevel(&u.uz)) {
982 /* keep attached ball&chain separate from bubble objects */
987 * Pick up everything inside of a bubble then fill all bubble
990 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
992 panic("movebubbles: cons != null");
993 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
994 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
995 if (b->bm[j + 2] & (1 << i)) {
997 impossible("movebubbles: bad pos (%d,%d)", x, y);
1001 /* pick up objects, monsters, hero, and traps */
1003 struct obj *olist = (struct obj *) 0, *otmp;
1004 struct container *cons =
1005 (struct container *) alloc(
1006 sizeof(struct container));
1008 while ((otmp = level.objects[x][y]) != 0) {
1009 remove_object(otmp);
1010 otmp->ox = otmp->oy = 0;
1011 otmp->nexthere = olist;
1017 cons->what = CONS_OBJ;
1018 cons->list = (genericptr_t) olist;
1019 cons->next = b->cons;
1023 struct monst *mon = m_at(x, y);
1024 struct container *cons =
1025 (struct container *) alloc(
1026 sizeof(struct container));
1030 cons->what = CONS_MON;
1031 cons->list = (genericptr_t) mon;
1033 cons->next = b->cons;
1039 remove_monster(x, y);
1041 newsym(x, y); /* clean up old position */
1042 mon->mx = mon->my = 0;
1044 if (!u.uswallow && x == u.ux && y == u.uy) {
1045 struct container *cons =
1046 (struct container *) alloc(
1047 sizeof(struct container));
1051 cons->what = CONS_HERO;
1052 cons->list = (genericptr_t) 0;
1054 cons->next = b->cons;
1057 if ((btrap = t_at(x, y)) != 0) {
1058 struct container *cons =
1059 (struct container *) alloc(
1060 sizeof(struct container));
1064 cons->what = CONS_TRAP;
1065 cons->list = (genericptr_t) btrap;
1067 cons->next = b->cons;
1071 levl[x][y] = water_pos;
1075 } else if (Is_airlevel(&u.uz)) {
1076 for (x = 0; x < COLNO; x++)
1077 for (y = 0; y < ROWNO; y++) {
1078 levl[x][y] = air_pos;
1079 unblock_point(x, y);
1084 * Every second time traverse down. This is because otherwise
1085 * all the junk that changes owners when bubbles overlap
1086 * would eventually end up in the last bubble in the chain.
1089 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1090 register int rx = rn2(3), ry = rn2(3);
1092 mv_bubble(b, b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1093 b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), FALSE);
1096 /* put attached ball&chain back */
1097 if (Is_waterlevel(&u.uz) && Punished)
1099 vision_full_recalc = 1;
1102 /* when moving in water, possibly (1 in 3) alter the intended destination */
1106 register int x, y, dx, dy;
1107 register boolean eff = FALSE;
1109 if (Swimming && rn2(4))
1110 return; /* natural swimmers have advantage */
1112 if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1113 /* cancel delta x and choose an arbitrary delta y value */
1116 dy = rn2(3) - 1; /* -1, 0, 1 */
1118 } while (dy && (!isok(x, y) || !is_pool(x, y)));
1122 } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1123 /* cancel delta y and choose an arbitrary delta x value */
1126 dx = rn2(3) - 1; /* -1 .. 1 */
1128 } while (dx && (!isok(x, y) || !is_pool(x, y)));
1135 pline("Water turbulence affects your movements.");
1137 pline("
\90\85\82Ì
\97¬
\82ê
\82ª
\82Â
\82È
\82½
\82Ì
\93®
\82«
\82É
\89e
\8b¿
\82ð
\97^
\82¦
\82½
\81D");
1141 save_waterlevel(fd, mode)
1144 register struct bubble *b;
1146 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1149 if (perform_bwrite(mode)) {
1151 for (b = bbubbles; b; b = b->next)
1153 bwrite(fd, (genericptr_t) &n, sizeof(int));
1154 bwrite(fd, (genericptr_t) &xmin, sizeof(int));
1155 bwrite(fd, (genericptr_t) &ymin, sizeof(int));
1156 bwrite(fd, (genericptr_t) &xmax, sizeof(int));
1157 bwrite(fd, (genericptr_t) &ymax, sizeof(int));
1158 for (b = bbubbles; b; b = b->next)
1159 bwrite(fd, (genericptr_t) b, sizeof(struct bubble));
1161 if (release_data(mode))
1162 unsetup_waterlevel();
1166 restore_waterlevel(fd)
1169 register struct bubble *b = (struct bubble *) 0, *btmp;
1173 if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
1177 mread(fd, (genericptr_t) &n, sizeof(int));
1178 mread(fd, (genericptr_t) &xmin, sizeof(int));
1179 mread(fd, (genericptr_t) &ymin, sizeof(int));
1180 mread(fd, (genericptr_t) &xmax, sizeof(int));
1181 mread(fd, (genericptr_t) &ymax, sizeof(int));
1182 for (i = 0; i < n; i++) {
1184 b = (struct bubble *) alloc(sizeof(struct bubble));
1185 mread(fd, (genericptr_t) b, sizeof(struct bubble));
1191 b->prev = (struct bubble *) 0;
1193 mv_bubble(b, 0, 0, TRUE);
1196 b->next = (struct bubble *) 0;
1197 was_waterlevel = TRUE;
1201 waterbody_name(x, y)
1204 register struct rm *lev;
1208 return "drink"; /* should never happen */
1211 if (ltyp == DRAWBRIDGE_UP)
1212 ltyp = db_under_typ(lev->drawbridgemask);
1214 if (ltyp == LAVAPOOL)
1219 else if (ltyp == ICE)
1224 else if (ltyp == POOL)
1226 return "pool of water";
1228 return "
\90\85\82½
\82Ü
\82è";
1229 else if (ltyp == WATER || Is_waterlevel(&u.uz))
1230 ; /* fall through to default return value */
1231 else if (Is_juiblex_level(&u.uz))
1236 else if (ltyp == MOAT && !Is_medusa_level(&u.uz))
1245 return "
\90\85\92\86";
1251 /* there better be only one magic portal on water level... */
1252 for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1253 if (wportal->ttyp == MAGIC_PORTAL)
1255 impossible("set_wportal(): no portal!");
1262 register int xskip, yskip;
1263 register int water_glyph = cmap_to_glyph(S_water);
1264 register int air_glyph = cmap_to_glyph(S_air);
1266 /* ouch, hardcoded... */
1273 /* set hero's memory to water */
1275 for (x = xmin; x <= xmax; x++)
1276 for (y = ymin; y <= ymax; y++)
1277 levl[x][y].glyph = Is_waterlevel(&u.uz) ? water_glyph : air_glyph;
1281 if (Is_waterlevel(&u.uz)) {
1282 xskip = 10 + rn2(10);
1289 for (x = bxmin; x <= bxmax; x += xskip)
1290 for (y = bymin; y <= bymax; y += yskip)
1291 mk_bubble(x, y, rn2(7));
1295 unsetup_waterlevel()
1297 register struct bubble *b, *bb;
1301 for (b = bbubbles; b; b = bb) {
1303 free((genericptr_t) b);
1305 bbubbles = ebubbles = (struct bubble *) 0;
1310 register int x, y, n;
1313 * These bit masks make visually pleasing bubbles on a normal aspect
1314 * 25x80 terminal, which naturally results in them being mathematically
1315 * anything but symmetric. For this reason they cannot be computed
1316 * in situ, either. The first two elements tell the dimensions of
1317 * the bubble's bounding box.
1319 static uchar bm2[] = { 2, 1, 0x3 }, bm3[] = { 3, 2, 0x7, 0x7 },
1320 bm4[] = { 4, 3, 0x6, 0xf, 0x6 },
1321 bm5[] = { 5, 3, 0xe, 0x1f, 0xe },
1322 bm6[] = { 6, 4, 0x1e, 0x3f, 0x3f, 0x1e },
1323 bm7[] = { 7, 4, 0x3e, 0x7f, 0x7f, 0x3e },
1324 bm8[] = { 8, 4, 0x7e, 0xff, 0xff, 0x7e },
1325 *bmask[] = { bm2, bm3, bm4, bm5, bm6, bm7, bm8 };
1326 register struct bubble *b;
1328 if (x >= bxmax || y >= bymax)
1330 if (n >= SIZE(bmask)) {
1331 impossible("n too large (mk_bubble)");
1332 n = SIZE(bmask) - 1;
1334 if (bmask[n][1] > MAX_BMASK) {
1335 panic("bmask size is larger than MAX_BMASK");
1337 b = (struct bubble *) alloc(sizeof(struct bubble));
1338 if ((x + (int) bmask[n][0] - 1) > bxmax)
1339 x = bxmax - bmask[n][0] + 1;
1340 if ((y + (int) bmask[n][1] - 1) > bymax)
1341 y = bymax - bmask[n][1] + 1;
1346 /* y dimension is the length of bitmap data - see bmask above */
1347 (void) memcpy((genericptr_t) b->bm, (genericptr_t) bmask[n],
1348 (bmask[n][1] + 2) * sizeof(b->bm[0]));
1356 b->prev = (struct bubble *) 0;
1357 b->next = (struct bubble *) 0;
1359 mv_bubble(b, 0, 0, TRUE);
1363 * The player, the portal and all other objects and monsters
1364 * float along with their associated bubbles. Bubbles may overlap
1365 * freely, and the contents may get associated with other bubbles in
1366 * the process. Bubbles are "sticky", meaning that if the player is
1367 * in the immediate neighborhood of one, he/she may get sucked inside.
1368 * This property also makes leaving a bubble slightly difficult.
1371 mv_bubble(b, dx, dy, ini)
1372 register struct bubble *b;
1373 register int dx, dy;
1374 register boolean ini;
1376 register int x, y, i, j, colli = 0;
1377 struct container *cons, *ctemp;
1379 /* clouds move slowly */
1380 if (!Is_airlevel(&u.uz) || !rn2(6)) {
1382 if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1383 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1389 * collision with level borders?
1390 * 1 = horizontal border, 2 = vertical, 3 = corner
1396 if ((int) (b->x + b->bm[0] - 1) >= bxmax)
1398 if ((int) (b->y + b->bm[1] - 1) >= bymax)
1402 pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1406 pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1409 if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1410 pline("bubble xmax: x = %d, xmax = %d", b->x + b->bm[0] - 1,
1412 b->x = bxmax - b->bm[0] + 1;
1414 if ((int) (b->y + b->bm[1] - 1) > bymax) {
1415 pline("bubble ymax: y = %d, ymax = %d", b->y + b->bm[1] - 1,
1417 b->y = bymax - b->bm[1] + 1;
1420 /* bounce if we're trying to move off the border */
1421 if (b->x == bxmin && dx < 0)
1423 if (b->x + b->bm[0] - 1 == bxmax && dx > 0)
1425 if (b->y == bymin && dy < 0)
1427 if (b->y + b->bm[1] - 1 == bymax && dy > 0)
1434 /* draw the bubbles */
1435 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1436 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1437 if (b->bm[j + 2] & (1 << i)) {
1438 if (Is_waterlevel(&u.uz)) {
1439 levl[x][y].typ = AIR;
1441 unblock_point(x, y);
1442 } else if (Is_airlevel(&u.uz)) {
1443 levl[x][y].typ = CLOUD;
1449 if (Is_waterlevel(&u.uz)) {
1450 /* replace contents of bubble */
1451 for (cons = b->cons; cons; cons = ctemp) {
1456 switch (cons->what) {
1458 struct obj *olist, *otmp;
1460 for (olist = (struct obj *) cons->list; olist; olist = otmp) {
1461 otmp = olist->nexthere;
1462 place_object(olist, cons->x, cons->y);
1468 struct monst *mon = (struct monst *) cons->list;
1469 (void) mnearto(mon, cons->x, cons->y, TRUE);
1474 int ux0 = u.ux, uy0 = u.uy;
1476 /* change u.ux0 and u.uy0? */
1479 newsym(ux0, uy0); /* clean up old position */
1481 if (MON_AT(cons->x, cons->y)) {
1482 mnexto(m_at(cons->x, cons->y));
1488 struct trap *btrap = (struct trap *) cons->list;
1489 btrap->tx = cons->x;
1490 btrap->ty = cons->y;
1495 impossible("mv_bubble: unknown bubble contents");
1498 free((genericptr_t) cons);
1509 b->dy = -b->dy; /* fall through */
1514 /* sometimes alter direction for fun anyway
1515 (higher probability for stationary bubbles) */
1516 if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {