root/games/hack/hack.c
/*      $NetBSD: hack.c,v 1.12 2021/05/02 12:50:44 rillig Exp $ */

/*
 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
 * Amsterdam
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Stichting Centrum voor Wiskunde en
 * Informatica, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: hack.c,v 1.12 2021/05/02 12:50:44 rillig Exp $");
#endif                          /* not lint */

#include "hack.h"
#include "extern.h"

static void movobj(struct obj *, int, int);
static int inv_cnt(void);

/*
 * called on movement: 1. when throwing ball+chain far away 2. when
 * teleporting 3. when walking out of a lit room
 */
void
unsee(void)
{
        int x, y;
        struct rm *lev;

        /*
                if(u.udispl){
                        u.udispl = 0;
                        newsym(u.udisx, u.udisy);
                }
        */
#ifndef QUEST
        if (seehx) {
                seehx = 0;
        } else
#endif  /* QUEST */
                for (x = u.ux - 1; x < u.ux + 2; x++)
                        for (y = u.uy - 1; y < u.uy + 2; y++) {
                                if (!isok(x, y))
                                        continue;
                                lev = &levl[x][y];
                                if (!lev->lit && lev->scrsym == '.') {
                                        lev->scrsym = ' ';
                                        lev->new = 1;
                                        on_scr(x, y);
                                }
                        }
}

/*
 * called: in hack.eat.c: seeoff(0) - blind after eating rotten food in
 * hack.mon.c: seeoff(0) - blinded by a yellow light in hack.mon.c: seeoff(1)
 * - swallowed in hack.do.c:  seeoff(0) - blind after drinking potion in
 * hack.do.c:  seeoff(1) - go up or down the stairs in hack.trap.c:seeoff(1)
 * - fall through trapdoor
 */
/* mode: */
        /* 1 to redo @, 0 to leave them *//* 1 means
         * misc movement, 0 means blindness */
void
seeoff(int mode)
{
        int x, y;
        struct rm *lev;

        if (u.udispl && mode) {
                u.udispl = 0;
                levl[u.udisx][u.udisy].scrsym = news0(u.udisx, u.udisy);
        }
#ifndef QUEST
        if (seehx) {
                seehx = 0;
        } else
#endif  /* QUEST */
        if (!mode) {
                for (x = u.ux - 1; x < u.ux + 2; x++)
                        for (y = u.uy - 1; y < u.uy + 2; y++) {
                                if (!isok(x, y))
                                        continue;
                                lev = &levl[x][y];
                                if (!lev->lit && lev->scrsym == '.')
                                        lev->seen = 0;
                        }
        }
}

void
domove(void)
{
        xchar           oldx, oldy;
        struct monst *mtmp = NULL;
        struct rm *tmpr, *ust;
        struct trap    *trap = NULL;
        struct obj *otmp = NULL;

        u_wipe_engr(rnd(5));

        if (inv_weight() > 0) {
                pline("You collapse under your load.");
                nomul(0);
                return;
        }
        if (u.uswallow) {
                u.dx = u.dy = 0;
                u.ux = u.ustuck->mx;
                u.uy = u.ustuck->my;
        } else {
                if (Confusion) {
                        do {
                                confdir();
                        } while (!isok(u.ux + u.dx, u.uy + u.dy) ||
                               IS_ROCK(levl[u.ux + u.dx][u.uy + u.dy].typ));
                }
                if (!isok(u.ux + u.dx, u.uy + u.dy)) {
                        nomul(0);
                        return;
                }
        }

        ust = &levl[u.ux][u.uy];
        oldx = u.ux;
        oldy = u.uy;
        if (!u.uswallow && (trap = t_at(u.ux + u.dx, u.uy + u.dy)) && trap->tseen)
                nomul(0);
        if (u.ustuck && !u.uswallow && (u.ux + u.dx != u.ustuck->mx ||
                                        u.uy + u.dy != u.ustuck->my)) {
                if (dist(u.ustuck->mx, u.ustuck->my) > 2) {
                        /* perhaps it fled (or was teleported or ... ) */
                        u.ustuck = 0;
                } else {
                        if (Blind)
                                pline("You cannot escape from it!");
                        else
                                pline("You cannot escape from %s!",
                                      monnam(u.ustuck));
                        nomul(0);
                        return;
                }
        }
        if (u.uswallow || (mtmp = m_at(u.ux + u.dx, u.uy + u.dy))) {
                /* attack monster */

                nomul(0);
                gethungry();
                if (multi < 0)
                        return; /* we just fainted */

                /* try to attack; note that it might evade */
                if (attack(u.uswallow ? u.ustuck : mtmp))
                        return;
        }
        /* not attacking an animal, so we try to move */
        if (u.utrap) {
                if (u.utraptype == TT_PIT) {
                        pline("You are still in a pit.");
                        u.utrap--;
                } else {
                        pline("You are caught in a beartrap.");
                        if ((u.dx && u.dy) || !rn2(5))
                                u.utrap--;
                }
                return;
        }
        tmpr = &levl[u.ux + u.dx][u.uy + u.dy];
        if (IS_ROCK(tmpr->typ) ||
            (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))) {
                flags.move = 0;
                nomul(0);
                return;
        }
        while ((otmp = sobj_at(ENORMOUS_ROCK, u.ux + u.dx, u.uy + u.dy)) != NULL){
                xchar  rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy;
                struct trap *ttmp;
                nomul(0);
                if (isok(rx, ry) && !IS_ROCK(levl[rx][ry].typ) &&
                    (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) &&
                    !sobj_at(ENORMOUS_ROCK, rx, ry)) {
                        if (m_at(rx, ry)) {
                                pline("You hear a monster behind the rock.");
                                pline("Perhaps that's why you cannot move it.");
                                goto cannot_push;
                        }
                        if ((ttmp = t_at(rx, ry)) != NULL)
                                switch (ttmp->ttyp) {
                                case PIT:
                                        pline("You push the rock into a pit!");
                                        deltrap(ttmp);
                                        delobj(otmp);
                                        pline("It completely fills the pit!");
                                        continue;
                                case TELEP_TRAP:
                                        pline("You push the rock and suddenly it disappears!");
                                        delobj(otmp);
                                        continue;
                                }
                        if (levl[rx][ry].typ == POOL) {
                                levl[rx][ry].typ = ROOM;
                                mnewsym(rx, ry);
                                prl(rx, ry);
                                pline("You push the rock into the water.");
                                pline("Now you can cross the water!");
                                delobj(otmp);
                                continue;
                        }
                        otmp->ox = rx;
                        otmp->oy = ry;
                        /* pobj(otmp); */
                        if (cansee(rx, ry))
                                atl(rx, ry, otmp->olet);
                        if (Invisible)
                                newsym(u.ux + u.dx, u.uy + u.dy);

                        {
                                static long     lastmovetime;
                                /*
                                 * note: this var contains garbage initially
                                 * and after a restore
                                 */
                                if (moves > lastmovetime + 2 || moves < lastmovetime)
                                        pline("With great effort you move the enormous rock.");
                                lastmovetime = moves;
                        }
                } else {
                        pline("You try to move the enormous rock, but in vain.");
        cannot_push:
                        if ((!invent || inv_weight() + 90 <= 0) &&
                            (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][u.uy + u.dy].typ)
                                && IS_ROCK(levl[u.ux + u.dx][u.uy].typ)))) {
                                pline("However, you can squeeze yourself into a small opening.");
                                break;
                        } else
                                return;
                }
        }
        if (u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy + u.dy].typ) &&
            IS_ROCK(levl[u.ux + u.dx][u.uy].typ) &&
            invent && inv_weight() + 40 > 0) {
                pline("You are carrying too much to get through.");
                nomul(0);
                return;
        }
        if (Punished &&
            DIST(u.ux + u.dx, u.uy + u.dy, uchain->ox, uchain->oy) > 2) {
                if (carried(uball)) {
                        movobj(uchain, u.ux, u.uy);
                        goto nodrag;
                }
                if (DIST(u.ux + u.dx, u.uy + u.dy, uball->ox, uball->oy) < 3) {
                        /* leave ball, move chain under/over ball */
                        movobj(uchain, uball->ox, uball->oy);
                        goto nodrag;
                }
                if (inv_weight() + (int) uball->owt / 2 > 0) {
                        pline("You cannot %sdrag the heavy iron ball.",
                              invent ? "carry all that and also " : "");
                        nomul(0);
                        return;
                }
                movobj(uball, uchain->ox, uchain->oy);
                unpobj(uball);  /* BAH %% */
                uchain->ox = u.ux;
                uchain->oy = u.uy;
                nomul(-2);
                nomovemsg = "";
nodrag: ;
        }
        u.ux += u.dx;
        u.uy += u.dy;
        if (flags.run) {
                if (tmpr->typ == DOOR ||
                    (xupstair == u.ux && yupstair == u.uy) ||
                    (xdnstair == u.ux && ydnstair == u.uy))
                        nomul(0);
        }
        if (tmpr->typ == POOL && !Levitation)
                drown();        /* not necessarily fatal */

        /*
                if(u.udispl) {
                        u.udispl = 0;
                        newsym(oldx,oldy);
                }
        */
        if (!Blind) {
#ifdef QUEST
                setsee();
#else
                if (ust->lit) {
                        if (tmpr->lit) {
                                if (tmpr->typ == DOOR)
                                        prl1(u.ux + u.dx, u.uy + u.dy);
                                else if (ust->typ == DOOR)
                                        nose1(oldx - u.dx, oldy - u.dy);
                        } else {
                                unsee();
                                prl1(u.ux + u.dx, u.uy + u.dy);
                        }
                } else {
                        if (tmpr->lit)
                                setsee();
                        else {
                                prl1(u.ux + u.dx, u.uy + u.dy);
                                if (tmpr->typ == DOOR) {
                                        if (u.dy) {
                                                prl(u.ux - 1, u.uy);
                                                prl(u.ux + 1, u.uy);
                                        } else {
                                                prl(u.ux, u.uy - 1);
                                                prl(u.ux, u.uy + 1);
                                        }
                                }
                        }
                        nose1(oldx - u.dx, oldy - u.dy);
                }
#endif  /* QUEST */
        } else {
                pru();
        }
        if (!flags.nopick)
                pickup(1);
        if (trap)
                dotrap(trap);   /* fall into pit, arrow trap, etc. */
        (void) inshop();
        if (!Blind)
                read_engr_at(u.ux, u.uy);
}

static void
movobj(struct obj *obj, int ox, int oy)
{
        /* Some dirty programming to get display right */
        freeobj(obj);
        unpobj(obj);
        obj->nobj = fobj;
        fobj = obj;
        obj->ox = ox;
        obj->oy = oy;
}

int
dopickup(void)
{
        if (!g_at(u.ux, u.uy) && !o_at(u.ux, u.uy)) {
                pline("There is nothing here to pick up.");
                return (0);
        }
        if (Levitation) {
                pline("You cannot reach the floor.");
                return (1);
        }
        pickup(0);
        return (1);
}

void
pickup(int all)
{
        struct gold *gold;
        struct obj *obj, *obj2;
        int    wt;

        if (Levitation)
                return;
        while ((gold = g_at(u.ux, u.uy)) != NULL) {
                pline("%ld gold piece%s.", gold->amount, plur(gold->amount));
                u.ugold += gold->amount;
                flags.botl = 1;
                freegold(gold);
                if (flags.run)
                        nomul(0);
                if (Invisible)
                        newsym(u.ux, u.uy);
        }

        /* check for more than one object */
        if (!all) {
                int    ct = 0;

                for (obj = fobj; obj; obj = obj->nobj)
                        if (obj->ox == u.ux && obj->oy == u.uy)
                                if (!Punished || obj != uchain)
                                        ct++;
                if (ct < 2)
                        all++;
                else
                        pline("There are several objects here.");
        }
        for (obj = fobj; obj; obj = obj2) {
                obj2 = obj->nobj;       /* perhaps obj will be picked up */
                if (obj->ox == u.ux && obj->oy == u.uy) {
                        if (flags.run)
                                nomul(0);

                        /* do not pick up uchain */
                        if (Punished && obj == uchain)
                                continue;

                        if (!all) {
                                char            c;

                                pline("Pick up %s ? [ynaq]", doname(obj));
                                while (!strchr("ynaq ", (c = readchar())))
                                        sound_bell();
                                if (c == 'q')
                                        return;
                                if (c == 'n')
                                        continue;
                                if (c == 'a')
                                        all = 1;
                        }
                        if (obj->otyp == DEAD_COCKATRICE && !uarmg) {
                                pline("Touching the dead cockatrice is a fatal mistake.");
                                pline("You turn to stone.");
                                killer = "cockatrice cadaver";
                                done("died");
                        }
                        if (obj->otyp == SCR_SCARE_MONSTER) {
                                if (!obj->spe)
                                        obj->spe = 1;
                                else {
                                        /*
                                         * Note: perhaps the 1st pickup
                                         * failed: you cannot carry anymore,
                                         * and so we never dropped it - let's
                                         * assume that treading on it twice
                                         * also destroys the scroll
                                         */
                                        pline("The scroll turns to dust as you pick it up.");
                                        delobj(obj);
                                        continue;
                                }
                        }
                        wt = inv_weight() + obj->owt;
                        if (wt > 0) {
                                if (obj->quan > 1) {
                                        /* see how many we can lift */
                                        int             savequan = obj->quan;
                                        int             iw = inv_weight();
                                        int             qq;
                                        for (qq = 1; qq < savequan; qq++) {
                                                obj->quan = qq;
                                                if (iw + weight(obj) > 0)
                                                        break;
                                        }
                                        obj->quan = savequan;
                                        qq--;
                                        /* we can carry qq of them */
                                        if (!qq)
                                                goto too_heavy;
                                        pline("You can only carry %s of the %s lying here.",
                                              (qq == 1) ? "one" : "some",
                                              doname(obj));
                                        (void) splitobj(obj, qq);
                                        /*
                                         * note: obj2 is set already, so
                                         * we'll never encounter the other
                                         * half; if it should be otherwise
                                         * then write obj2 =
                                         * splitobj(obj,qq);
                                         */
                                        goto lift_some;
                                }
                too_heavy:
                                pline("There %s %s here, but %s.",
                                      (obj->quan == 1) ? "is" : "are",
                                      doname(obj),
                                 !invent ? "it is too heavy for you to lift"
                                      : "you cannot carry anymore");
                                break;
                        }
        lift_some:
                        if (inv_cnt() >= 52) {
                                pline("Your knapsack cannot accommodate anymore items.");
                                break;
                        }
                        if (wt > -5)
                                pline("You have a little trouble lifting");
                        freeobj(obj);
                        if (Invisible)
                                newsym(u.ux, u.uy);
                        addtobill(obj); /* sets obj->unpaid if necessary */
                        {
                                int             pickquan = obj->quan;
                                int             mergquan;
                                if (!Blind)
                                        obj->dknown = 1;        /* this is done by
                                                                 * prinv(), but addinv()
                                                                 * needs it already for
                                                                 * merging */
                                obj = addinv(obj);      /* might merge it with
                                                         * other objects */
                                mergquan = obj->quan;
                                obj->quan = pickquan;   /* to fool prinv() */
                                prinv(obj);
                                obj->quan = mergquan;
                        }
                }
        }
}

/* stop running if we see something interesting */
/* turn around a corner if that is the only way we can proceed */
/* do not turn left or right twice */
void
lookaround(void)
{
        int    x, y, i, x0 = 0, y0 = 0, m0 = 0, i0 = 9;
        int    corrct = 0, noturn = 0;
        struct monst *mtmp;
        if (Blind || flags.run == 0)
                return;
        if (flags.run == 1 && levl[u.ux][u.uy].typ == ROOM)
                return;
#ifdef QUEST
        if (u.ux0 == u.ux + u.dx && u.uy0 == u.uy + u.dy)
                goto stop;
#endif  /* QUEST */
        for (x = u.ux - 1; x <= u.ux + 1; x++)
                for (y = u.uy - 1; y <= u.uy + 1; y++) {
                        if (x == u.ux && y == u.uy)
                                continue;
                        if (!levl[x][y].typ)
                                continue;
                        if ((mtmp = m_at(x, y)) && !mtmp->mimic &&
                            (!mtmp->minvis || See_invisible)) {
                                if (!mtmp->mtame || (x == u.ux + u.dx && y == u.uy + u.dy))
                                        goto stop;
                        } else
                                mtmp = 0;       /* invisible M cannot
                                                 * influence us */
                        if (x == u.ux - u.dx && y == u.uy - u.dy)
                                continue;
                        switch (levl[x][y].scrsym) {
                        case '|':
                        case '-':
                        case '.':
                        case ' ':
                                break;
                        case '+':
                                if (x != u.ux && y != u.uy)
                                        break;
                                if (flags.run != 1)
                                        goto stop;
                                /* FALLTHROUGH */
                        case CORR_SYM:
                corr:
                                if (flags.run == 1 || flags.run == 3) {
                                        i = DIST(x, y, u.ux + u.dx, u.uy + u.dy);
                                        if (i > 2)
                                                break;
                                        if (corrct == 1 && DIST(x, y, x0, y0) != 1)
                                                noturn = 1;
                                        if (i < i0) {
                                                i0 = i;
                                                x0 = x;
                                                y0 = y;
                                                m0 = mtmp ? 1 : 0;
                                        }
                                }
                                corrct++;
                                break;
                        case '^':
                                if (flags.run == 1)
                                        goto corr;      /* if you must */
                                if (x == u.ux + u.dx && y == u.uy + u.dy)
                                        goto stop;
                                break;
                        default:        /* e.g. objects or trap or stairs */
                                if (flags.run == 1)
                                        goto corr;
                                if (mtmp)
                                        break;  /* d */
                stop:
                                nomul(0);
                                return;
                        }
                }
#ifdef QUEST
        if (corrct > 0 && (flags.run == 4 || flags.run == 5))
                goto stop;
#endif  /* QUEST */
        if (corrct > 1 && flags.run == 2)
                goto stop;
        if ((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
            (corrct == 1 || (corrct == 2 && i0 == 1))) {
                /* make sure that we do not turn too far */
                if (i0 == 2) {
                        if (u.dx == y0 - u.uy && u.dy == u.ux - x0)
                                i = 2;  /* straight turn right */
                        else
                                i = -2; /* straight turn left */
                } else if (u.dx && u.dy) {
                        if ((u.dx == u.dy && y0 == u.uy) ||
                            (u.dx != u.dy && y0 != u.uy))
                                i = -1; /* half turn left */
                        else
                                i = 1;  /* half turn right */
                } else {
                        if ((x0 - u.ux == y0 - u.uy && !u.dy) ||
                            (x0 - u.ux != y0 - u.uy && u.dy))
                                i = 1;  /* half turn right */
                        else
                                i = -1; /* half turn left */
                }
                i += u.last_str_turn;
                if (i <= 2 && i >= -2) {
                        u.last_str_turn = i;
                        u.dx = x0 - u.ux, u.dy = y0 - u.uy;
                }
        }
}

/* something like lookaround, but we are not running */
/* react only to monsters that might hit us */
int
monster_nearby(void)
{
        int    x, y;
        struct monst *mtmp;
        if (!Blind)
                for (x = u.ux - 1; x <= u.ux + 1; x++)
                        for (y = u.uy - 1; y <= u.uy + 1; y++) {
                                if (x == u.ux && y == u.uy)
                                        continue;
                                if ((mtmp = m_at(x, y)) && !mtmp->mimic && !mtmp->mtame &&
                                    !mtmp->mpeaceful && !strchr("Ea", mtmp->data->mlet) &&
                                    !mtmp->mfroz && !mtmp->msleep &&    /* aplvax!jcn */
                                    (!mtmp->minvis || See_invisible))
                                        return (1);
                        }
        return (0);
}

#ifdef QUEST
int
cansee(xchar x, xchar y)
{
        int    dx, dy, adx, ady, sdx, sdy, dmax, d;
        if (Blind)
                return (0);
        if (!isok(x, y))
                return (0);
        d = dist(x, y);
        if (d < 3)
                return (1);
        if (d > u.uhorizon * u.uhorizon)
                return (0);
        if (!levl[x][y].lit)
                return (0);
        dx = x - u.ux;
        adx = abs(dx);
        sdx = sgn(dx);
        dy = y - u.uy;
        ady = abs(dy);
        sdy = sgn(dy);
        if (dx == 0 || dy == 0 || adx == ady) {
                dmax = (dx == 0) ? ady : adx;
                for (d = 1; d <= dmax; d++)
                        if (!rroom(sdx * d, sdy * d))
                                return (0);
                return (1);
        } else if (ady > adx) {
                for (d = 1; d <= ady; d++) {
                        if (!rroom(sdx * ((d * adx) / ady), sdy * d) ||
                            !rroom(sdx * ((d * adx - 1) / ady + 1), sdy * d))
                                return (0);
                }
                return (1);
        } else {
                for (d = 1; d <= adx; d++) {
                        if (!rroom(sdx * d, sdy * ((d * ady) / adx)) ||
                            !rroom(sdx * d, sdy * ((d * ady - 1) / adx + 1)))
                                return (0);
                }
                return (1);
        }
}

int
rroom(int x, int y)
{
        return (IS_ROOM(levl[u.ux + x][u.uy + y].typ));
}

#else

int
cansee(xchar x, xchar y)
{
        if (Blind || u.uswallow)
                return (0);
        if (dist(x, y) < 3)
                return (1);
        if (levl[x][y].lit && seelx <= x && x <= seehx && seely <= y &&
            y <= seehy)
                return (1);
        return (0);
}
#endif  /* QUEST */

int
sgn(int a)
{
        return ((a > 0) ? 1 : (a == 0) ? 0 : -1);
}

#ifdef QUEST
void
setsee(void)
{
        int     x, y;

        if (Blind) {
                pru();
                return;
        }
        for (y = u.uy - u.uhorizon; y <= u.uy + u.uhorizon; y++)
                for (x = u.ux - u.uhorizon; x <= u.ux + u.uhorizon; x++) {
                        if (cansee(x, y))
                                prl(x, y);
                }
}

#else

void
setsee(void)
{
        int x, y;

        if (Blind) {
                pru();
                return;
        }
        if (!levl[u.ux][u.uy].lit) {
                seelx = u.ux - 1;
                seehx = u.ux + 1;
                seely = u.uy - 1;
                seehy = u.uy + 1;
        } else {
                for (seelx = u.ux; levl[seelx - 1][u.uy].lit; seelx--);
                for (seehx = u.ux; levl[seehx + 1][u.uy].lit; seehx++);
                for (seely = u.uy; levl[u.ux][seely - 1].lit; seely--);
                for (seehy = u.uy; levl[u.ux][seehy + 1].lit; seehy++);
        }
        for (y = seely; y <= seehy; y++)
                for (x = seelx; x <= seehx; x++) {
                        prl(x, y);
                }
        if (!levl[u.ux][u.uy].lit)
                seehx = 0;      /* seems necessary elsewhere */
        else {
                if (seely == u.uy)
                        for (x = u.ux - 1; x <= u.ux + 1; x++)
                                prl(x, seely - 1);
                if (seehy == u.uy)
                        for (x = u.ux - 1; x <= u.ux + 1; x++)
                                prl(x, seehy + 1);
                if (seelx == u.ux)
                        for (y = u.uy - 1; y <= u.uy + 1; y++)
                                prl(seelx - 1, y);
                if (seehx == u.ux)
                        for (y = u.uy - 1; y <= u.uy + 1; y++)
                                prl(seehx + 1, y);
        }
}
#endif  /* QUEST */

void
nomul(int nval)
{
        if (multi < 0)
                return;
        multi = nval;
        flags.mv = flags.run = 0;
}

int
abon(void)
{
        if (u.ustr == 3)
                return (-3);
        else if (u.ustr < 6)
                return (-2);
        else if (u.ustr < 8)
                return (-1);
        else if (u.ustr < 17)
                return (0);
        else if (u.ustr < 69)
                return (1);     /* up to 18/50 */
        else if (u.ustr < 118)
                return (2);
        else
                return (3);
}

int
dbon(void)
{
        if (u.ustr < 6)
                return (-1);
        else if (u.ustr < 16)
                return (0);
        else if (u.ustr < 18)
                return (1);
        else if (u.ustr == 18)
                return (2);     /* up to 18 */
        else if (u.ustr < 94)
                return (3);     /* up to 18/75 */
        else if (u.ustr < 109)
                return (4);     /* up to 18/90 */
        else if (u.ustr < 118)
                return (5);     /* up to 18/99 */
        else
                return (6);
}

/* may kill you; cause may be poison or */
/* monster like 'A' */
void
losestr(int num)
{
        u.ustr -= num;
        while (u.ustr < 3) {
                u.ustr++;
                u.uhp -= 6;
                u.uhpmax -= 6;
        }
        flags.botl = 1;
}

void
losehp(int n, const char *knam)
{
        u.uhp -= n;
        if (u.uhp > u.uhpmax)
                u.uhpmax = u.uhp;       /* perhaps n was negative */
        flags.botl = 1;
        if (u.uhp < 1) {
                killer = knam;  /* the thing that killed you */
                done("died");
        }
}

void
losehp_m(int n, struct monst *mtmp)
{
        u.uhp -= n;
        flags.botl = 1;
        if (u.uhp < 1)
                done_in_by(mtmp);
}

void
losexp(void)
{                               /* hit by V or W */
        int num;

        if (u.ulevel > 1)
                pline("Goodbye level %u.", u.ulevel--);
        else
                u.uhp = -1;
        num = rnd(10);
        u.uhp -= num;
        u.uhpmax -= num;
        u.uexp = newuexp();
        flags.botl = 1;
}

int
inv_weight(void)
{
        struct obj *otmp = invent;
        int    wt = (u.ugold + 500) / 1000;
        int    carrcap;
        if (Levitation)         /* pugh@cornell */
                carrcap = MAX_CARR_CAP;
        else {
                carrcap = 5 * (((u.ustr > 18) ? 20 : u.ustr) + u.ulevel);
                if (carrcap > MAX_CARR_CAP)
                        carrcap = MAX_CARR_CAP;
                if (Wounded_legs & LEFT_SIDE)
                        carrcap -= 10;
                if (Wounded_legs & RIGHT_SIDE)
                        carrcap -= 10;
        }
        while (otmp) {
                wt += otmp->owt;
                otmp = otmp->nobj;
        }
        return (wt - carrcap);
}

static int
inv_cnt(void)
{
        struct obj *otmp = invent;
        int    ct = 0;
        while (otmp) {
                ct++;
                otmp = otmp->nobj;
        }
        return (ct);
}

long
newuexp(void)
{
        return (10 * (1L << (u.ulevel - 1)));
}