/*
 *	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 $
 * $DragonFly: src/games/larn/monster.c,v 1.4 2006/08/26 17:05:05 pavalos 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"

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 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 (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 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 */
		if (itm==0 || (item[x][y]==0))	/* is it free of items? */
		  if (monst==0 || (mitem[x][y]==0))	/* is it free of monsters? */
		    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 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') goto over; /*	to escape casting a spell	*/
	if ((b=getchr())=='\33') goto over; /*	to escape casting a spell	*/
	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) 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: i=rnd(3)+1;
				cp="  While the %s slept, you smashed it %d times";
			ws:	direct(x,fullhit(i),cp,i); /*	sleep	*/	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: i=rnd(3)+2;	cp="  While the %s is entangled, you hit %d times";
				goto ws; /* web */

		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 */ 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) 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 (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==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 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) 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]))
				{
				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++;
			case 'n':	i++;
			case 'y':	i++;
			case 'u':	i++;
			case 'h':	i++;
			case 'k':	i++;
			case 'l':	i++;
			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) 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();
	if ((rnd(20) < tmp-c[HARDGAME]) || (rnd(71) < 5)) /* need at least random chance to hit */
		{
		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]) 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 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) return;	/* correct level? */
	if (rnd(101)<8) something(lvl); /* possibly more than one item */
	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) 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++)
					if (m == rustarm[i][0]) /* find his armor in table */
						{
						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]) 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
 */
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=(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 */
		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) 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
 */
long
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
				{ 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=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!");
	}

