1 /* SCCS Id: @(#)mkmaze.c 3.4 2002/04/04 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 #include "lev.h" /* save & restore info */
9 /* from sp_lev.c, for fixup_special() */
10 extern char *lev_message;
11 extern lev_region *lregions;
12 extern int num_lregions;
14 STATIC_DCL boolean FDECL(iswall,(int,int));
15 STATIC_DCL boolean FDECL(iswall_or_stone,(int,int));
16 STATIC_DCL boolean FDECL(is_solid,(int,int));
17 STATIC_DCL int FDECL(extend_spine, (int [3][3], int, int, int));
18 STATIC_DCL boolean FDECL(okay,(int,int,int));
19 STATIC_DCL void FDECL(maze0xy,(coord *));
20 STATIC_DCL boolean FDECL(put_lregion_here,(XCHAR_P,XCHAR_P,XCHAR_P,
21 XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,BOOLEAN_P,d_level *));
22 STATIC_DCL void NDECL(fixup_special);
23 STATIC_DCL void FDECL(move, (int *,int *,int));
24 STATIC_DCL void NDECL(setup_waterlevel);
25 STATIC_DCL void NDECL(unsetup_waterlevel);
34 if (!isok(x,y)) return FALSE;
35 type = levl[x][y].typ;
36 return (IS_WALL(type) || IS_DOOR(type) ||
37 type == SDOOR || type == IRONBARS);
46 /* out of bounds = stone */
47 if (!isok(x,y)) return TRUE;
49 type = levl[x][y].typ;
50 return (type == STONE || IS_WALL(type) || IS_DOOR(type) ||
51 type == SDOOR || type == IRONBARS);
54 /* return TRUE if out of bounds, wall or rock */
59 return (!isok(x,y) || IS_STWALL(levl[x][y].typ));
64 * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend
65 * a wall spine in the (dx,dy) direction. Return 0 otherwise.
67 * To extend a wall spine in that direction, first there must be a wall there.
68 * Then, extend a spine unless the current position is surrounded by walls
69 * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W'
70 * a wall, '.' a room, 'a' anything (we don't care), and our direction is
71 * (0,1) - South or down - then:
74 * W x W This would not extend a spine from x down
75 * W W W (a corridor of walls is formed).
78 * W x W This would extend a spine from x down.
82 extend_spine(locale, wall_there, dx, dy)
84 int wall_there, dx, dy;
91 if (wall_there) { /* wall in that direction */
93 if (locale[ 1][0] && locale[ 1][2] && /* EW are wall/stone */
94 locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */
100 if (locale[0][ 1] && locale[2][ 1] && /* NS are wall/stone */
101 locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */
116 * Wall cleanup. This function has two purposes: (1) remove walls that
117 * are totally surrounded by stone - they are redundant. (2) correct
118 * the types so that they extend and connect to each other.
121 wallification(x1, y1, x2, y2)
128 int locale[3][3]; /* rock or wall status surrounding positions */
130 * Value 0 represents a free-standing wall. It could be anything,
131 * so even though this table says VWALL, we actually leave whatever
132 * typ was there alone.
134 static xchar spine_array[16] = {
135 VWALL, HWALL, HWALL, HWALL,
136 VWALL, TRCORNER, TLCORNER, TDWALL,
137 VWALL, BRCORNER, BLCORNER, TUWALL,
138 VWALL, TLWALL, TRWALL, CROSSWALL
141 /* sanity check on incoming variables */
142 if (x1<0 || x2>=COLNO || x1>x2 || y1<0 || y2>=ROWNO || y1>y2)
143 panic("wallification: bad bounds (%d,%d) to (%d,%d)",x1,y1,x2,y2);
145 /* Step 1: change walls surrounded by rock to rock. */
146 for(x = x1; x <= x2; x++)
147 for(y = y1; y <= y2; y++) {
150 if (IS_WALL(type) && type != DBWALL) {
151 if (is_solid(x-1,y-1) &&
164 * Step 2: set the correct wall type. We can't combine steps
165 * 1 and 2 into a single sweep because we depend on knowing if
166 * the surrounding positions are stone.
168 for(x = x1; x <= x2; x++)
169 for(y = y1; y <= y2; y++) {
172 if ( !(IS_WALL(type) && type != DBWALL)) continue;
174 /* set the locations TRUE if rock or wall or out of bounds */
175 locale[0][0] = iswall_or_stone(x-1,y-1);
176 locale[1][0] = iswall_or_stone( x,y-1);
177 locale[2][0] = iswall_or_stone(x+1,y-1);
179 locale[0][1] = iswall_or_stone(x-1, y);
180 locale[2][1] = iswall_or_stone(x+1, y);
182 locale[0][2] = iswall_or_stone(x-1,y+1);
183 locale[1][2] = iswall_or_stone( x,y+1);
184 locale[2][2] = iswall_or_stone(x+1,y+1);
186 /* determine if wall should extend to each direction NSEW */
187 bits = (extend_spine(locale, iswall(x,y-1), 0, -1) << 3)
188 | (extend_spine(locale, iswall(x,y+1), 0, 1) << 2)
189 | (extend_spine(locale, iswall(x+1,y), 1, 0) << 1)
190 | extend_spine(locale, iswall(x-1,y), -1, 0);
192 /* don't change typ if wall is free-standing */
193 if (bits) lev->typ = spine_array[bits];
204 if(x<3 || y<3 || x>x_maze_max || y>y_maze_max || levl[x][y].typ != 0)
210 maze0xy(cc) /* find random starting point for maze generation */
213 cc->x = 3 + 2*rn2((x_maze_max>>1) - 1);
214 cc->y = 3 + 2*rn2((y_maze_max>>1) - 1);
221 * pos is inside restricted region (lx,ly,hx,hy) OR
222 * NOT (pos is corridor and a maze level OR pos is a room OR pos is air)
225 bad_location(x, y, lx, ly, hx, hy)
227 xchar lx, ly, hx, hy;
229 return((boolean)(occupied(x, y) ||
230 within_bounded_area(x,y, lx,ly, hx,hy) ||
231 !((levl[x][y].typ == CORR && level.flags.is_maze_lev) ||
232 levl[x][y].typ == ROOM || levl[x][y].typ == AIR)));
235 /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy) */
236 /* and place something (based on rtype) in that region */
238 place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev)
239 xchar lx, ly, hx, hy;
240 xchar nlx, nly, nhx, nhy;
248 if(!lx) { /* default to whole level */
250 * if there are rooms and this a branch, let place_branch choose
251 * the branch location (to avoid putting branches in corridors).
253 if(rtype == LR_BRANCH && nroom) {
254 place_branch(Is_branchlev(&u.uz), 0, 0);
258 lx = 1; hx = COLNO-1;
259 ly = 1; hy = ROWNO-1;
262 /* first a probabilistic approach */
264 oneshot = (lx == hx && ly == hy);
265 for (trycnt = 0; trycnt < 200; trycnt++) {
266 x = rn1((hx - lx) + 1, lx);
267 y = rn1((hy - ly) + 1, ly);
268 if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev))
272 /* then a deterministic one */
275 for (x = lx; x <= hx; x++)
276 for (y = ly; y <= hy; y++)
277 if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev))
280 impossible("Couldn't place lregion type %d!", rtype);
284 put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)
286 xchar nlx, nly, nhx, nhy;
291 if (bad_location(x, y, nlx, nly, nhx, nhy)) {
293 return FALSE; /* caller should try again */
295 /* Must make do with the only location possible;
296 avoid failure due to a misplaced trap.
297 It might still fail if there's a dungeon feature here. */
298 struct trap *t = t_at(x,y);
300 if (t && t->ttyp != MAGIC_PORTAL) deltrap(t);
301 if (bad_location(x, y, nlx, nly, nhx, nhy)) return FALSE;
308 /* "something" means the player in this case */
310 /* move the monster if no choice, or just try again */
311 if(oneshot) (void) rloc(m_at(x,y), FALSE);
317 mkportal(x, y, lev->dnum, lev->dlevel);
321 mkstairs(x, y, (char)rtype, (struct mkroom *)0);
324 place_branch(Is_branchlev(&u.uz), x, y);
330 static boolean was_waterlevel; /* ugh... this shouldn't be needed */
332 /* this is special stuff that the level compiler cannot (yet) handle */
336 register lev_region *r = lregions;
339 struct mkroom *croom;
340 boolean added_branch = FALSE;
342 if (was_waterlevel) {
343 was_waterlevel = FALSE;
345 unsetup_waterlevel();
346 } else if (Is_waterlevel(&u.uz)) {
347 level.flags.hero_memory = 0;
348 was_waterlevel = TRUE;
349 /* water level is an odd beast - it has to be set up
350 before calling place_lregions etc. */
353 for(x = 0; x < num_lregions; x++, r++) {
360 if(*r->rname.str >= '0' && *r->rname.str <= '9') {
361 /* "chutes and ladders" */
363 lev.dlevel = atoi(r->rname.str);
365 s_level *sp = find_level(r->rname.str);
373 place_lregion(r->inarea.x1, r->inarea.y1,
374 r->inarea.x2, r->inarea.y2,
375 r->delarea.x1, r->delarea.y1,
376 r->delarea.x2, r->delarea.y2,
383 /* save the region outlines for goto_level() */
384 if(r->rtype == LR_TELE || r->rtype == LR_UPTELE) {
385 updest.lx = r->inarea.x1; updest.ly = r->inarea.y1;
386 updest.hx = r->inarea.x2; updest.hy = r->inarea.y2;
387 updest.nlx = r->delarea.x1; updest.nly = r->delarea.y1;
388 updest.nhx = r->delarea.x2; updest.nhy = r->delarea.y2;
390 if(r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) {
391 dndest.lx = r->inarea.x1; dndest.ly = r->inarea.y1;
392 dndest.hx = r->inarea.x2; dndest.hy = r->inarea.y2;
393 dndest.nlx = r->delarea.x1; dndest.nly = r->delarea.y1;
394 dndest.nhx = r->delarea.x2; dndest.nhy = r->delarea.y2;
396 /* place_lregion gets called from goto_level() */
400 if (r->rname.str) free((genericptr_t) r->rname.str), r->rname.str = 0;
403 /* place dungeon branch if not placed above */
404 if (!added_branch && Is_branchlev(&u.uz)) {
405 place_lregion(0,0,0,0,0,0,0,0,LR_BRANCH,(d_level *)0);
408 /* KMH -- Sokoban levels */
409 if(In_sokoban(&u.uz))
412 /* Still need to add some stuff to level file */
413 if (Is_medusa_level(&u.uz)) {
417 croom = &rooms[0]; /* only one room on the medusa level */
418 for (tryct = rnd(4); tryct; tryct--) {
419 x = somex(croom); y = somey(croom);
420 if (goodpos(x, y, (struct monst *)0, 0)) {
421 otmp = mk_tt_object(STATUE, x, y);
422 while (otmp && (poly_when_stoned(&mons[otmp->corpsenm]) ||
423 pm_resistance(&mons[otmp->corpsenm],MR_STONE))) {
424 otmp->corpsenm = rndmonnum();
425 otmp->owt = weight(otmp);
431 otmp = mk_tt_object(STATUE, somex(croom), somey(croom));
432 else /* Medusa statues don't contain books */
433 otmp = mkcorpstat(STATUE, (struct monst *)0, (struct permonst *)0,
434 somex(croom), somey(croom), FALSE);
436 while (pm_resistance(&mons[otmp->corpsenm],MR_STONE)
437 || poly_when_stoned(&mons[otmp->corpsenm])) {
438 otmp->corpsenm = rndmonnum();
439 otmp->owt = weight(otmp);
442 } else if(Is_wiz1_level(&u.uz)) {
443 croom = search_special(MORGUE);
445 create_secret_door(croom, W_SOUTH|W_EAST|W_WEST);
446 } else if(Is_knox(&u.uz)) {
447 /* using an unfilled morgue for rm id */
448 croom = search_special(MORGUE);
449 /* avoid inappropriate morgue-related messages */
450 level.flags.graveyard = level.flags.has_morgue = 0;
451 croom->rtype = OROOM; /* perhaps it should be set to VAULT? */
452 /* stock the main vault */
453 for(x = croom->lx; x <= croom->hx; x++)
454 for(y = croom->ly; y <= croom->hy; y++) {
455 (void) mkgold((long) rn1(300, 600), x, y);
456 if (!rn2(3) && !is_pool(x,y))
457 (void)maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT);
459 } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) {
460 /* less chance for undead corpses (lured from lower morgues) */
461 level.flags.graveyard = 1;
462 } else if (Is_stronghold(&u.uz)) {
463 level.flags.graveyard = 1;
464 } else if(Is_sanctum(&u.uz)) {
465 croom = search_special(TEMPLE);
467 create_secret_door(croom, W_ANY);
468 } else if(on_level(&u.uz, &orcus_level)) {
469 register struct monst *mtmp, *mtmp2;
471 /* it's a ghost town, get rid of shopkeepers */
472 for(mtmp = fmon; mtmp; mtmp = mtmp2) {
474 if(mtmp->isshk) mongone(mtmp);
480 for(str = lev_message; (nl = index(str, '\n')) != 0; str = nl+1) {
486 free((genericptr_t)lev_message);
491 free((genericptr_t) lregions), lregions = 0;
497 register const char *s;
501 s_level *sp = Is_special(&u.uz);
505 if(sp && sp->rndlevs) Sprintf(protofile, "%s-%d", s,
506 rnd((int) sp->rndlevs));
507 else Strcpy(protofile, s);
508 } else if(*(dungeons[u.uz.dnum].proto)) {
509 if(dunlevs_in_dungeon(&u.uz) > 1) {
510 if(sp && sp->rndlevs)
511 Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto,
513 rnd((int) sp->rndlevs));
514 else Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto,
516 } else if(sp && sp->rndlevs) {
517 Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto,
518 rnd((int) sp->rndlevs));
519 } else Strcpy(protofile, dungeons[u.uz.dnum].proto);
521 } else Strcpy(protofile, "");
524 /* SPLEVTYPE format is "level-choice,level-choice"... */
525 if (wizard && *protofile && sp && sp->rndlevs) {
526 char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */
528 /* rindex always succeeds due to code in prior block */
529 int len = (rindex(protofile, '-') - protofile) + 1;
532 if (!strncmp(ep, protofile, len)) {
533 int pick = atoi(ep + len);
534 /* use choice only if valid */
535 if (pick > 0 && pick <= (int) sp->rndlevs)
536 Sprintf(protofile + len, "%d", pick);
548 Strcat(protofile, LEV_EXT);
549 if(load_special(protofile)) {
551 /* some levels can end up with monsters
552 on dead mon list, including light source monsters */
554 return; /* no mazification right now */
556 impossible("Couldn't load \"%s\" - making a maze.", protofile);
559 level.flags.is_maze_lev = TRUE;
561 #ifndef WALLIFIED_MAZE
562 for(x = 2; x < x_maze_max; x++)
563 for(y = 2; y < y_maze_max; y++)
564 levl[x][y].typ = STONE;
566 for(x = 2; x <= x_maze_max; x++)
567 for(y = 2; y <= y_maze_max; y++)
568 levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
572 walkfrom((int) mm.x, (int) mm.y);
573 /* put a boulder at the maze center */
574 (void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE, FALSE);
576 #ifdef WALLIFIED_MAZE
577 wallification(2, 2, x_maze_max, y_maze_max);
580 mkstairs(mm.x, mm.y, 1, (struct mkroom *)0); /* up */
581 if (!Invocation_lev(&u.uz)) {
583 mkstairs(mm.x, mm.y, 0, (struct mkroom *)0); /* down */
584 } else { /* choose "vibrating square" location */
588 * Pick a position where the stairs down to Moloch's Sanctum
589 * level will ultimately be created. At that time, an area
590 * will be altered: walls removed, moat and traps generated,
591 * boulders destroyed. The position picked here must ensure
592 * that that invocation area won't extend off the map.
594 * We actually allow up to 2 squares around the usual edge of
595 * the area to get truncated; see mkinvokearea(mklev.c).
597 #define INVPOS_X_MARGIN (6 - 2)
598 #define INVPOS_Y_MARGIN (5 - 2)
599 #define INVPOS_DISTANCE 11
600 int x_range = x_maze_max - x_maze_min - 2*INVPOS_X_MARGIN - 1,
601 y_range = y_maze_max - y_maze_min - 2*INVPOS_Y_MARGIN - 1;
604 if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN ||
605 (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE))
606 panic("inv_pos: maze is too small! (%d x %d)",
607 x_maze_max, y_maze_max);
609 inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/
611 x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1);
612 y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1);
613 /* we don't want it to be too near the stairs, nor
614 to be on a spot that's already in use (wall|trap) */
615 } while (x == xupstair || y == yupstair || /*(direct line)*/
616 abs(x - xupstair) == abs(y - yupstair) ||
617 distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE ||
618 !SPACE_POS(levl[x][y].typ) || occupied(x, y));
621 #undef INVPOS_X_MARGIN
622 #undef INVPOS_Y_MARGIN
623 #undef INVPOS_DISTANCE
628 /* place branch stair or portal */
629 place_branch(Is_branchlev(&u.uz), 0, 0);
631 for(x = rn1(8,11); x; x--) {
633 (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE);
635 for(x = rn1(10,2); x; x--) {
637 (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
639 for (x = rn2(3); x; x--) {
641 (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
643 for(x = rn1(5,7); x; x--) {
645 (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
647 for(x = rn1(6,7); x; x--) {
649 (void) mkgold(0L,mm.x,mm.y);
651 for(x = rn1(6,7); x; x--)
652 mktrap(0,1,(struct mkroom *) 0, (coord*) 0);
656 /* Make the mazewalk iterative by faking a stack. This is needed to
657 * ensure the mazewalk is successful in the limited stack space of
658 * the program. This iterative version uses the minimum amount of stack
659 * that is totally safe.
665 #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */
666 char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */
671 mazex[pos] = (char) x;
672 mazey[pos] = (char) y;
674 x = (int) mazex[pos];
675 y = (int) mazey[pos];
676 if(!IS_DOOR(levl[x][y].typ)) {
677 /* might still be on edge of MAP, so don't overwrite */
678 #ifndef WALLIFIED_MAZE
679 levl[x][y].typ = CORR;
681 levl[x][y].typ = ROOM;
683 levl[x][y].flags = 0;
686 for (a = 0; a < 4; a++)
687 if(okay(x, y, a)) dirs[q++]= a;
693 #ifndef WALLIFIED_MAZE
694 levl[x][y].typ = CORR;
696 levl[x][y].typ = ROOM;
701 panic("Overflow in walkfrom");
702 mazex[pos] = (char) x;
703 mazey[pos] = (char) y;
713 register int q,a,dir;
716 if(!IS_DOOR(levl[x][y].typ)) {
717 /* might still be on edge of MAP, so don't overwrite */
718 #ifndef WALLIFIED_MAZE
719 levl[x][y].typ = CORR;
721 levl[x][y].typ = ROOM;
723 levl[x][y].flags = 0;
728 for(a = 0; a < 4; a++)
729 if(okay(x,y,a)) dirs[q++]= a;
733 #ifndef WALLIFIED_MAZE
734 levl[x][y].typ = CORR;
736 levl[x][y].typ = ROOM;
750 case 0: --(*y); break;
751 case 1: (*x)++; break;
752 case 2: (*y)++; break;
753 case 3: --(*x); break;
754 default: panic("move: bad direction");
759 mazexy(cc) /* find random point in generated corridors,
760 so we don't create items in moats, bunkers, or walls */
766 cc->x = 3 + 2*rn2((x_maze_max>>1) - 1);
767 cc->y = 3 + 2*rn2((y_maze_max>>1) - 1);
769 } while (cpt < 100 && levl[cc->x][cc->y].typ !=
770 #ifdef WALLIFIED_MAZE
779 for (x = 0; x < (x_maze_max>>1) - 1; x++)
780 for (y = 0; y < (y_maze_max>>1) - 1; y++) {
783 if (levl[cc->x][cc->y].typ ==
784 #ifdef WALLIFIED_MAZE
791 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_MAZE special levels
810 register unsigned typ;
811 register struct rm *lev;
812 boolean found, nonwall;
813 int xmin,xmax,ymin,ymax;
815 if(Is_earthlevel(&u.uz)) return; /* everything diggable here */
817 found = nonwall = FALSE;
818 for(xmin=0; !found; xmin++) {
819 lev = &levl[xmin][0];
820 for(y=0; y<=ROWNO-1; y++, lev++) {
824 if(!IS_WALL(typ)) nonwall = TRUE;
828 xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
829 if (xmin < 0) xmin = 0;
831 found = nonwall = FALSE;
832 for(xmax=COLNO-1; !found; xmax--) {
833 lev = &levl[xmax][0];
834 for(y=0; y<=ROWNO-1; y++, lev++) {
838 if(!IS_WALL(typ)) nonwall = TRUE;
842 xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
843 if (xmax >= COLNO) xmax = COLNO-1;
845 found = nonwall = FALSE;
846 for(ymin=0; !found; ymin++) {
847 lev = &levl[xmin][ymin];
848 for(x=xmin; x<=xmax; x++, lev += ROWNO) {
852 if(!IS_WALL(typ)) nonwall = TRUE;
856 ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
858 found = nonwall = FALSE;
859 for(ymax=ROWNO-1; !found; ymax--) {
860 lev = &levl[xmin][ymax];
861 for(x=xmin; x<=xmax; x++, lev += ROWNO) {
865 if(!IS_WALL(typ)) nonwall = TRUE;
869 ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1;
871 for (x = 0; x < COLNO; x++)
872 for (y = 0; y < ROWNO; y++)
873 if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) {
876 lev->wall_info |= W_NONDIGGABLE;
878 levl[x][y].wall_info |= W_NONDIGGABLE;
884 mkportal(x, y, todnum, todlevel)
885 register xchar x, y, todnum, todlevel;
887 /* a portal "trap" must be matched by a */
888 /* portal in the destination dungeon/dlevel */
889 register struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL);
892 impossible("portal on top of portal??");
896 pline("mkportal: at (%d,%d), to %s, level %d",
897 x, y, dungeons[todnum].dname, todlevel);
899 ttmp->dst.dnum = todnum;
900 ttmp->dst.dlevel = todlevel;
905 * Special waterlevel stuff in endgame (TH).
907 * Some of these functions would probably logically belong to some
908 * other source files, but they are all so nicely encapsulated here.
911 /* to ease the work of debuggers at this stage */
919 static struct bubble *bbubbles, *ebubbles;
921 static struct trap *wportal;
922 static int xmin, ymin, xmax, ymax; /* level boundaries */
923 /* bubble movement boundaries */
924 #define bxmin (xmin + 1)
925 #define bymin (ymin + 1)
926 #define bxmax (xmax - 1)
927 #define bymax (ymax - 1)
929 STATIC_DCL void NDECL(set_wportal);
930 STATIC_DCL void FDECL(mk_bubble, (int,int,int));
931 STATIC_DCL void FDECL(mv_bubble, (struct bubble *,int,int,BOOLEAN_P));
937 register struct bubble *b;
938 register int x, y, i, j;
940 static const struct rm water_pos =
941 { cmap_to_glyph(S_water), WATER, 0, 0, 0, 0, 0, 0, 0 };
943 /* set up the portal the first time bubbles are moved */
944 if (!wportal) set_wportal();
947 /* keep attached ball&chain separate from bubble objects */
948 if (Punished) unplacebc();
951 * Pick up everything inside of a bubble then fill all bubble
955 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
956 if (b->cons) panic("movebubbles: cons != null");
957 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
958 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
959 if (b->bm[j + 2] & (1 << i)) {
961 impossible("movebubbles: bad pos (%d,%d)", x,y);
965 /* pick up objects, monsters, hero, and traps */
967 struct obj *olist = (struct obj *) 0, *otmp;
968 struct container *cons = (struct container *)
969 alloc(sizeof(struct container));
971 while ((otmp = level.objects[x][y]) != 0) {
973 otmp->ox = otmp->oy = 0;
974 otmp->nexthere = olist;
980 cons->what = CONS_OBJ;
981 cons->list = (genericptr_t) olist;
982 cons->next = b->cons;
986 struct monst *mon = m_at(x,y);
987 struct container *cons = (struct container *)
988 alloc(sizeof(struct container));
992 cons->what = CONS_MON;
993 cons->list = (genericptr_t) mon;
995 cons->next = b->cons;
1001 remove_monster(x, y);
1003 newsym(x,y); /* clean up old position */
1004 mon->mx = mon->my = 0;
1006 if (!u.uswallow && x == u.ux && y == u.uy) {
1007 struct container *cons = (struct container *)
1008 alloc(sizeof(struct container));
1012 cons->what = CONS_HERO;
1013 cons->list = (genericptr_t) 0;
1015 cons->next = b->cons;
1018 if ((btrap = t_at(x,y)) != 0) {
1019 struct container *cons = (struct container *)
1020 alloc(sizeof(struct container));
1024 cons->what = CONS_TRAP;
1025 cons->list = (genericptr_t) btrap;
1027 cons->next = b->cons;
1031 levl[x][y] = water_pos;
1037 * Every second time traverse down. This is because otherwise
1038 * all the junk that changes owners when bubbles overlap
1039 * would eventually end up in the last bubble in the chain.
1043 for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) {
1044 register int rx = rn2(3), ry = rn2(3);
1046 mv_bubble(b,b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)),
1047 b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)),
1051 /* put attached ball&chain back */
1052 if (Punished) placebc();
1053 vision_full_recalc = 1;
1056 /* when moving in water, possibly (1 in 3) alter the intended destination */
1060 register int x, y, dx, dy;
1061 register boolean eff = FALSE;
1063 if (Swimming && rn2(4))
1064 return; /* natural swimmers have advantage */
1066 if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */
1067 /* cancel delta x and choose an arbitrary delta y value */
1070 dy = rn2(3) - 1; /* -1, 0, 1 */
1072 } while (dy && (!isok(x,y) || !is_pool(x,y)));
1076 } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */
1077 /* cancel delta y and choose an arbitrary delta x value */
1080 dx = rn2(3) - 1; /* -1 .. 1 */
1082 } while (dx && (!isok(x,y) || !is_pool(x,y)));
1087 if (eff) pline("Water turbulence affects your movements.");
1091 save_waterlevel(fd, mode)
1094 register struct bubble *b;
1096 if (!Is_waterlevel(&u.uz)) return;
1098 if (perform_bwrite(mode)) {
1100 for (b = bbubbles; b; b = b->next) ++n;
1101 bwrite(fd, (genericptr_t)&n, sizeof (int));
1102 bwrite(fd, (genericptr_t)&xmin, sizeof (int));
1103 bwrite(fd, (genericptr_t)&ymin, sizeof (int));
1104 bwrite(fd, (genericptr_t)&xmax, sizeof (int));
1105 bwrite(fd, (genericptr_t)&ymax, sizeof (int));
1106 for (b = bbubbles; b; b = b->next)
1107 bwrite(fd, (genericptr_t)b, sizeof (struct bubble));
1109 if (release_data(mode))
1110 unsetup_waterlevel();
1114 restore_waterlevel(fd)
1117 register struct bubble *b = (struct bubble *)0, *btmp;
1121 if (!Is_waterlevel(&u.uz)) return;
1124 mread(fd,(genericptr_t)&n,sizeof(int));
1125 mread(fd,(genericptr_t)&xmin,sizeof(int));
1126 mread(fd,(genericptr_t)&ymin,sizeof(int));
1127 mread(fd,(genericptr_t)&xmax,sizeof(int));
1128 mread(fd,(genericptr_t)&ymax,sizeof(int));
1129 for (i = 0; i < n; i++) {
1131 b = (struct bubble *)alloc(sizeof(struct bubble));
1132 mread(fd,(genericptr_t)b,sizeof(struct bubble));
1138 b->prev = (struct bubble *)0;
1140 mv_bubble(b,0,0,TRUE);
1143 b->next = (struct bubble *)0;
1144 was_waterlevel = TRUE;
1147 const char *waterbody_name(x, y)
1150 register struct rm *lev;
1154 return "drink"; /* should never happen */
1160 else if (ltyp == ICE ||
1161 (ltyp == DRAWBRIDGE_UP &&
1162 (levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE))
1164 else if (((ltyp != POOL) && (ltyp != WATER) &&
1165 !Is_medusa_level(&u.uz) && !Is_waterlevel(&u.uz) && !Is_juiblex_level(&u.uz)) ||
1166 (ltyp == DRAWBRIDGE_UP && (levl[x][y].drawbridgemask & DB_UNDER) == DB_MOAT))
1168 else if ((ltyp != POOL) && (ltyp != WATER) && Is_juiblex_level(&u.uz))
1170 else if (ltyp == POOL)
1171 return "pool of water";
1172 else return "water";
1178 /* there better be only one magic portal on water level... */
1179 for (wportal = ftrap; wportal; wportal = wportal->ntrap)
1180 if (wportal->ttyp == MAGIC_PORTAL) return;
1181 impossible("set_wportal(): no portal!");
1188 register int xskip, yskip;
1189 register int water_glyph = cmap_to_glyph(S_water);
1191 /* ouch, hardcoded... */
1198 /* set hero's memory to water */
1200 for (x = xmin; x <= xmax; x++)
1201 for (y = ymin; y <= ymax; y++)
1202 levl[x][y].glyph = water_glyph;
1206 xskip = 10 + rn2(10);
1208 for (x = bxmin; x <= bxmax; x += xskip)
1209 for (y = bymin; y <= bymax; y += yskip)
1210 mk_bubble(x,y,rn2(7));
1214 unsetup_waterlevel()
1216 register struct bubble *b, *bb;
1220 for (b = bbubbles; b; b = bb) {
1222 free((genericptr_t)b);
1224 bbubbles = ebubbles = (struct bubble *)0;
1229 register int x, y, n;
1232 * These bit masks make visually pleasing bubbles on a normal aspect
1233 * 25x80 terminal, which naturally results in them being mathematically
1234 * anything but symmetric. For this reason they cannot be computed
1235 * in situ, either. The first two elements tell the dimensions of
1236 * the bubble's bounding box.
1240 bm3[] = {3,2,0x7,0x7},
1241 bm4[] = {4,3,0x6,0xf,0x6},
1242 bm5[] = {5,3,0xe,0x1f,0xe},
1243 bm6[] = {6,4,0x1e,0x3f,0x3f,0x1e},
1244 bm7[] = {7,4,0x3e,0x7f,0x7f,0x3e},
1245 bm8[] = {8,4,0x7e,0xff,0xff,0x7e},
1246 *bmask[] = {bm2,bm3,bm4,bm5,bm6,bm7,bm8};
1248 register struct bubble *b;
1250 if (x >= bxmax || y >= bymax) return;
1251 if (n >= SIZE(bmask)) {
1252 impossible("n too large (mk_bubble)");
1253 n = SIZE(bmask) - 1;
1255 b = (struct bubble *)alloc(sizeof(struct bubble));
1256 if ((x + (int) bmask[n][0] - 1) > bxmax) x = bxmax - bmask[n][0] + 1;
1257 if ((y + (int) bmask[n][1] - 1) > bymax) y = bymax - bmask[n][1] + 1;
1264 if (!bbubbles) bbubbles = b;
1270 b->prev = (struct bubble *)0;
1271 b->next = (struct bubble *)0;
1273 mv_bubble(b,0,0,TRUE);
1277 * The player, the portal and all other objects and monsters
1278 * float along with their associated bubbles. Bubbles may overlap
1279 * freely, and the contents may get associated with other bubbles in
1280 * the process. Bubbles are "sticky", meaning that if the player is
1281 * in the immediate neighborhood of one, he/she may get sucked inside.
1282 * This property also makes leaving a bubble slightly difficult.
1285 mv_bubble(b,dx,dy,ini)
1286 register struct bubble *b;
1287 register int dx, dy;
1288 register boolean ini;
1290 register int x, y, i, j, colli = 0;
1291 struct container *cons, *ctemp;
1294 if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
1295 /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */
1301 * collision with level borders?
1302 * 1 = horizontal border, 2 = vertical, 3 = corner
1304 if (b->x <= bxmin) colli |= 2;
1305 if (b->y <= bymin) colli |= 1;
1306 if ((int) (b->x + b->bm[0] - 1) >= bxmax) colli |= 2;
1307 if ((int) (b->y + b->bm[1] - 1) >= bymax) colli |= 1;
1310 pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin);
1314 pline("bubble ymin: y = %d, ymin = %d", b->y, bymin);
1317 if ((int) (b->x + b->bm[0] - 1) > bxmax) {
1318 pline("bubble xmax: x = %d, xmax = %d",
1319 b->x + b->bm[0] - 1, bxmax);
1320 b->x = bxmax - b->bm[0] + 1;
1322 if ((int) (b->y + b->bm[1] - 1) > bymax) {
1323 pline("bubble ymax: y = %d, ymax = %d",
1324 b->y + b->bm[1] - 1, bymax);
1325 b->y = bymax - b->bm[1] + 1;
1328 /* bounce if we're trying to move off the border */
1329 if (b->x == bxmin && dx < 0) dx = -dx;
1330 if (b->x + b->bm[0] - 1 == bxmax && dx > 0) dx = -dx;
1331 if (b->y == bymin && dy < 0) dy = -dy;
1332 if (b->y + b->bm[1] - 1 == bymax && dy > 0) dy = -dy;
1337 /* void positions inside bubble */
1339 for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++)
1340 for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++)
1341 if (b->bm[j + 2] & (1 << i)) {
1342 levl[x][y].typ = AIR;
1347 /* replace contents of bubble */
1348 for (cons = b->cons; cons; cons = ctemp) {
1353 switch(cons->what) {
1355 struct obj *olist, *otmp;
1357 for (olist=(struct obj *)cons->list; olist; olist=otmp) {
1358 otmp = olist->nexthere;
1359 place_object(olist, cons->x, cons->y);
1365 struct monst *mon = (struct monst *) cons->list;
1366 (void) mnearto(mon, cons->x, cons->y, TRUE);
1371 int ux0 = u.ux, uy0 = u.uy;
1373 /* change u.ux0 and u.uy0? */
1376 newsym(ux0, uy0); /* clean up old position */
1378 if (MON_AT(cons->x, cons->y)) {
1379 mnexto(m_at(cons->x,cons->y));
1385 struct trap *btrap = (struct trap *) cons->list;
1386 btrap->tx = cons->x;
1387 btrap->ty = cons->y;
1392 impossible("mv_bubble: unknown bubble contents");
1395 free((genericptr_t)cons);
1402 case 1: b->dy = -b->dy; break;
1403 case 3: b->dy = -b->dy; /* fall through */
1404 case 2: b->dx = -b->dx; break;
1406 /* sometimes alter direction for fun anyway
1407 (higher probability for stationary bubbles) */
1408 if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) {