root/games/larn/monster.c
/*
 *      monster.c               Larn is copyrighted 1986 by Noah Morgan.
 * $FreeBSD: src/games/larn/monster.c,v 1.6 1999/11/16 11:47:40 marcel Exp $
 *
 *      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 "header.h"

/* used for altar reality */
struct isave {
        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 long 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 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(%d)\n", (long)mon);
                nap(3000);
                return;
        }
        while (mon < MAXMONST && monster[mon].genocided)        /* genocided? */
                mon++;
        for (k = rnd(8), i = -8; i < 0; i++, k++) {     /* choose direction, then try all */
                if (k > 8)      /* wraparound the diroff arrays */
                        k = 1;
                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 itm, int monst)
{
        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 (itm == 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)       /* no such object */
                return;
        for (k = rnd(8), i = -8; i < 0; i++, k++) {     /* choose direction, then try all */
                if (k > 8)      /* wraparound the diroff arrays */
                        k = 1;
                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 const 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 = getchr()) == 'D') {
                seemagic(-1);
                cursors();
                lprcat(eys);
        }
        if (a == '\33') /* to escape casting a spell */
                goto over;
        if ((b = getchr()) == '\33')    /* to escape casting a spell */
                goto over;
        if ((d = getchr()) == '\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;
        char *p, *kn, *pm;
        const char *cp;

        if (x >= SPNUM) /* no such spell */
                return;
        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:         /* protection field +2 */
                if (c[PROTECTIONTIME] == 0)
                        c[MOREDEFENSES] += 2;
                c[PROTECTIONTIME] += 250;
                return;

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

                return;

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

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

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

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

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

        case 6:         /* web */
                i = rnd(3) + 2;
                cp = "  While the %s is entangled, you hit %d times";
                goto ws;

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

        case 8:         /* enlightenment */
                yl = playery - 5;
                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:         /* healing */
                raisehp(20 + (clev << 1));
                return;

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

        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:        /* fireball */
                godirect(x, rnd(25 + clev) + 25 + clev, "  The fireball hits the %s", 40, '*');
                return;

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

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

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

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

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

        case 20:        /* vaporize rock */
                xh = min(playerx + 1, MAXX - 2);
                yh = min(playery + 1, MAXY - 2);
                for (i = max(playerx - 1, 1); i <= xh; i++)
                        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:        /* dehydration */
                direct(x, 100 + clev, "  The %s shrivels up", 0);
                return;

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

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

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

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

        case 26:        /* finger of death */
                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);
                else
                        lprcat("  It didn't work");
                return;

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

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

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

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

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

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

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

        case 32:        /* sphere of annihilation */
                if ((rnd(23) == 5) && (wizard == 0)) {
                        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:        /* genocide */
                genmonst();
                spelknow[33] = 0;
                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 = 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(save);
                positionplayer();
                return;
        }

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

        default:
                lprintf("  spell %d 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 long
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)  /* fullhits are out of range */
                return (0);
        if (c[LANCEDEATH])      /* lance of death */
                return (10000);
        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 == NULL) /* bad arguments */
                return;
        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 (thats 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, char cshow)
{
        char *p;
        int x, y, m;
        int dx, dy;
        if (spnum < 0 || spnum >= SPNUM || str == NULL || delay < 0)    /* bad args */
                return;
        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 my 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)        /* bad args */
                return;
        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 == NULL) /* bad args */
                return;
        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 (getchr()) {
                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++;
                        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)        /* bad args */
                return;
        if (isconfuse())        /* if he is confused, he can't aim his magic */
                return;
        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])        /* not if time stopped */
                return;
        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)
 */
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])         /* if half damage curse adjust damage points */
                amt >>= 1;
        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 naga's and poltergeist's 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(lvl)  Function to create a random item around player
 *              int lvl;
 *
 *      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 lvl)
{
        int j;
        int i;
        if (lvl < 0 || lvl > MAXLEVEL + MAXVLEVEL)      /* correct level? */
                return;
        if (rnd(101) < 8)       /* possibly more than one item */
                something(lvl);
        j = newobject(lvl, &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)  /* correct level? */
                return (0);
        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 -- Your lucky you have leather on";
                                break;
                        case OSSPLATE:
                                p = "\nThe %s hit you -- Your 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 phase 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])         /* he has a device of no theft */
                        return (0);
                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])         /* he has device of no theft */
                        return (0);
                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
 */
long
annihilate(void)
{
        int i, j;
        long k;
        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[(int)*p].experience;
                                                *p = know[i][j] = 0;
                                        } else {
                                                lprintf("\nThe %s barely escapes being annihilated!", monster[(int)*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
 */
long
newsphere(int x, int y, int dir, int life)
{
        int m;
        struct sphere *sp;
        if (((sp = malloc(sizeof(struct sphere)))) == NULL)
                return (c[SPHCAST]);    /* can't malloc, therefore failure */
        if (dir >= 9)           /* no movement if direction not found */
                dir = 0;
        if (level == 0)         /* don't go out of bounds */
                vxy(&x, &y);
        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 */
                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 */
                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)       /* if first node in the sphere list */
                spheres = sp;
        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
 */
long
rmsphere(int x, int y)
{
        struct sphere *sp, *sp2 = NULL;
        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(sp2);
                                } else {
                                        sp2->p = sp->p;
                                        free(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 = getchr())
                ;
        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!");
}