/*	$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!");
}
