/*      $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 <[email protected]>
* 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;
}