root/games/hack/hack.pri.c
/*      $NetBSD: hack.pri.c,v 1.13 2010/02/03 15:34:38 roy 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.pri.c,v 1.13 2010/02/03 15:34:38 roy Exp $");
#endif                          /* not lint */

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

static xchar scrlx, scrhx, scrly, scrhy;        /* corners of new area on
                                                 * screen */

static void cornbot(int);

void
swallowed(void)
{
        char            ulook[] = "|@|";
        ulook[1] = u.usym;

        cls();
        curs(u.ux - 1, u.uy + 1);
        fputs("/-\\", stdout);
        curx = u.ux + 2;
        curs(u.ux - 1, u.uy + 2);
        fputs(ulook, stdout);
        curx = u.ux + 2;
        curs(u.ux - 1, u.uy + 3);
        fputs("\\-/", stdout);
        curx = u.ux + 2;
        u.udispl = 1;
        u.udisx = u.ux;
        u.udisy = u.uy;
}


/* VARARGS1 */
static boolean panicking;

void
panic(const char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);
        if (panicking++)
                exit(1);        /* avoid loops - this should never happen */
        home();
        puts(" Suddenly, the dungeon collapses.");
        fputs(" ERROR:  ", stdout);
        vprintf(fmt, ap);
        va_end(ap);
#ifdef DEBUG
#ifdef UNIX
        if (!fork())
                abort();        /* generate core dump */
#endif  /* UNIX */
#endif  /* DEBUG */
        more();                 /* contains a fflush() */
        done("panicked");
}

void
atl(int x, int y, int ch)
{
        struct rm      *crm = &levl[x][y];

        if (x < 0 || x > COLNO - 1 || y < 0 || y > ROWNO - 1) {
                impossible("atl(%d,%d,%c)", x, y, ch);
                return;
        }
        if (crm->seen && crm->scrsym == ch)
                return;
        crm->scrsym = ch;
        crm->new = 1;
        on_scr(x, y);
}

void
on_scr(int x, int y)
{
        if (x < scrlx)
                scrlx = x;
        if (x > scrhx)
                scrhx = x;
        if (y < scrly)
                scrly = y;
        if (y > scrhy)
                scrhy = y;
}

/*
 * call: (x,y) - display (-1,0) - close (leave last symbol) (-1,-1)- close
 * (undo last symbol) (-1,let)-open: initialize symbol (-2,let)-change let
 */

void
tmp_at(schar x, schar y)
{
        static schar    prevx, prevy;
        static char     let;
        if ((int) x == -2) {    /* change let call */
                let = y;
                return;
        }
        if ((int) x == -1 && (int) y >= 0) {    /* open or close call */
                let = y;
                prevx = -1;
                return;
        }
        if (prevx >= 0 && cansee(prevx, prevy)) {
                delay_output();
                prl(prevx, prevy);      /* in case there was a monster */
                at(prevx, prevy, levl[prevx][prevy].scrsym);
        }
        if (x >= 0) {           /* normal call */
                if (cansee(x, y))
                        at(x, y, let);
                prevx = x;
                prevy = y;
        } else {                /* close call */
                let = 0;
                prevx = -1;
        }
}

/* like the previous, but the symbols are first erased on completion */
void
Tmp_at(schar x, schar y)
{
        static char     let;
        static xchar    cnt;
        static coord    tc[COLNO];      /* but watch reflecting beams! */
        int xx, yy;
        if ((int) x == -1) {
                if (y > 0) {    /* open call */
                        let = y;
                        cnt = 0;
                        return;
                }
                /* close call (do not distinguish y==0 and y==-1) */
                while (cnt--) {
                        xx = tc[cnt].x;
                        yy = tc[cnt].y;
                        prl(xx, yy);
                        at(xx, yy, levl[xx][yy].scrsym);
                }
                cnt = let = 0;  /* superfluous */
                return;
        }
        if ((int) x == -2) {    /* change let call */
                let = y;
                return;
        }
        /* normal call */
        if (cansee(x, y)) {
                if (cnt)
                        delay_output();
                at(x, y, let);
                tc[cnt].x = x;
                tc[cnt].y = y;
                if (++cnt >= COLNO)
                        panic("Tmp_at overflow?");
                levl[x][y].new = 0;     /* prevent pline-nscr erasing --- */
        }
}

void
setclipped(void)
{
        error("Hack needs a screen of size at least %d by %d.\n",
              ROWNO + 2, COLNO);
}

void
at(xchar x, xchar y, int ch)
{
#ifndef lint
        /* if xchar is unsigned, lint will complain about  if(x < 0)  */
        if (x < 0 || x > COLNO - 1 || y < 0 || y > ROWNO - 1) {
                impossible("At gets 0%o at %d %d.", ch, x, y);
                return;
        }
#endif  /* lint */
        if (!ch) {
                impossible("At gets null at %d %d.", x, y);
                return;
        }
        y += 2;
        curs(x, y);
        (void) putchar(ch);
        curx++;
}

void
prme(void)
{
        if (!Invisible)
                at(u.ux, u.uy, u.usym);
}

int
doredraw(void)
{
        docrt();
        return (0);
}

void
docrt(void)
{
        int x, y;
        struct rm      *room;
        struct monst   *mtmp;

        if (u.uswallow) {
                swallowed();
                return;
        }
        cls();

        /*
         * Some ridiculous code to get display of @ and monsters (almost)
         * right
         */
        if (!Invisible) {
                levl[(u.udisx = u.ux)][(u.udisy = u.uy)].scrsym = u.usym;
                levl[u.udisx][u.udisy].seen = 1;
                u.udispl = 1;
        } else
                u.udispl = 0;

        seemons();              /* reset old positions */
        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
                mtmp->mdispl = 0;
        seemons();              /* force new positions to be shown */
        /*
         * This nonsense should disappear soon
         * ---------------------------------
         */

        for (y = 0; y < ROWNO; y++)
                for (x = 0; x < COLNO; x++)
                        if ((room = &levl[x][y])->new) {
                                room->new = 0;
                                at(x, y, room->scrsym);
                        } else if (room->seen)
                                at(x, y, room->scrsym);
        scrlx = COLNO;
        scrly = ROWNO;
        scrhx = scrhy = 0;
        flags.botlx = 1;
        bot();
}

void
docorner(int xmin, int ymax)
{
        int x, y;
        struct rm      *room;
        struct monst   *mtmp;

        if (u.uswallow) {       /* Can be done more efficiently */
                swallowed();
                return;
        }
        seemons();              /* reset old positions */
        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
                if (mtmp->mx >= xmin && mtmp->my < ymax)
                        mtmp->mdispl = 0;
        seemons();              /* force new positions to be shown */

        for (y = 0; y < ymax; y++) {
                if (y > ROWNO && CD)
                        break;
                curs(xmin, y + 2);
                cl_end();
                if (y < ROWNO) {
                        for (x = xmin; x < COLNO; x++) {
                                if ((room = &levl[x][y])->new) {
                                        room->new = 0;
                                        at(x, y, room->scrsym);
                                } else if (room->seen)
                                        at(x, y, room->scrsym);
                        }
                }
        }
        if (ymax > ROWNO) {
                cornbot(xmin - 1);
                if (ymax > ROWNO + 1 && CD) {
                        curs(1, ROWNO + 3);
                        cl_eos();
                }
        }
}

void
curs_on_u(void)
{
        curs(u.ux, u.uy + 2);
}

void
pru(void)
{
        if (u.udispl && (Invisible || u.udisx != u.ux || u.udisy != u.uy))
                /* if(! levl[u.udisx][u.udisy].new) */
                if (!vism_at(u.udisx, u.udisy))
                        newsym(u.udisx, u.udisy);
        if (Invisible) {
                u.udispl = 0;
                prl(u.ux, u.uy);
        } else if (!u.udispl || u.udisx != u.ux || u.udisy != u.uy) {
                atl(u.ux, u.uy, u.usym);
                u.udispl = 1;
                u.udisx = u.ux;
                u.udisy = u.uy;
        }
        levl[u.ux][u.uy].seen = 1;
}

#ifndef NOWORM
#include        "def.wseg.h"
#endif  /* NOWORM */

/* print a position that is visible for @ */
void
prl(int x, int y)
{
        struct rm      *room;
        struct monst   *mtmp;
        struct obj     *otmp;

        if (x == u.ux && y == u.uy && (!Invisible)) {
                pru();
                return;
        }
        if (!isok(x, y))
                return;
        room = &levl[x][y];
        if ((!room->typ) ||
            (IS_ROCK(room->typ) && levl[u.ux][u.uy].typ == CORR))
                return;
        if ((mtmp = m_at(x, y)) && !mtmp->mhide &&
            (!mtmp->minvis || See_invisible)) {
#ifndef NOWORM
                if (m_atseg)
                        pwseg(m_atseg);
                else
#endif  /* NOWORM */
                        pmon(mtmp);
        } else if ((otmp = o_at(x, y)) && room->typ != POOL)
                atl(x, y, otmp->olet);
        else if (mtmp && (!mtmp->minvis || See_invisible)) {
                /* must be a hiding monster, but not hiding right now */
                /* assume for the moment that long worms do not hide */
                pmon(mtmp);
        } else if (g_at(x, y) && room->typ != POOL)
                atl(x, y, '$');
        else if (!room->seen || room->scrsym == ' ') {
                room->new = room->seen = 1;
                newsym(x, y);
                on_scr(x, y);
        }
        room->seen = 1;
}

char
news0(xchar x, xchar y)
{
        struct obj     *otmp;
        struct trap    *ttmp;
        struct rm      *room;
        char            tmp;

        room = &levl[x][y];
        if (!room->seen)
                tmp = ' ';
        else if (room->typ == POOL)
                tmp = POOL_SYM;
        else if (!Blind && (otmp = o_at(x, y)))
                tmp = otmp->olet;
        else if (!Blind && g_at(x, y))
                tmp = '$';
        else if (x == xupstair && y == yupstair)
                tmp = '<';
        else if (x == xdnstair && y == ydnstair)
                tmp = '>';
        else if ((ttmp = t_at(x, y)) && ttmp->tseen)
                tmp = '^';
        else
                switch (room->typ) {
                case SCORR:
                case SDOOR:
                        tmp = room->scrsym;     /* %% wrong after killing
                                                 * mimic ! */
                        break;
                case HWALL:
                        tmp = '-';
                        break;
                case VWALL:
                        tmp = '|';
                        break;
                case LDOOR:
                case DOOR:
                        tmp = '+';
                        break;
                case CORR:
                        tmp = CORR_SYM;
                        break;
                case ROOM:
                        if (room->lit || cansee(x, y) || Blind)
                                tmp = '.';
                        else
                                tmp = ' ';
                        break;
                        /*
                                case POOL:
                                        tmp = POOL_SYM;
                                        break;
                        */
                default:
                        tmp = ERRCHAR;
                }
        return (tmp);
}

void
newsym(int x, int y)
{
        atl(x, y, news0(x, y));
}

/* used with wand of digging (or pick-axe): fill scrsym and force display */
/* also when a POOL evaporates */
void
mnewsym(int x, int y)
{
        struct rm      *room;
        char            newscrsym;

        if (!vism_at(x, y)) {
                room = &levl[x][y];
                newscrsym = news0(x, y);
                if (room->scrsym != newscrsym) {
                        room->scrsym = newscrsym;
                        room->seen = 0;
                }
        }
}

void
nosee(int x, int y)
{
        struct rm      *room;

        if (!isok(x, y))
                return;
        room = &levl[x][y];
        if (room->scrsym == '.' && !room->lit && !Blind) {
                room->scrsym = ' ';
                room->new = 1;
                on_scr(x, y);
        }
}

#ifndef QUEST
void
prl1(int x, int y)
{
        if (u.dx) {
                if (u.dy) {
                        prl(x - (2 * u.dx), y);
                        prl(x - u.dx, y);
                        prl(x, y);
                        prl(x, y - u.dy);
                        prl(x, y - (2 * u.dy));
                } else {
                        prl(x, y - 1);
                        prl(x, y);
                        prl(x, y + 1);
                }
        } else {
                prl(x - 1, y);
                prl(x, y);
                prl(x + 1, y);
        }
}

void
nose1(int x, int y)
{
        if (u.dx) {
                if (u.dy) {
                        nosee(x, u.uy);
                        nosee(x, u.uy - u.dy);
                        nosee(x, y);
                        nosee(u.ux - u.dx, y);
                        nosee(u.ux, y);
                } else {
                        nosee(x, y - 1);
                        nosee(x, y);
                        nosee(x, y + 1);
                }
        } else {
                nosee(x - 1, y);
                nosee(x, y);
                nosee(x + 1, y);
        }
}
#endif  /* QUEST */

int
vism_at(int x, int y)
{
        struct monst   *mtmp;

        return ((x == u.ux && y == u.uy && !Invisible)
                ? 1 :
                (mtmp = m_at(x, y))
                ? ((Blind && Telepat) || canseemon(mtmp)) :
                0);
}

#ifdef NEWSCR
void
pobj(struct obj *obj)
{
        int             show = (!obj->oinvis || See_invisible) &&
        cansee(obj->ox, obj->oy);
        if (obj->odispl) {
                if (obj->odx != obj->ox || obj->ody != obj->oy || !show)
                        if (!vism_at(obj->odx, obj->ody)) {
                                newsym(obj->odx, obj->ody);
                                obj->odispl = 0;
                        }
        }
        if (show && !vism_at(obj->ox, obj->oy)) {
                atl(obj->ox, obj->oy, obj->olet);
                obj->odispl = 1;
                obj->odx = obj->ox;
                obj->ody = obj->oy;
        }
}
#endif  /* NEWSCR */

void
unpobj(struct obj *obj)
{
        /*
         * if(obj->odispl){ if(!vism_at(obj->odx, obj->ody)) newsym(obj->odx,
         * obj->ody); obj->odispl = 0; }
         */
        if (!vism_at(obj->ox, obj->oy))
                newsym(obj->ox, obj->oy);
}

void
seeobjs(void)
{
        struct obj     *obj, *obj2;
        for (obj = fobj; obj; obj = obj2) {
                obj2 = obj->nobj;
                if (obj->olet == FOOD_SYM && obj->otyp >= CORPSE
                    && obj->age + 250 < moves)
                        delobj(obj);
        }
        for (obj = invent; obj; obj = obj2) {
                obj2 = obj->nobj;
                if (obj->olet == FOOD_SYM && obj->otyp >= CORPSE
                    && obj->age + 250 < moves)
                        useup(obj);
        }
}

void
seemons(void)
{
        struct monst   *mtmp;
        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
                if (mtmp->data->mlet == ';')
                        mtmp->minvis = (u.ustuck != mtmp &&
                                      levl[mtmp->mx][mtmp->my].typ == POOL);
                pmon(mtmp);
#ifndef NOWORM
                if (mtmp->wormno)
                        wormsee(mtmp->wormno);
#endif  /* NOWORM */
        }
}

void
pmon(struct monst *mon)
{
        int             show = (Blind && Telepat) || canseemon(mon);
        if (mon->mdispl) {
                if (mon->mdx != mon->mx || mon->mdy != mon->my || !show)
                        unpmon(mon);
        }
        if (show && !mon->mdispl) {
                atl(mon->mx, mon->my,
                    (!mon->mappearance
                || u.uprops[PROP(RIN_PROTECTION_FROM_SHAPE_CHANGERS)].p_flgs
                     ) ? mon->data->mlet : mon->mappearance);
                mon->mdispl = 1;
                mon->mdx = mon->mx;
                mon->mdy = mon->my;
        }
}

void
unpmon(struct monst *mon)
{
        if (mon->mdispl) {
                newsym(mon->mdx, mon->mdy);
                mon->mdispl = 0;
        }
}

void
nscr(void)
{
        int x, y;
        struct rm      *room;

        if (u.uswallow || u.ux == FAR || flags.nscrinh)
                return;
        pru();
        for (y = scrly; y <= scrhy; y++)
                for (x = scrlx; x <= scrhx; x++)
                        if ((room = &levl[x][y])->new) {
                                room->new = 0;
                                at(x, y, room->scrsym);
                        }
        scrhx = scrhy = 0;
        scrlx = COLNO;
        scrly = ROWNO;
}

/* 100 suffices for bot(); no relation with COLNO */
static char oldbot[100], newbot[100];
void
cornbot(int lth)
{
        if ((unsigned)lth < sizeof(oldbot)) {
                oldbot[lth] = 0;
                flags.botl = 1;
        }
}

void
bot(void)
{
        char           *ob = oldbot, *nb = newbot;
        int             i;
        size_t pos;

        if (flags.botlx)
                *ob = 0;
        flags.botl = flags.botlx = 0;
#ifdef GOLD_ON_BOTL
        (void) snprintf(newbot, sizeof(newbot),
                       "Level %-2d  Gold %-5lu  Hp %3d(%d)  Ac %-2d  Str ",
                       dlevel, u.ugold, u.uhp, u.uhpmax, u.uac);
#else
        (void) snprintf(newbot, sizeof(newbot),
                       "Level %-2d   Hp %3d(%d)   Ac %-2d   Str ",
                       dlevel, u.uhp, u.uhpmax, u.uac);
#endif  /* GOLD_ON_BOTL */
        if (u.ustr > 18) {
                if (u.ustr > 117)
                        (void) strlcat(newbot, "18/**", sizeof(newbot));
                else {
                        pos = strlen(newbot);
                        (void) snprintf(newbot+pos, sizeof(newbot)-pos,
                                        "18/%02d", u.ustr - 18);
                }
        } else {
                pos = strlen(newbot);
                (void) snprintf(newbot+pos, sizeof(newbot)-pos,
                                "%-2d   ", u.ustr);
        }
        pos = strlen(newbot);
#ifdef EXP_ON_BOTL
        (void) snprintf(newbot+pos, sizeof(newbot)-pos,
                        "  Exp %2d/%-5lu ", u.ulevel, u.uexp);
#else
        (void) snprintf(newbot+pos, sizeof(newbot)-pos,
                        "   Exp %2u  ", u.ulevel);
#endif  /* EXP_ON_BOTL */
        (void) strlcat(newbot, hu_stat[u.uhs], sizeof(newbot));
        if (flags.time) {
                pos = strlen(newbot);
                (void) snprintf(newbot+pos, sizeof(newbot)-pos,
                                "  %ld", moves);
        }
        if (strlen(newbot) >= COLNO) {
                char           *bp0, *bp1;
                bp0 = bp1 = newbot;
                do {
                        if (*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
                                *bp1++ = *bp0;
                } while (*bp0++);
        }
        for (i = 1; i < COLNO; i++) {
                if (*ob != *nb) {
                        curs(i, ROWNO + 2);
                        (void) putchar(*nb ? *nb : ' ');
                        curx++;
                }
                if (*ob)
                        ob++;
                if (*nb)
                        nb++;
        }
        (void) strcpy(oldbot, newbot);
}

#ifdef WAN_PROBING
void
mstatusline(struct monst *mtmp)
{
        pline("Status of %s: ", monnam(mtmp));
        pline("Level %-2d  Gold %-5lu  Hp %3d(%d)  Ac %-2d  Dam %d",
              mtmp->data->mlevel, mtmp->mgold, mtmp->mhp, mtmp->mhpmax,
           mtmp->data->ac, (mtmp->data->damn + 1) * (mtmp->data->damd + 1));
}
#endif  /* WAN_PROBING */

void
cls(void)
{
        if (flags.toplin == 1)
                more();
        flags.toplin = 0;

        clearscreen();

        flags.botlx = 1;
}