/* NetHack 5.0	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*/
