root/games/larn/monster.c
/*      $NetBSD: monster.c,v 1.20 2021/05/02 12:50:45 rillig Exp $      */

/*
 * monster.c    Larn is copyrighted 1986 by Noah Morgan.
 *
 * This file contains the following functions:
 * ----------------------------------------------------------------------------
 *
 * createmonster(monstno)       Function to create a monster next to the player
 * int monstno;
 *
 * int cgood(x,y,itm,monst)Function to check location for emptiness
 * int x,y,itm,monst;
 *
 * createitem(it,arg)           Routine to place an item next to the player
 * int it,arg;
 *
 * cast()                       Subroutine called by parse to cast a spell for the user
 *
 * speldamage(x)        Function to perform spell functions cast by the player
 * int x;
 *
 * loseint()            Routine to decrement your int (intelligence) if > 3
 *
 * isconfuse()  Routine to check to see if player is confused
 *
 * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster
 * int x,monst;
 *
 * fullhit(xx)          Function to return full damage against a monst (aka web)
 * int xx;
 *
 * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir
 * int spnum,dam,arg;
 * char *str;
 *
 * godirect(spnum,dam,str,delay,cshow)  Function to perform missile attacks
 * int spnum,dam,delay;
 * char *str,cshow;
 *
 * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt
 * int x,y;
 *
 * tdirect(spnum)               Routine to teleport away a monster
 * int spnum;
 *
 * omnidirect(sp,dam,str)  Routine to damage all monsters 1 square from player
 * int sp,dam;
 * char *str;
 *
 * dirsub(x,y)          Routine to ask for direction, then modify x,y for it
 * int *x,*y;
 *
 * vxy(x,y)             Routine to verify/fix (*x,*y) for being within bounds
 * int *x,*y;
 *
 * dirpoly(spnum)       Routine to ask for a direction and polymorph a monst
 * int spnum;
 *
 * hitmonster(x,y) Function to hit a monster at the designated coordinates
 * int x,y;
 *
 * hitm(x,y,amt)        Function to just hit a monster at a given coordinates
 * int x,y,amt;
 *
 * hitplayer(x,y)       Function for the monster to hit the player from (x,y)
 * int x,y;
 *
 * dropsomething(monst) Function to create an object when a monster dies
 * int monst;
 *
 * dropgold(amount)     Function to drop some gold around player
 * int amount;
 *
 * something(level)     Function to create a random item around player
 * int level;
 *
 * newobject(lev,i)     Routine to return a randomly selected new object
 * int lev,*i;
 *
 *  spattack(atckno,xx,yy)  Function to process special attacks from monsters
 *   int atckno,xx,yy;
 *
 * checkloss(x) Routine to subtract hp from user and flag bottomline display
 * int x;
 *
 * annihilate()   Routine to annihilate monsters around player, playerx,playery
 *
 * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
 * int x,y,dir,lifetime;
 *
 * rmsphere(x,y)        Function to delete a sphere of annihilation from list
 * int x,y;
 *
 * sphboom(x,y) Function to perform the effects of a sphere detonation
 * int x,y;
 *
 * genmonst()           Function to ask for monster and genocide from game
 *
 */
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: monster.c,v 1.20 2021/05/02 12:50:45 rillig Exp $");
#endif                          /* not lint */

#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "header.h"
#include "extern.h"

struct isave {                  /* used for altar reality */
        char            type;   /* 0=item,  1=monster */
        char            id;     /* item number or monster number */
        short           arg;    /* the type of item or hitpoints of monster */
};

static int cgood(int, int, int, int);
static void speldamage(int);
static void loseint(void);
static int isconfuse(void);
static int nospell(int, int);
static int fullhit(int);
static void direct(int, int, const char *, int);
static void ifblind(int, int);
static void tdirect(int);
static void omnidirect(int, int, const char *);
static int dirsub(int *, int *);
static void dirpoly(int);
static int hitm(int, int, int);
static void dropsomething(int);
static int spattack(int, int, int);
static void sphboom(int, int);
static void genmonst(void);

/*
 * createmonster(monstno)       Function to create a monster next to the player
 *      int monstno;
 *
 * Enter with the monster number (1 to MAXMONST+8)
 * Returns no value.
 */
void
createmonster(int mon)
{
        int    x, y, k, i;
        if (mon < 1 || mon > MAXMONST + 8) {    /* check for monster number
                                                 * out of bounds */
                beep();
                lprintf("\ncan't createmonst(%ld)\n", (long) mon);
                nap(3000);
                return;
        }
        while (monster[mon].genocided && mon < MAXMONST)
                mon++;          /* genocided? */
        for (k = rnd(8), i = -8; i < 0; i++, k++) {     /* choose direction,
                                                         * then try all */
                if (k > 8)
                        k = 1;  /* wraparound the diroff arrays */
                x = playerx + diroffx[k];
                y = playery + diroffy[k];
                if (cgood(x, y, 0, 1)) {        /* if we can create here */
                        mitem[x][y] = mon;
                        hitp[x][y] = monster[mon].hitpoints;
                        stealth[x][y] = know[x][y] = 0;
                        switch (mon) {
                        case ROTHE:
                        case POLTERGEIST:
                        case VAMPIRE:
                                stealth[x][y] = 1;
                        };
                        return;
                }
        }
}

/*
 * int cgood(x,y,itm,monst)       Function to check location for emptiness
 *      int x,y,itm,monst;
 *
 * Routine to return TRUE if a location does not have itm or monst there
 * returns FALSE (0) otherwise
 * Enter with itm or monst TRUE or FALSE if checking it
 * Example:  if itm==TRUE check for no item at this location
 *                if monst==TRUE check for no monster at this location
 * This routine will return FALSE if at a wall or the dungeon exit on level 1
 */
static int
cgood(int x, int y, int theitem, int monst)
{
#define itm __lose
        if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
                /* within bounds? */
                if (item[x][y] != OWALL) /* can't make anything on walls */
                        /* is it free of items? */
                        if (theitem == 0 || (item[x][y] == 0))
                                /* is it free of monsters? */
                                if (monst == 0 || (mitem[x][y] == 0))
                                        if ((level != 1) || (x != 33) ||
                                            (y != MAXY - 1))
                                                /* not exit to level 1 */
                                                return (1);
        return (0);
}

/*
 * createitem(it,arg)   Routine to place an item next to the player
 *      int it,arg;
 *
 * Enter with the item number and its argument (iven[], ivenarg[])
 * Returns no value, thus we don't know about createitem() failures.
 */
void
createitem(int it, int arg)
{
        int    x, y, k, i;
        if (it >= MAXOBJ)
                return;         /* no such object */
        for (k = rnd(8), i = -8; i < 0; i++, k++) {     /* choose direction,
                                                         * then try all */
                if (k > 8)
                        k = 1;  /* wraparound the diroff arrays */
                x = playerx + diroffx[k];
                y = playery + diroffy[k];
                if (cgood(x, y, 1, 0)) {        /* if we can create here */
                        item[x][y] = it;
                        know[x][y] = 0;
                        iarg[x][y] = arg;
                        return;
                }
        }
}

/*
 * cast()               Subroutine called by parse to cast a spell for the user
 *
 * No arguments and no return value.
 */
static char     eys[] = "\nEnter your spell: ";
void
cast(void)
{
        int    i, j, a, b, d;
        cursors();
        if (c[SPELLS] <= 0) {
                lprcat("\nYou don't have any spells!");
                return;
        }
        lprcat(eys);
        --c[SPELLS];
        while ((a = ttgetch()) == 'D') {
                seemagic(-1);
                cursors();
                lprcat(eys);
        }
        if (a == '\33')
                goto over;      /* to escape casting a spell     */
        if ((b = ttgetch()) == '\33')
                goto over;      /* to escape casting a spell     */
        if ((d = ttgetch()) == '\33') {
over:           lprcat(aborted);
                c[SPELLS]++;
                return;
        }                       /* to escape casting a spell     */
#ifdef EXTRA
        c[SPELLSCAST]++;
#endif
        for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++) /* seq search for his
                                                         * spell, hash? */
                if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
                        if (spelknow[i]) {
                                speldamage(i);
                                j = 1;
                                i = SPNUM;
                        }
        if (j == -1)
                lprcat("  Nothing Happened ");
        bottomline();
}

/*
 * speldamage(x)                Function to perform spell functions cast by the player
 *      int x;
 *
 * Enter with the spell number, returns no value.
 * Please insure that there are 2 spaces before all messages here
 */
static void
speldamage(int x)
{
        int    i, j, clev;
        int             xl, xh, yl, yh;
        u_char  *p, *kn, *pm;

        if (x >= SPNUM)
                return;         /* no such spell */
        if (c[TIMESTOP]) {
                lprcat("  It didn't seem to work");
                return;
        }                       /* not if time stopped */
        clev = c[LEVEL];
        if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
                lprcat("  It didn't work!");
                return;
        }
        if (clev * 3 + 2 < x) {
                lprcat("  Nothing happens.  You seem inexperienced at this");
                return;
        }
        switch (x) {
                /* ----- LEVEL 1 SPELLS ----- */

        case 0:
                if (c[PROTECTIONTIME] == 0)
                        c[MOREDEFENSES] += 2;   /* protection field +2 */
                c[PROTECTIONTIME] += 250;
                return;

        case 1:
                i = rnd(((clev + 1) << 1)) + clev + 3;
                godirect(x, i, (clev >= 2) ? "  Your missiles hit the %s" : "  Your missile hit the %s", 100, '+');     /* magic missile */

                return;

        case 2:
                if (c[DEXCOUNT] == 0)
                        c[DEXTERITY] += 3;      /* dexterity     */
                c[DEXCOUNT] += 400;
                return;

        case 3:         /* sleep                 */
                i = rnd(3) + 1;
                direct(x, fullhit(i),
                       "  While the %s slept, you smashed it %ld times", i);
                return;

        case 4:         /* charm monster         */
                c[CHARMCOUNT] += c[CHARISMA] << 1;
                return;

        case 5:
                godirect(x, rnd(10) + 15 + clev, "  The sound damages the %s", 70, '@');        /* sonic spear */
                return;

                /* ----- LEVEL 2 SPELLS ----- */

        case 6:         /* web                  */
                i = rnd(3) + 2;
                direct(x, fullhit(i),
                       "  While the %s is entangled, you hit %ld times", i);
                return;

        case 7:
                if (c[STRCOUNT] == 0)
                        c[STREXTRA] += 3;       /* strength      */
                c[STRCOUNT] += 150 + rnd(100);
                return;

        case 8:
                yl = playery - 5;       /* enlightenment */
                yh = playery + 6;
                xl = playerx - 15;
                xh = playerx + 16;
                vxy(&xl, &yl);
                vxy(&xh, &yh);  /* check bounds */
                for (i = yl; i <= yh; i++)      /* enlightenment         */
                        for (j = xl; j <= xh; j++)
                                know[j][i] = 1;
                draws(xl, xh + 1, yl, yh + 1);
                return;

        case 9:
                raisehp(20 + (clev << 1));
                return;         /* healing */

        case 10:
                c[BLINDCOUNT] = 0;
                return;         /* cure blindness        */

        case 11:
                createmonster(makemonst(level + 1) + 8);
                return;

        case 12:
                if (rnd(11) + 7 <= c[WISDOM])
                        direct(x, rnd(20) + 20 + clev, "  The %s believed!", 0);
                else
                        lprcat("  It didn't believe the illusions!");
                return;

        case 13:                /* if he has the amulet of invisibility then
                                 * add more time */
                for (j = i = 0; i < 26; i++)
                        if (iven[i] == OAMULET)
                                j += 1 + ivenarg[i];
                c[INVISIBILITY] += (j << 7) + 12;
                return;

                /* ----- LEVEL 3 SPELLS ----- */

        case 14:
                godirect(x, rnd(25 + clev) + 25 + clev, "  The fireball hits the %s", 40, '*');
                return;         /* fireball */

        case 15:
                godirect(x, rnd(25) + 20 + clev, "  Your cone of cold strikes the %s", 60, 'O');        /* cold */
                return;

        case 16:
                dirpoly(x);
                return;         /* polymorph */

        case 17:
                c[CANCELLATION] += 5 + clev;
                return;         /* cancellation  */

        case 18:
                c[HASTESELF] += 7 + clev;
                return;         /* haste self    */

        case 19:
                omnidirect(x, 30 + rnd(10), "  The %s gasps for air");  /* cloud kill */
                return;

        case 20:
                xh = min(playerx + 1, MAXX - 2);
                yh = min(playery + 1, MAXY - 2);
                for (i = max(playerx - 1, 1); i <= xh; i++)     /* vaporize rock */
                        for (j = max(playery - 1, 1); j <= yh; j++) {
                                kn = &know[i][j];
                                pm = &mitem[i][j];
                                switch (*(p = &item[i][j])) {
                                case OWALL:
                                        if (level < MAXLEVEL + MAXVLEVEL - 1)
                                                *p = *kn = 0;
                                        break;

                                case OSTATUE:
                                        if (c[HARDGAME] < 3) {
                                                *p = OBOOK;
                                                iarg[i][j] = level;
                                                *kn = 0;
                                        }
                                        break;

                                case OTHRONE:
                                        *pm = GNOMEKING;
                                        *kn = 0;
                                        *p = OTHRONE2;
                                        hitp[i][j] = monster[GNOMEKING].hitpoints;
                                        break;

                                case OALTAR:
                                        *pm = DEMONPRINCE;
                                        *kn = 0;
                                        hitp[i][j] = monster[DEMONPRINCE].hitpoints;
                                        break;
                                };
                                switch (*pm) {
                                case XORN:
                                        ifblind(i, j);
                                        hitm(i, j, 200);
                                        break;  /* Xorn takes damage from vpr */
                                }
                        }
                return;

                /* ----- LEVEL 4 SPELLS ----- */

        case 21:
                direct(x, 100 + clev, "  The %s shrivels up", 0);       /* dehydration */
                return;

        case 22:
                godirect(x, rnd(25) + 20 + (clev << 1), "  A lightning bolt hits the %s", 1, '~');      /* lightning */
                return;

        case 23:
                i = min(c[HP] - 1, c[HPMAX] / 2);       /* drain life */
                direct(x, i + i, "", 0);
                c[HP] -= i;
                return;

        case 24:
                if (c[GLOBE] == 0)
                        c[MOREDEFENSES] += 10;
                c[GLOBE] += 200;
                loseint();      /* globe of invulnerability */
                return;

        case 25:
                omnidirect(x, 32 + clev, "  The %s struggles for air in your flood!");  /* flood */
                return;

        case 26:
                if (rnd(151) == 63) {
                        beep();
                        lprcat("\nYour heart stopped!\n");
                        nap(4000);
                        died(270);
                        return;
                }
                if (c[WISDOM] > rnd(10) + 10)
                        direct(x, 2000, "  The %s's heart stopped", 0); /* finger of death */
                else
                        lprcat("  It didn't work");
                return;

                /* ----- LEVEL 5 SPELLS ----- */

        case 27:
                c[SCAREMONST] += rnd(10) + clev;
                return;         /* scare monster */

        case 28:
                c[HOLDMONST] += rnd(10) + clev;
                return;         /* hold monster */

        case 29:
                c[TIMESTOP] += rnd(20) + (clev << 1);
                return;         /* time stop */

        case 30:
                tdirect(x);
                return;         /* teleport away */

        case 31:
                omnidirect(x, 35 + rnd(10) + clev, "  The %s cringes from the flame");  /* magic fire */
                return;

                /* ----- LEVEL 6 SPELLS ----- */

        case 32:
                if ((rnd(23) == 5) && (wizard == 0)) {  /* sphere of
                                                         * annihilation */
                        beep();
                        lprcat("\nYou have been enveloped by the zone of nothingness!\n");
                        nap(4000);
                        died(258);
                        return;
                }
                xl = playerx;
                yl = playery;
                loseint();
                i = dirsub(&xl, &yl);   /* get direction of sphere */
                newsphere(xl, yl, i, rnd(20) + 11);     /* make a sphere */
                return;

        case 33:
                genmonst();
                spelknow[33] = 0;       /* genocide */
                loseint();
                return;

        case 34:                /* summon demon */
                if (rnd(100) > 30) {
                        direct(x, 150, "  The demon strikes at the %s", 0);
                        return;
                }
                if (rnd(100) > 15) {
                        lprcat("  Nothing seems to have happened");
                        return;
                }
                lprcat("  The demon turned on you and vanished!");
                beep();
                i = rnd(40) + 30;
                lastnum = 277;
                losehp(i);      /* must say killed by a demon */
                return;

        case 35:                /* walk through walls */
                c[WTW] += rnd(10) + 5;
                return;

        case 36:                /* alter reality */
                {
                        struct isave   *save;   /* pointer to item save
                                                 * structure */
                        int             sc;
                        sc = 0; /* # items saved */
                        save = (struct isave *) malloc(sizeof(struct isave) * MAXX * MAXY * 2);
                        for (j = 0; j < MAXY; j++)
                                for (i = 0; i < MAXX; i++) {    /* save all items and
                                                                 * monsters */
                                        xl = item[i][j];
                                        if (xl && xl != OWALL && xl != OANNIHILATION) {
                                                save[sc].type = 0;
                                                save[sc].id = item[i][j];
                                                save[sc++].arg = iarg[i][j];
                                        }
                                        if (mitem[i][j]) {
                                                save[sc].type = 1;
                                                save[sc].id = mitem[i][j];
                                                save[sc++].arg = hitp[i][j];
                                        }
                                        item[i][j] = OWALL;
                                        mitem[i][j] = 0;
                                        if (wizard)
                                                know[i][j] = 1;
                                        else
                                                know[i][j] = 0;
                                }
                        eat(1, 1);
                        if (level == 1)
                                item[33][MAXY - 1] = 0;
                        for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
                                item[i][j] = 0;
                        while (sc > 0) {        /* put objects back in level */
                                --sc;
                                if (save[sc].type == 0) {
                                        int             trys;
                                        for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1));
                                        if (trys) {
                                                item[i][j] = save[sc].id;
                                                iarg[i][j] = save[sc].arg;
                                        }
                                } else {        /* put monsters back in */
                                        int             trys;
                                        for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1));
                                        if (trys) {
                                                mitem[i][j] = save[sc].id;
                                                hitp[i][j] = save[sc].arg;
                                        }
                                }
                        }
                        loseint();
                        draws(0, MAXX, 0, MAXY);
                        if (wizard == 0)
                                spelknow[36] = 0;
                        free((char *) save);
                        positionplayer();
                        return;
                }

        case 37:                /* permanence */
                adjusttime(-99999L);
                spelknow[37] = 0;       /* forget */
                loseint();
                return;

        default:
                lprintf("  spell %ld not available!", (long) x);
                beep();
                return;
        };
}

/*
 * loseint()            Routine to subtract 1 from your int (intelligence) if > 3
 *
 * No arguments and no return value
 */
static void
loseint(void)
{
        if (--c[INTELLIGENCE] < 3)
                c[INTELLIGENCE] = 3;
}

/*
 * isconfuse()          Routine to check to see if player is confused
 *
 * This routine prints out a message saying "You can't aim your magic!"
 * returns 0 if not confused, non-zero (time remaining confused) if confused
 */
static int
isconfuse(void)
{
        if (c[CONFUSE]) {
                lprcat(" You can't aim your magic!");
                beep();
        }
        return (c[CONFUSE]);
}

/*
 * nospell(x,monst)     Routine to return 1 if a spell doesn't affect a monster
 *      int x,monst;
 *
 * Subroutine to return 1 if the spell can't affect the monster
 *   otherwise returns 0
 * Enter with the spell number in x, and the monster number in monst.
 */
static int
nospell(int x, int monst)
{
        int    tmp;
        if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0)
                return (0);     /* bad spell or monst */
        if ((tmp = spelweird[monst - 1][x]) == 0)
                return (0);
        cursors();
        lprc('\n');
        lprintf(spelmes[tmp], monster[monst].name);
        return (1);
}

/*
 * fullhit(xx)          Function to return full damage against a monster (aka web)
 *      int xx;
 *
 * Function to return hp damage to monster due to a number of full hits
 * Enter with the number of full hits being done
 */
static int
fullhit(int xx)
{
        int    i;
        if (xx < 0 || xx > 20)
                return (0);     /* fullhits are out of range */
        if (c[LANCEDEATH])
                return (10000); /* lance of death */
        i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
        return ((i >= 1) ? i : xx);
}

/*
 * direct(spnum,dam,str,arg)    Routine to direct spell damage 1 square in 1 dir
 *      int spnum,dam,arg;
 *      char *str;
 *
 * Routine to ask for a direction to a spell and then hit the monster
 * Enter with the spell number in spnum, the damage to be done in dam,
 *   lprintf format string in str, and lprintf's argument in arg.
 * Returns no value.
 */
static void
direct(int spnum, int dam, const char *str, int arg)
{
        int             x, y;
        int    m;
        if (spnum < 0 || spnum >= SPNUM || str == 0)
                return;         /* bad arguments */
        if (isconfuse())
                return;
        dirsub(&x, &y);
        m = mitem[x][y];
        if (item[x][y] == OMIRROR) {
                if (spnum == 3) {       /* sleep */
                        lprcat("You fall asleep! ");
                        beep();
        fool:
                        arg += 2;
                        while (arg-- > 0) {
                                parse2();
                                nap(1000);
                        }
                        return;
                } else if (spnum == 6) {        /* web */
                        lprcat("You get stuck in your own web! ");
                        beep();
                        goto fool;
                } else {
                        lastnum = 278;
                        lprintf(str, "spell caster (that's you)", (long) arg);
                        beep();
                        losehp(dam);
                        return;
                }
        }
        if (m == 0) {
                lprcat("  There wasn't anything there!");
                return;
        }
        ifblind(x, y);
        if (nospell(spnum, m)) {
                lasthx = x;
                lasthy = y;
                return;
        }
        lprintf(str, lastmonst, (long) arg);
        hitm(x, y, dam);
}

/*
 * godirect(spnum,dam,str,delay,cshow)          Function to perform missile attacks
 *      int spnum,dam,delay;
 *      char *str,cshow;
 *
 * Function to hit in a direction from a missile weapon and have it keep
 * on going in that direction until its power is exhausted
 * Enter with the spell number in spnum, the power of the weapon in hp,
 *   lprintf format string in str, the # of milliseconds to delay between
 *   locations in delay, and the character to represent the weapon in cshow.
 * Returns no value.
 */
void
godirect(int spnum, int dam, const char *str, int delay, int cshow_i)
{
        u_char  *p;
        int    x, y, m;
        int             dx, dy;
        char cshow;

        /* truncate to char width in case it matters */
        cshow = (char)cshow_i;

        if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0)
                return;         /* bad args */
        if (isconfuse())
                return;
        dirsub(&dx, &dy);
        x = dx;
        y = dy;
        dx = x - playerx;
        dy = y - playery;
        x = playerx;
        y = playery;
        while (dam > 0) {
                x += dx;
                y += dy;
                if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
                        dam = 0;
                        break;  /* out of bounds */
                }
                if ((x == playerx) && (y == playery)) { /* if energy hits player */
                        cursors();
                        lprcat("\nYou are hit by your own magic!");
                        beep();
                        lastnum = 278;
                        losehp(dam);
                        return;
                }
                if (c[BLINDCOUNT] == 0) {       /* if not blind show effect */
                        cursor(x + 1, y + 1);
                        lprc(cshow);
                        nap(delay);
                        show1cell(x, y);
                }
                if ((m = mitem[x][y])) {        /* is there a monster there? */
                        ifblind(x, y);
                        if (nospell(spnum, m)) {
                                lasthx = x;
                                lasthy = y;
                                return;
                        }
                        cursors();
                        lprc('\n');
                        lprintf(str, lastmonst);
                        dam -= hitm(x, y, dam);
                        show1cell(x, y);
                        nap(1000);
                        x -= dx;
                        y -= dy;
                } else
                        switch (*(p = &item[x][y])) {
                        case OWALL:
                                cursors();
                                lprc('\n');
                                lprintf(str, "wall");
                                if (dam >= 50 + c[HARDGAME])    /* enough damage? */
                                        if (level < MAXLEVEL + MAXVLEVEL - 1)   /* not on V3 */
                                                if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
                                                        lprcat("  The wall crumbles");
                                        god3:           *p = 0;
                                        god:            know[x][y] = 0;
                                                        show1cell(x, y);
                                                }
                god2:           dam = 0;
                                break;

                        case OCLOSEDDOOR:
                                cursors();
                                lprc('\n');
                                lprintf(str, "door");
                                if (dam >= 40) {
                                        lprcat("  The door is blasted apart");
                                        goto god3;
                                }
                                goto god2;

                        case OSTATUE:
                                cursors();
                                lprc('\n');
                                lprintf(str, "statue");
                                if (c[HARDGAME] < 3)
                                        if (dam > 44) {
                                                lprcat("  The statue crumbles");
                                                *p = OBOOK;
                                                iarg[x][y] = level;
                                                goto god;
                                        }
                                goto god2;

                        case OTHRONE:
                                cursors();
                                lprc('\n');
                                lprintf(str, "throne");
                                if (dam > 39) {
                                        mitem[x][y] = GNOMEKING;
                                        hitp[x][y] = monster[GNOMEKING].hitpoints;
                                        *p = OTHRONE2;
                                        goto god;
                                }
                                goto god2;

                        case OMIRROR:
                                dx *= -1;
                                dy *= -1;
                                break;
                        };
                dam -= 3 + (c[HARDGAME] >> 1);
        }
}

/*
 * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
 *      int x,y;
 *
 * Subroutine to copy the word "monster" into lastmonst if the player is blind
 * Enter with the coordinates (x,y) of the monster
 * Returns no value.
 */
static void
ifblind(int x, int y)
{
        const char *p;

        vxy(&x, &y);            /* verify correct x,y coordinates */
        if (c[BLINDCOUNT]) {
                lastnum = 279;
                p = "monster";
        } else {
                lastnum = mitem[x][y];
                p = monster[lastnum].name;
        }
        strcpy(lastmonst, p);
}

/*
 * tdirect(spnum)               Routine to teleport away a monster
 *      int spnum;
 *
 * Routine to ask for a direction to a spell and then teleport away monster
 * Enter with the spell number that wants to teleport away
 * Returns no value.
 */
static void
tdirect(int spnum)
{
        int             x, y;
        int    m;
        if (spnum < 0 || spnum >= SPNUM)
                return;         /* bad args */
        if (isconfuse())
                return;
        dirsub(&x, &y);
        if ((m = mitem[x][y]) == 0) {
                lprcat("  There wasn't anything there!");
                return;
        }
        ifblind(x, y);
        if (nospell(spnum, m)) {
                lasthx = x;
                lasthy = y;
                return;
        }
        fillmonst(m);
        mitem[x][y] = know[x][y] = 0;
}

/*
 * omnidirect(sp,dam,str)   Routine to damage all monsters 1 square from player
 *      int sp,dam;
 *      char *str;
 *
 * Routine to cast a spell and then hit the monster in all directions
 * Enter with the spell number in sp, the damage done to wach square in dam,
 *   and the lprintf string to identify the spell in str.
 * Returns no value.
 */
static void
omnidirect(int spnum, int dam, const char *str)
{
        int    x, y, m;

        if (spnum < 0 || spnum >= SPNUM || str == 0)
                return;         /* bad args */
        for (x = playerx - 1; x < playerx + 2; x++)
                for (y = playery - 1; y < playery + 2; y++) {
                        if ((m = mitem[x][y]) != 0) {
                                if (nospell(spnum, m) == 0) {
                                        ifblind(x, y);
                                        cursors();
                                        lprc('\n');
                                        lprintf(str, lastmonst);
                                        hitm(x, y, dam);
                                        nap(800);
                                } else {
                                        lasthx = x;
                                        lasthy = y;
                                }
                        }
                }
}

/*
 * static dirsub(x,y)           Routine to ask for direction, then modify x,y for it
 *      int *x,*y;
 *
 * Function to ask for a direction and modify an x,y for that direction
 * Enter with the origination coordinates in (x,y).
 * Returns index into diroffx[] (0-8).
 */
static int
dirsub(int *x, int *y)
{
        int    i;
        lprcat("\nIn What Direction? ");
        for (i = 0;;)
                switch (ttgetch()) {
                case 'b':
                        i++;
                        /* FALLTHROUGH */
                case 'n':
                        i++;
                        /* FALLTHROUGH */
                case 'y':
                        i++;
                        /* FALLTHROUGH */
                case 'u':
                        i++;
                        /* FALLTHROUGH */
                case 'h':
                        i++;
                        /* FALLTHROUGH */
                case 'k':
                        i++;
                        /* FALLTHROUGH */
                case 'l':
                        i++;
                        /* FALLTHROUGH */
                case 'j':
                        i++;
                        /* FALLTHROUGH */
                        goto out;
                };
out:
        *x = playerx + diroffx[i];
        *y = playery + diroffy[i];
        vxy(x, y);
        return (i);
}

/*
 * vxy(x,y)        Routine to verify/fix coordinates for being within bounds
 *      int *x,*y;
 *
 * Function to verify x & y are within the bounds for a level
 * If *x or *y is not within the absolute bounds for a level, fix them so that
 *   they are on the level.
 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
 * routine are affected.
 */
int
vxy(int *x, int *y)
{
        int             flag = 0;
        if (*x < 0) {
                *x = 0;
                flag++;
        }
        if (*y < 0) {
                *y = 0;
                flag++;
        }
        if (*x >= MAXX) {
                *x = MAXX - 1;
                flag++;
        }
        if (*y >= MAXY) {
                *y = MAXY - 1;
                flag++;
        }
        return (flag);
}

/*
 * dirpoly(spnum)       Routine to ask for a direction and polymorph a monst
 *      int spnum;
 *
 * Subroutine to polymorph a monster and ask for the direction its in
 * Enter with the spell number in spmun.
 * Returns no value.
 */
static void
dirpoly(int spnum)
{
        int             x, y, m;
        if (spnum < 0 || spnum >= SPNUM)
                return;         /* bad args */
        if (isconfuse())
                return;         /* if he is confused, he can't aim his magic */
        dirsub(&x, &y);
        if (mitem[x][y] == 0) {
                lprcat("  There wasn't anything there!");
                return;
        }
        ifblind(x, y);
        if (nospell(spnum, mitem[x][y])) {
                lasthx = x;
                lasthy = y;
                return;
        }
        while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided);
        hitp[x][y] = monster[m].hitpoints;
        show1cell(x, y);        /* show the new monster */
}

/*
 * hitmonster(x,y)      Function to hit a monster at the designated coordinates
 *      int x,y;
 *
 * This routine is used for a bash & slash type attack on a monster
 * Enter with the coordinates of the monster in (x,y).
 * Returns no value.
 */
void
hitmonster(int x, int y)
{
        int    tmp, monst, damag = 0, flag;
        if (c[TIMESTOP])
                return;         /* not if time stopped */
        vxy(&x, &y);            /* verify coordinates are within range */
        if ((monst = mitem[x][y]) == 0)
                return;
        hit3flag = 1;
        ifblind(x, y);
        tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
            c[WCLASS] / 4 - 12;
        cursors();
        /* need at least random chance to hit */
        if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
                lprcat("\nYou hit");
                flag = 1;
                damag = fullhit(1);
                if (damag < 9999)
                        damag = rnd(damag) + 1;
        } else {
                lprcat("\nYou missed");
                flag = 0;
        }
        lprcat(" the ");
        lprcat(lastmonst);
        if (flag)               /* if the monster was hit */
                if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
                        if (c[WIELD] > 0)
                                if (ivenarg[c[WIELD]] > -10) {
                                        lprintf("\nYour weapon is dulled by the %s", lastmonst);
                                        beep();
                                        --ivenarg[c[WIELD]];
                                }
        if (flag)
                hitm(x, y, damag);
        if (monst == VAMPIRE)
                if (hitp[x][y] < 25) {
                        mitem[x][y] = BAT;
                        know[x][y] = 0;
                }
}

/*
 * hitm(x,y,amt)        Function to just hit a monster at a given coordinates
 *      int x,y,amt;
 *
 * Returns the number of hitpoints the monster absorbed
 * This routine is used to specifically damage a monster at a location (x,y)
 * Called by hitmonster(x,y)
 */
static int
hitm(int x, int y, int amt)
{
        int    monst;
        int    hpoints, amt2;
        vxy(&x, &y);            /* verify coordinates are within range */
        amt2 = amt;             /* save initial damage so we can return it */
        monst = mitem[x][y];
        if (c[HALFDAM])
                amt >>= 1;      /* if half damage curse adjust damage points */
        if (amt <= 0)
                amt2 = amt = 1;
        lasthx = x;
        lasthy = y;
        stealth[x][y] = 1;      /* make sure hitting monst breaks stealth
                                 * condition */
        c[HOLDMONST] = 0;       /* hit a monster breaks hold monster spell       */
        switch (monst) {        /* if a dragon and orb(s) of dragon slaying      */
        case WHITEDRAGON:
        case REDDRAGON:
        case GREENDRAGON:
        case BRONZEDRAGON:
        case PLATINUMDRAGON:
        case SILVERDRAGON:
                amt *= 1 + (c[SLAYING] << 1);
                break;
        }
        /* invincible monster fix is here */
        if (hitp[x][y] > monster[monst].hitpoints)
                hitp[x][y] = monster[monst].hitpoints;
        if ((hpoints = hitp[x][y]) <= amt) {
#ifdef EXTRA
                c[MONSTKILLED]++;
#endif
                lprintf("\nThe %s died!", lastmonst);
                raiseexperience((long) monster[monst].experience);
                amt = monster[monst].gold;
                if (amt > 0)
                        dropgold(rnd(amt) + amt);
                dropsomething(monst);
                disappear(x, y);
                bottomline();
                return (hpoints);
        }
        hitp[x][y] = hpoints - amt;
        return (amt2);
}

/*
 * hitplayer(x,y)       Function for the monster to hit the player from (x,y)
 *      int x,y;
 *
 * Function for the monster to hit the player with monster at location x,y
 * Returns nothing of value.
 */
void
hitplayer(int x, int y)
{
        int    dam, tmp, mster, bias;
        vxy(&x, &y);            /* verify coordinates are within range */
        lastnum = mster = mitem[x][y];
        /*
         * spirit nagas and poltergeists do nothing if scarab of negate
         * spirit
         */
        if (c[NEGATESPIRIT] || c[SPIRITPRO])
                if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
                        return;
        /* if undead and cube of undead control  */
        if (c[CUBEofUNDEAD] || c[UNDEADPRO])
                if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
                        return;
        if ((know[x][y] & 1) == 0) {
                know[x][y] = 1;
                show1cell(x, y);
        }
        bias = (c[HARDGAME]) + 1;
        hitflag = hit2flag = hit3flag = 1;
        yrepcount = 0;
        cursors();
        ifblind(x, y);
        if (c[INVISIBILITY])
                if (rnd(33) < 20) {
                        lprintf("\nThe %s misses wildly", lastmonst);
                        return;
                }
        if (c[CHARMCOUNT])
                if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
                        lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
                        return;
                }
        if (mster == BAT)
                dam = 1;
        else {
                dam = monster[mster].damage;
                dam += rnd((int) ((dam < 1) ? 1 : dam)) + monster[mster].level;
        }
        tmp = 0;
        if (monster[mster].attack > 0)
                if (((dam + bias + 8) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
                        if (spattack(monster[mster].attack, x, y)) {
                                flushall();
                                return;
                        }
                        tmp = 1;
                        bias -= 2;
                        cursors();
                }
        if (((dam + bias) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
                lprintf("\n  The %s hit you ", lastmonst);
                tmp = 1;
                if ((dam -= c[AC]) < 0)
                        dam = 0;
                if (dam > 0) {
                        losehp(dam);
                        bottomhp();
                        flushall();
                }
        }
        if (tmp == 0)
                lprintf("\n  The %s missed ", lastmonst);
}

/*
 * dropsomething(monst)         Function to create an object when a monster dies
 *      int monst;
 *
 * Function to create an object near the player when certain monsters are killed
 * Enter with the monster number
 * Returns nothing of value.
 */
static void
dropsomething(int monst)
{
        switch (monst) {
        case ORC:
        case NYMPH:
        case ELF:
        case TROGLODYTE:
        case TROLL:
        case ROTHE:
        case VIOLETFUNGI:
        case PLATINUMDRAGON:
        case GNOMEKING:
        case REDDRAGON:
                something(level);
                return;

        case LEPRECHAUN:
                if (rnd(101) >= 75)
                        creategem();
                if (rnd(5) == 1)
                        dropsomething(LEPRECHAUN);
                return;
        }
}

/*
 * dropgold(amount)     Function to drop some gold around player
 *      int amount;
 *
 * Enter with the number of gold pieces to drop
 * Returns nothing of value.
 */
void
dropgold(int amount)
{
        if (amount > 250)
                createitem(OMAXGOLD, amount / 100);
        else
                createitem(OGOLDPILE, amount);
}

/*
 * something(level)     Function to create a random item around player
 *      int level;
 *
 * Function to create an item from a designed probability around player
 * Enter with the cave level on which something is to be dropped
 * Returns nothing of value.
 */
void
something(int cavelevel)
{
        int    j;
        int             i;
        if (cavelevel < 0 || cavelevel > MAXLEVEL + MAXVLEVEL)
                return;         /* correct level? */
        if (rnd(101) < 8)
                something(cavelevel);   /* possibly more than one item */
        j = newobject(cavelevel, &i);
        createitem(j, i);
}

/*
 * newobject(lev,i)     Routine to return a randomly selected new object
 *      int lev,*i;
 *
 * Routine to return a randomly selected object to be created
 * Returns the object number created, and sets *i for its argument
 * Enter with the cave level and a pointer to the items arg
 */
static char     nobjtab[] = {
        0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
        OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
        OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
        OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
        OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
        OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
        OLONGSWORD};

int
newobject(int lev, int *i)
{
        int    tmp = 32, j;
        if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
                return (0);     /* correct level? */
        if (lev > 6)
                tmp = 37;
        else if (lev > 4)
                tmp = 35;
        j = nobjtab[tmp = rnd(tmp)];    /* the object type */
        switch (tmp) {
        case 1:
        case 2:
        case 3:
        case 4:
                *i = newscroll();
                break;
        case 5:
        case 6:
        case 7:
        case 8:
                *i = newpotion();
                break;
        case 9:
        case 10:
        case 11:
        case 12:
                *i = rnd((lev + 1) * 10) + lev * 10 + 10;
                break;
        case 13:
        case 14:
        case 15:
        case 16:
                *i = lev;
                break;
        case 17:
        case 18:
        case 19:
                if (!(*i = newdagger()))
                        return (0);
                break;
        case 20:
        case 21:
        case 22:
                if (!(*i = newleather()))
                        return (0);
                break;
        case 23:
        case 32:
        case 35:
                *i = rund(lev / 3 + 1);
                break;
        case 24:
        case 26:
                *i = rnd(lev / 4 + 1);
                break;
        case 25:
                *i = rund(lev / 4 + 1);
                break;
        case 27:
                *i = rnd(lev / 2 + 1);
                break;
        case 30:
        case 33:
                *i = rund(lev / 2 + 1);
                break;
        case 28:
                *i = rund(lev / 3 + 1);
                if (*i == 0)
                        return (0);
                break;
        case 29:
        case 31:
                *i = rund(lev / 2 + 1);
                if (*i == 0)
                        return (0);
                break;
        case 34:
                *i = newchain();
                break;
        case 36:
                *i = newplate();
                break;
        case 37:
                *i = newsword();
                break;
        }
        return (j);
}

/*
 *  spattack(atckno,xx,yy) Function to process special attacks from monsters
 *      int atckno,xx,yy;
 *
 * Enter with the special attack number, and the coordinates (xx,yy)
 *      of the monster that is special attacking
 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
 *
 * atckno   monster     effect
 * ---------------------------------------------------
 * 0    none
 * 1    rust monster    eat armor
 * 2    hell hound      breathe light fire
 * 3    dragon          breathe fire
 * 4    giant centipede weakening sing
 * 5    white dragon    cold breath
 * 6    wraith          drain level
 * 7    waterlord       water gusher
 * 8    leprechaun      steal gold
 * 9    disenchantress  disenchant weapon or armor
 * 10   ice lizard      hits with barbed tail
 * 11   umber hulk      confusion
 * 12   spirit naga     cast spells     taken from special attacks
 * 13   platinum dragon psionics
 * 14   nymph           steal objects
 * 15   bugbear         bite
 * 16   osequip         bite
 *
 * char rustarm[ARMORTYPES][2];
 * special array for maximum rust damage to armor from rustmonster
 * format is: { armor type , minimum attribute
 */
#define ARMORTYPES 6
static char     rustarm[ARMORTYPES][2] = {
        { OSTUDLEATHER, -2 },
        { ORING, -4 },
        { OCHAIN, -5 },
        { OSPLINT, -6 },
        { OPLATE, -8 },
        { OPLATEARMOR, -9}
};
static char     spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
static int
spattack(int x, int xx, int yy)
{
        int    i, j = 0, k, m;
        const char *p = NULL;

        if (c[CANCELLATION])
                return (0);
        vxy(&xx, &yy);          /* verify x & y coordinates */
        switch (x) {
        case 1:         /* rust your armor, j=1 when rusting has occurred */
                m = k = c[WEAR];
                if ((i = c[SHIELD]) != -1) {
                        if (--ivenarg[i] < -1)
                                ivenarg[i] = -1;
                        else
                                j = 1;
                }
                if ((j == 0) && (k != -1)) {
                        m = iven[k];
                        for (i = 0; i < ARMORTYPES; i++)
                                /* find his armor in table */
                                if (m == rustarm[i][0]) {
                                        if (--ivenarg[k] < rustarm[i][1])
                                                ivenarg[k] = rustarm[i][1];
                                        else
                                                j = 1;
                                        break;
                                }
                }
                if (j == 0)     /* if rusting did not occur */
                        switch (m) {
                        case OLEATHER:
                                p = "\nThe %s hit you -- You're lucky you have leather on";
                                break;
                        case OSSPLATE:
                                p = "\nThe %s hit you -- You're fortunate to have stainless steel armor!";
                                break;
                        }
                else {
                        beep();
                        p = "\nThe %s hit you -- your armor feels weaker";
                }
                break;

        case 2:
                i = rnd(15) + 8 - c[AC];
spout:          p = "\nThe %s breathes fire at you!";
                if (c[FIRERESISTANCE])
                        p = "\nThe %s's flame doesn't faze you!";
                else
spout2: if (p) {
                        lprintf(p, lastmonst);
                        beep();
                }
                checkloss(i);
                return (0);

        case 3:
                i = rnd(20) + 25 - c[AC];
                goto spout;

        case 4:
                if (c[STRENGTH] > 3) {
                        p = "\nThe %s stung you!  You feel weaker";
                        beep();
                        --c[STRENGTH];
                } else
                        p = "\nThe %s stung you!";
                break;

        case 5:
                p = "\nThe %s blasts you with his cold breath";
                i = rnd(15) + 18 - c[AC];
                goto spout2;

        case 6:
                lprintf("\nThe %s drains you of your life energy!", lastmonst);
                loselevel();
                beep();
                return (0);

        case 7:
                p = "\nThe %s got you with a gusher!";
                i = rnd(15) + 25 - c[AC];
                goto spout2;

        case 8:
                if (c[NOTHEFT])
                        return (0);     /* he has a device of no theft */
                if (c[GOLD]) {
                        p = "\nThe %s hit you -- Your purse feels lighter";
                        if (c[GOLD] > 32767)
                                c[GOLD] >>= 1;
                        else
                                c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
                        if (c[GOLD] < 0)
                                c[GOLD] = 0;
                } else
                        p = "\nThe %s couldn't find any gold to steal";
                lprintf(p, lastmonst);
                disappear(xx, yy);
                beep();
                bottomgold();
                return (1);

        case 9:
                for (j = 50;;) {/* disenchant */
                        i = rund(26);
                        m = iven[i];    /* randomly select item */
                        if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
                                if ((ivenarg[i] -= 3) < 0)
                                        ivenarg[i] = 0;
                                lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
                                srcount = 0;
                                beep();
                                show3(i);
                                bottomline();
                                return (0);
                        }
                        if (--j <= 0) {
                                p = "\nThe %s nearly misses";
                                break;
                        }
                        break;
                }
                break;

        case 10:
                p = "\nThe %s hit you with his barbed tail";
                i = rnd(25) - c[AC];
                goto spout2;

        case 11:
                p = "\nThe %s has confused you";
                beep();
                c[CONFUSE] += 10 + rnd(10);
                break;

        case 12:                /* performs any number of other special
                                 * attacks       */
                return (spattack(spsel[rund(10)], xx, yy));

        case 13:
                p = "\nThe %s flattens you with his psionics!";
                i = rnd(15) + 30 - c[AC];
                goto spout2;

        case 14:
                if (c[NOTHEFT])
                        return (0);     /* he has device of no theft */
                if (emptyhanded() == 1) {
                        p = "\nThe %s couldn't find anything to steal";
                        break;
                }
                lprintf("\nThe %s picks your pocket and takes:", lastmonst);
                beep();
                if (stealsomething() == 0)
                        lprcat("  nothing");
                disappear(xx, yy);
                bottomline();
                return (1);

        case 15:
                i = rnd(10) + 5 - c[AC];
spout3: p = "\nThe %s bit you!";
                goto spout2;

        case 16:
                i = rnd(15) + 10 - c[AC];
                goto spout3;
        };
        if (p) {
                lprintf(p, lastmonst);
                bottomline();
        }
        return (0);
}

/*
 * checkloss(x) Routine to subtract hp from user and flag bottomline display
 *      int x;
 *
 * Routine to subtract hitpoints from the user and flag the bottomline display
 * Enter with the number of hit points to lose
 * Note: if x > c[HP] this routine could kill the player!
 */
void
checkloss(int x)
{
        if (x > 0) {
                losehp(x);
                bottomhp();
        }
}

/*
 * annihilate()         Routine to annihilate all monsters around player (playerx,playery)
 *
 * Gives player experience, but no dropped objects
 * Returns the experience gained from all monsters killed
 */
int
annihilate(void)
{
        int             i, j;
        long   k;
        u_char  *p;
        for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
                for (j = playery - 1; j <= playery + 1; j++)
                        if (!vxy(&i, &j)) {     /* if not out of bounds */
                                if (*(p = &mitem[i][j])) {      /* if a monster there */
                                        if (*p < DEMONLORD + 2) {
                                                k += monster[*p].experience;
                                                *p = know[i][j] = 0;
                                        } else {
                                                lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
                                                hitp[i][j] = (hitp[i][j] >> 1) + 1;     /* lose half hit points */
                                        }
                                }
                        }
        if (k > 0) {
                lprcat("\nYou hear loud screams of agony!");
                raiseexperience((long) k);
        }
        return (k);
}

/*
 * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
 *      int x,y,dir,lifetime;
 *
 * Enter with the coordinates of the sphere in x,y
 *   the direction (0-8 diroffx format) in dir, and the lifespan of the
 *   sphere in lifetime (in turns)
 * Returns the number of spheres currently in existence
 */
int
newsphere(int x, int y, int dir, int life)
{
        int             m;
        struct sphere  *sp;
        if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
                return (c[SPHCAST]);    /* can't malloc, therefore failure */
        if (dir >= 9)
                dir = 0;        /* no movement if direction not found */
        if (level == 0)
                vxy(&x, &y);    /* don't go out of bounds */
        else {
                if (x < 1)
                        x = 1;
                if (x >= MAXX - 1)
                        x = MAXX - 2;
                if (y < 1)
                        y = 1;
                if (y >= MAXY - 1)
                        y = MAXY - 2;
        }
        if ((m = mitem[x][y]) >= DEMONLORD + 4) {       /* demons dispel spheres */
                know[x][y] = 1;
                show1cell(x, y);/* show the demon (ha ha) */
                cursors();
                lprintf("\nThe %s dispels the sphere!", monster[m].name);
                beep();
                rmsphere(x, y); /* remove any spheres that are here */
                free(sp);
                return (c[SPHCAST]);
        }
        if (m == DISENCHANTRESS) {      /* disenchantress cancels spheres */
                cursors();
                lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
                beep();
boom:           sphboom(x, y);  /* blow up stuff around sphere */
                rmsphere(x, y); /* remove any spheres that are here */
                free(sp);
                return (c[SPHCAST]);
        }
        if (c[CANCELLATION]) {  /* cancellation cancels spheres */
                cursors();
                lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
                beep();
                goto boom;
        }
        if (item[x][y] == OANNIHILATION) {      /* collision of spheres
                                                 * detonates spheres */
                cursors();
                lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
                beep();
                rmsphere(x, y);
                goto boom;
        }
        if (playerx == x && playery == y) {     /* collision of sphere and
                                                 * player! */
                cursors();
                lprcat("\nYou have been enveloped by the zone of nothingness!\n");
                beep();
                rmsphere(x, y); /* remove any spheres that are here */
                nap(4000);
                died(258);
        }
        item[x][y] = OANNIHILATION;
        mitem[x][y] = 0;
        know[x][y] = 1;
        show1cell(x, y);        /* show the new sphere */
        sp->x = x;
        sp->y = y;
        sp->lev = level;
        sp->dir = dir;
        sp->lifetime = life;
        sp->p = 0;
        if (spheres == 0)
                spheres = sp;   /* if first node in the sphere list */
        else {                  /* add sphere to beginning of linked list */
                sp->p = spheres;
                spheres = sp;
        }
        return (++c[SPHCAST]);  /* one more sphere in the world */
}

/*
 * rmsphere(x,y)                Function to delete a sphere of annihilation from list
 *      int x,y;
 *
 * Enter with the coordinates of the sphere (on current level)
 * Returns the number of spheres currently in existence
 */
int
rmsphere(int x, int y)
{
        struct sphere *sp, *sp2 = 0;
        for (sp = spheres; sp; sp2 = sp, sp = sp->p)
                if (level == sp->lev)   /* is sphere on this level? */
                        if ((x == sp->x) && (y == sp->y)) {     /* locate sphere at this
                                                                 * location */
                                item[x][y] = mitem[x][y] = 0;
                                know[x][y] = 1;
                                show1cell(x, y);        /* show the now missing
                                                         * sphere */
                                --c[SPHCAST];
                                if (sp == spheres) {
                                        sp2 = sp;
                                        spheres = sp->p;
                                        free((char *) sp2);
                                } else {
                                        if (sp2)
                                                sp2->p = sp->p;
                                        free((char *) sp);
                                }
                                break;
                        }
        return (c[SPHCAST]);    /* return number of spheres in the world */
}

/*
 * sphboom(x,y) Function to perform the effects of a sphere detonation
 *      int x,y;
 *
 * Enter with the coordinates of the blast, Returns no value
 */
static void
sphboom(int x, int y)
{
        int    i, j;
        if (c[HOLDMONST])
                c[HOLDMONST] = 1;
        if (c[CANCELLATION])
                c[CANCELLATION] = 1;
        for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
                for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
                        item[j][i] = mitem[j][i] = 0;
                        show1cell(j, i);
                        if (playerx == j && playery == i) {
                                cursors();
                                beep();
                                lprcat("\nYou were too close to the sphere!");
                                nap(3000);
                                died(283);      /* player killed in explosion */
                        }
                }
}

/*
 * genmonst()           Function to ask for monster and genocide from game
 *
 * This is done by setting a flag in the monster[] structure
 */
static void
genmonst(void)
{
        int    i, j;
        cursors();
        lprcat("\nGenocide what monster? ");
        for (i = 0; (!isalpha(i)) && (i != ' '); i = ttgetch());
        lprc(i);
        for (j = 0; j < MAXMONST; j++)  /* search for the monster type */
                if (monstnamelist[j] == i) {    /* have we found it? */
                        monster[j].genocided = 1;       /* genocided from game */
                        lprintf("  There will be no more %s's", monster[j].name);
                        /* now wipe out monsters on this level */
                        newcavelevel(level);
                        draws(0, MAXX, 0, MAXY);
                        bot_linex();
                        return;
                }
        lprcat("  You sense failure!");
}