/* NetHack 3.7 extralev.c $NHDT-Date: 1737345573 2025/01/19 19:59:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.28 $ */ /* Copyright 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ /* * Support code for "rogue"-style level. */ #include "hack.h" #define XL_UP 1 #define XL_DOWN 2 #define XL_LEFT 4 #define XL_RIGHT 8 staticfn void roguejoin(coordxy, coordxy, coordxy, coordxy, int); staticfn void roguecorr(coordxy, coordxy, int); staticfn void miniwalk(coordxy, coordxy); staticfn void roguejoin(coordxy x1, coordxy y1, coordxy x2, coordxy y2, int horiz) { coordxy x, y, middle; if (horiz) { middle = x1 + rn2(x2 - x1 + 1); for (x = min(x1, middle); x <= max(x1, middle); x++) corr(x, y1); for (y = min(y1, y2); y <= max(y1, y2); y++) corr(middle, y); for (x = min(middle, x2); x <= max(middle, x2); x++) corr(x, y2); } else { middle = y1 + rn2(y2 - y1 + 1); for (y = min(y1, middle); y <= max(y1, middle); y++) corr(x1, y); for (x = min(x1, x2); x <= max(x1, x2); x++) corr(x, middle); for (y = min(middle, y2); y <= max(middle, y2); y++) corr(x2, y); } } staticfn void roguecorr(coordxy x, coordxy y, int dir) { coordxy fromx, fromy, tox, toy; if (dir == XL_DOWN) { gr.r[x][y].doortable &= ~XL_DOWN; if (!gr.r[x][y].real) { fromx = gr.r[x][y].rlx; fromy = gr.r[x][y].rly; fromx += 1 + 26 * x; fromy += 7 * y; } else { fromx = gr.r[x][y].rlx + rn2(gr.r[x][y].dx); fromy = gr.r[x][y].rly + gr.r[x][y].dy; fromx += 1 + 26 * x; fromy += 7 * y; if (!IS_WALL(levl[fromx][fromy].typ)) impossible("down: no wall at %d,%d?", fromx, fromy); dodoor(fromx, fromy, &svr.rooms[gr.r[x][y].nroom]); levl[fromx][fromy].doormask = D_NODOOR; fromy++; } if (y >= 2) { impossible("down door from %d,%d going nowhere?", x, y); return; } y++; gr.r[x][y].doortable &= ~XL_UP; if (!gr.r[x][y].real) { tox = gr.r[x][y].rlx; toy = gr.r[x][y].rly; tox += 1 + 26 * x; toy += 7 * y; } else { tox = gr.r[x][y].rlx + rn2(gr.r[x][y].dx); toy = gr.r[x][y].rly - 1; tox += 1 + 26 * x; toy += 7 * y; if (!IS_WALL(levl[tox][toy].typ)) impossible("up: no wall at %d,%d?", tox, toy); dodoor(tox, toy, &svr.rooms[gr.r[x][y].nroom]); levl[tox][toy].doormask = D_NODOOR; toy--; } roguejoin(fromx, fromy, tox, toy, FALSE); return; } else if (dir == XL_RIGHT) { gr.r[x][y].doortable &= ~XL_RIGHT; if (!gr.r[x][y].real) { fromx = gr.r[x][y].rlx; fromy = gr.r[x][y].rly; fromx += 1 + 26 * x; fromy += 7 * y; } else { fromx = gr.r[x][y].rlx + gr.r[x][y].dx; fromy = gr.r[x][y].rly + rn2(gr.r[x][y].dy); fromx += 1 + 26 * x; fromy += 7 * y; if (!IS_WALL(levl[fromx][fromy].typ)) impossible("down: no wall at %d,%d?", fromx, fromy); dodoor(fromx, fromy, &svr.rooms[gr.r[x][y].nroom]); levl[fromx][fromy].doormask = D_NODOOR; fromx++; } if (x >= 2) { impossible("right door from %d,%d going nowhere?", x, y); return; } x++; gr.r[x][y].doortable &= ~XL_LEFT; if (!gr.r[x][y].real) { tox = gr.r[x][y].rlx; toy = gr.r[x][y].rly; tox += 1 + 26 * x; toy += 7 * y; } else { tox = gr.r[x][y].rlx - 1; toy = gr.r[x][y].rly + rn2(gr.r[x][y].dy); tox += 1 + 26 * x; toy += 7 * y; if (!IS_WALL(levl[tox][toy].typ)) impossible("left: no wall at %d,%d?", tox, toy); dodoor(tox, toy, &svr.rooms[gr.r[x][y].nroom]); levl[tox][toy].doormask = D_NODOOR; tox--; } roguejoin(fromx, fromy, tox, toy, TRUE); return; } else impossible("corridor in direction %d?", dir); } /* Modified walkfrom() from mkmaze.c */ staticfn void miniwalk(coordxy x, coordxy y) { int q, dir; int dirs[4]; while (1) { q = 0; #define doorhere (gr.r[x][y].doortable) if (x > 0 && (!(doorhere & XL_LEFT)) && (!gr.r[x - 1][y].doortable || !rn2(10))) dirs[q++] = 0; if (x < 2 && (!(doorhere & XL_RIGHT)) && (!gr.r[x + 1][y].doortable || !rn2(10))) dirs[q++] = 1; if (y > 0 && (!(doorhere & XL_UP)) && (!gr.r[x][y - 1].doortable || !rn2(10))) dirs[q++] = 2; if (y < 2 && (!(doorhere & XL_DOWN)) && (!gr.r[x][y + 1].doortable || !rn2(10))) dirs[q++] = 3; /* Rogue levels aren't just 3 by 3 mazes; they have some extra * connections, thus that 1/10 chance */ if (!q) return; dir = dirs[rn2(q)]; switch (dir) { /* Move in direction */ case 0: doorhere |= XL_LEFT; x--; doorhere |= XL_RIGHT; break; case 1: doorhere |= XL_RIGHT; x++; doorhere |= XL_LEFT; break; case 2: doorhere |= XL_UP; y--; doorhere |= XL_DOWN; break; case 3: doorhere |= XL_DOWN; y++; doorhere |= XL_UP; break; } miniwalk(x, y); } #undef doorhere } void makeroguerooms(void) { coordxy x, y; /* Rogue levels are structured 3 by 3, with each section containing * a room or an intersection. The minimum width is 2 each way. * One difference between these and "real" Rogue levels: real Rogue * uses 24 rows and NetHack only 23. So we cheat a bit by making the * second row of rooms not as deep. * * Each normal space has 6/7 rows and 25 columns in which a room may * actually be placed. Walls go from rows 0-5/6 and columns 0-24. * Not counting walls, the room may go in * rows 1-5 and columns 1-23 (numbering starting at 0). A room * coordinate of this type may be converted to a level coordinate * by adding 1+28*x to the column, and 7*y to the row. (The 1 * is because column 0 isn't used [we only use 1-78]). * Room height may be 2-4 (2-5 on last row), length 2-23 (not * counting walls). */ #define here gr.r[x][y] svn.nroom = 0; for (y = 0; y < 3; y++) for (x = 0; x < 3; x++) { /* Note: we want to insure at least 1 room. So, if the * first 8 are all dummies, force the last to be a room. */ if (!rn2(5) && (svn.nroom || (x < 2 && y < 2))) { /* Arbitrary: dummy rooms may only go where real * ones do. */ here.real = FALSE; here.rlx = rn1(22, 2); here.rly = rn1((y == 2) ? 4 : 3, 2); } else { here.real = TRUE; here.dx = rn1(22, 2); /* 2-23 long, plus walls */ here.dy = rn1((y == 2) ? 4 : 3, 2); /* 2-5 high, plus walls */ /* boundaries of room floor */ here.rlx = rnd(23 - here.dx + 1); here.rly = rnd(((y == 2) ? 5 : 4) - here.dy + 1); svn.nroom++; } here.doortable = 0; } miniwalk(rn2(3), rn2(3)); svn.nroom = 0; for (y = 0; y < 3; y++) for (x = 0; x < 3; x++) { if (here.real) { /* Make a room */ coordxy lowx, lowy, hix, hiy; gr.r[x][y].nroom = svn.nroom; gs.smeq[svn.nroom] = svn.nroom; lowx = 1 + 26 * x + here.rlx; lowy = 7 * y + here.rly; hix = 1 + 26 * x + here.rlx + here.dx - 1; hiy = 7 * y + here.rly + here.dy - 1; /* Strictly speaking, it should be lit only if above * level 10, but since Rogue rooms are only * encountered below level 10, use !rn2(7). */ add_room(lowx, lowy, hix, hiy, (boolean) !rn2(7), OROOM, FALSE); } } /* Now, add connecting corridors. */ for (y = 0; y < 3; y++) for (x = 0; x < 3; x++) { if (here.doortable & XL_DOWN) roguecorr(x, y, XL_DOWN); if (here.doortable & XL_RIGHT) roguecorr(x, y, XL_RIGHT); if (here.doortable & XL_LEFT) impossible("left end of %d, %d never connected?", x, y); if (here.doortable & XL_UP) impossible("up end of %d, %d never connected?", x, y); } #undef here } void corr(coordxy x, coordxy y) { if (rn2(50)) { levl[x][y].typ = CORR; } else { levl[x][y].typ = SCORR; } } void makerogueghost(void) { struct monst *ghost; struct obj *ghostobj; struct mkroom *croom; coordxy x, y; if (!svn.nroom) return; /* Should never happen */ croom = &svr.rooms[rn2(svn.nroom)]; x = somex(croom); y = somey(croom); if (!(ghost = makemon(&mons[PM_GHOST], x, y, NO_MM_FLAGS))) return; ghost->msleeping = 1; ghost = christen_monst(ghost, roguename()); nhUse(ghost); if (rn2(4)) { ghostobj = mksobj_at(FOOD_RATION, x, y, FALSE, FALSE); ghostobj->quan = (long) rnd(7); ghostobj->owt = weight(ghostobj); } if (rn2(2)) { ghostobj = mksobj_at(MACE, x, y, FALSE, FALSE); ghostobj->spe = rnd(3); if (rn2(4)) curse(ghostobj); } else { ghostobj = mksobj_at(TWO_HANDED_SWORD, x, y, FALSE, FALSE); ghostobj->spe = rnd(5) - 2; if (rn2(4)) curse(ghostobj); } ghostobj = mksobj_at(BOW, x, y, FALSE, FALSE); ghostobj->spe = 1; if (rn2(4)) curse(ghostobj); ghostobj = mksobj_at(ARROW, x, y, FALSE, FALSE); ghostobj->spe = 0; ghostobj->quan = (long) rn1(10, 25); ghostobj->owt = weight(ghostobj); if (rn2(4)) curse(ghostobj); if (rn2(2)) { ghostobj = mksobj_at(RING_MAIL, x, y, FALSE, FALSE); ghostobj->spe = rn2(3); if (!rn2(3)) ghostobj->oerodeproof = TRUE; if (rn2(4)) curse(ghostobj); } else { ghostobj = mksobj_at(PLATE_MAIL, x, y, FALSE, FALSE); ghostobj->spe = rnd(5) - 2; if (!rn2(3)) ghostobj->oerodeproof = TRUE; if (rn2(4)) curse(ghostobj); } if (rn2(2)) { ghostobj = mksobj_at(FAKE_AMULET_OF_YENDOR, x, y, TRUE, FALSE); ghostobj->known = TRUE; } } /*extralev.c*/