/*
 *	monster.c
 *
 *	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;
 *
 *      enlighten(int dyl, dyh, dxl, dyh) Enlighten area around player
 *
 *	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;
 *
 *			Routine to direct spell damage 1 square in 1 dir
 *	direct(spnum,dam,str,arg)	
 *		int spnum,dam,arg;
 *		char *str;
 *
 *			Function to perform missile attacks
 *	godirect(spnum,dam,str,delay,cshow)		
 *		int spnum,dam,delay;
 *		char *str,cshow;
 *
 *		  Routine to put "monster" or the monster name into lastmosnt
 *	ifblind(x,y)	
 *		int x,y;
 *
 *	tdirect(spnum)		Routine to teleport away a monster
 *		int spnum;
 *
 *		  	Routine to damage all monsters 1 square from player
 *	omnidirect(sp,dam,str)  
 *		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;
 *
 *		   Routine to subtract hp from user and flag bottomline display
 *	checkloss(x) 	
 *		int x;
 *
 *		   Routine to annihilate monsters around player, playerx,playery
 *	annihilate()   
 *
 *		  Function to create a new sphere of annihilation
 *	newsphere(x,y,dir,lifetime)  
 *		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 */
};

/*
 *		Function to create a monster next to the player
 *	createmonster(monstno) 		
 *		int monstno;
 *
 *	Enter with the monster number (1 to MAXMONST+8)
 *	Returns no value.
 */
createmonster(mon)
int mon;
{
  register int x,y,k,i;
  if (mon<1 || mon>MAXMONST+8)	{
    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
 */
cgood(x,y,itm,monst)
     register int x,y;
     int itm,monst;
{
  if ((y>=0) && (y<=MAXY-1) 
      && (x>=0) && (x<=MAXX-1)) 		/* within bounds? */
    if (item[x][y]!=OWALL)		/* can't make 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.
 */
createitem(it,arg)
     int it;
     long arg;
{
  register int x,y,k,i;
  
  if (it >= MAXOBJ) 
    return;	/* no such object */
  
  for (k=rnd(8), i= -8; i<0; i++,k++) { /* choose direction, try all */
    if (k>8) k=1;		/* wraparound the diroff arrays */
    x = playerx + diroffx[k];
    y = playery + diroffy[k];
    if ( (it != OMAXGOLD && it != OGOLDPILE) ) {
      if (cgood(x,y,1,0)) {	/* if we can create here */
	item[x][y] = it;  
	know[x][y]=0;  
	iarg[x][y]=arg;  
	return;
      }
    }
    else {
      int i;
      switch (item[x][y]) {
      case OGOLDPILE :
	if ( (iarg[x][y] + arg) < 32767) {
	  iarg[x][y] += arg;
	  return;
	}
      case ODGOLD :
	if ( (10L * iarg[x][y] + arg) < 327670L) {
	  i = iarg[x][y] ;
	  iarg[x][y] = (10L * i + arg) / 10;
	  item[x][y] = ODGOLD;
	  return;
	}
      case OMAXGOLD :
	if ( (100L * iarg[x][y] + arg) < 3276700L) {
	  i = ((100L * iarg[x][y]) + arg)/100;
	  iarg[x][y] = i;
	  item[x][y] = OMAXGOLD;
	  return;
	}
      case OKGOLD :
	if ( (1000L * iarg[x][y] + arg) < 32767000L) { 
	  i = iarg[x][y];
	  iarg[x][y] = (1000L * i + arg) / 1000;
	  item[x][y] = OKGOLD;
	  return;
	}
	else iarg[x][y] = 32767000L; 
	return;
	default :
	  if (cgood(x,y,1,0)) {
	    item[x][y] = it;  
	    know[x][y]=0;  
	    if (it == OMAXGOLD)
	      iarg[x][y]= arg / 100;  
	    else iarg[x][y]=arg;  
	    return;
	  }
      } /* end switch */
    } /* end else */
  } /* end for */
}

/*
 *	cast() 		Subroutine called by parse to cast a spell for the user
 *
 *	No arguments and no return value.
 */
static char eys[] = "\nEnter your spell: ";

cast()
{
  register 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=getcharacter())=='D') { 	
    seemagic(-1); 
    cursors();  
    lprcat(eys); 
  }
  if (a=='\33') goto over; 	/* to escape casting a spell	*/
  if ((b=getcharacter())=='\33') goto over; /*to escape casting a spell */
  if ((d=getcharacter())=='\33') { 
  over: 
    lprcat(aborted); 
    c[SPELLS]++; 
    return; 
  } 			/* to escape casting a spell */
  /*seq search for his spell, hash?*/
  for (lprc('\n'),i=0; i<SPNUM; i++) 
    if ((spelcode[i][0]==a) && (spelcode[i][1]==b) && (spelcode[i][2]==d))
      if (spelknow[i]) {  	
	speldamage(i);   
	i=SPNUM;
      }
  
  if (i == SPNUM) 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
 */
speldamage(x)
     int x;
{
  register int i,j,clev;
  int xl,xh,yl,yh;
  register 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 */
  
  if ((rnd(23)==7) || (rnd(18) > c[INTELLIGENCE]))
  { 	
    lprcat("  It didn't work!");  
    return; 
  }
  
  clev = c[LEVEL];
  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;   
	  break;

  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;
	  p="  While the %s slept, you smashed it %d times";
ws:			
	  direct(x,fullhit(i),p,i); /*	sleep	*/
	  return;

  case 4:	/* charm monster */

	  c[CHARMCOUNT] += c[CHARISMA]<<1;	
	  return;

		/* sonic spear */
  case 5:	
	  godirect(x,rnd(10)+15+clev,"  The sound damages the %s",70,'@'); 
	  return;
	  
	  /* ----- LEVEL 2 SPELLS ----- */
	  
  case 6: 
	  i=rnd(3)+2;	
	  p="  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:	
          enlighten(-5, +6, -15, +16); /* enlightenment */
	  return;
	  
  case 9:	
	  raisehp(20+(clev<<1));  
	  return;  /* healing */
	  
  case 10:	
	  c[BLINDCOUNT]=0;	
	  return;	/* cure blindness	*/
	  
  case 11:	
	  if (wizard) {
	    lprintf("Number: ");
	    if ((i = readnum((long)-1)) != -1) {
	      createmonster(i);
	      return;
	    }
	  }
	  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;
	  
	  /* if he has the amulet of invisibility then add more time */
  case 13:
	  j = 0;
	  if ( (i = has_object(OAMULET)) >= 0 ) 
	    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');
	  return; 	/*	cold */
	  
  case 16:	
	  dirpoly(x);  
	  return;	/*	polymorph */
	  
  case 17:	
	  c[CANCELLATION]+= 5+clev;	
	  return;	/*	cancellation	*/
	  
  case 18:	
	  c[HASTESELF]+= 7+clev;  
	  return;  /*	haste self	*/
	  
	  /* cloud kill */
  case 19:    
	  omnidirect(x,30+rnd(10),"  The %s gasps for air");	
	  return;
	  
	  /* vaporize rock */
  case 20:	
	  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-3)
			*p = *kn = 0;
		      break;

	      case OSTATUE:
		      if (c[HARDGAME]>3 && rnd(60)<30)
			break;
		      *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; 
		      createmonster(DEMONPRINCE);
		      createmonster(DEMONPRINCE);
		      createmonster(DEMONPRINCE);
		      createmonster(DEMONPRINCE);
		      break;
	      };
	      switch(*pm)
	      {	/* Xorn takes damage from vpr */
	      case XORN:	
		     ifblind(i,j);  
		     hitm(i,j,200); 
		     break; 
	      case TROLL:
		    ifblind(i,j);  
		     hitm(i,j,200); 
		     break; 
	      }
	    }
	  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:	
	  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:			/* finger of death */
	  if (rnd(151)==63) { 
	    beep(); 
	    lprcat("\nYour heart stopped!\n"); 
	    nap(4000);  
	    died(270); 
	  }else 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 ----- */

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

	  /* if have HANDofFEAR make last longer */
	  if ( has_object(OHANDofFEAR) >= 0 ) {
	    c[SCAREMONST] *= 3;
	  }
	  return;

  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 */

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

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

  case 32:	/* make wall */
	  makewall(x);
	  return;

		/* sphere of annihilation */
  case 33:	
	  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 34:	
	  genmonst();  
	  spelknow[34]=0;  /* genocide */
	  loseint();
	  return;

  case 35:/* summon demon */
	  if (rnd(100) > 30) { 
	    direct(x-1,150,"  The demon strikes at the %s",0);  
	  }else if (rnd(100) > 15) { 
	    lprcat("  Nothing seems to have happened");  
	  }else{
	    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 36:/* walk through walls */
	  c[WTW] += rnd(10)+5;	
	  return;

  case 37:/* 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++)/* save all items and monsters */
	      for (i=0; i<MAXX; i++)
	      {
		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[37]=0;
	    free((char*)save);	 
	    positionplayer();  
	    return;
	  }

  case 38:	/* permanence */

	  adjtime(-99999L);  
	  spelknow[38]=0; /* forget */
	  loseint();
	  break;

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

/*
 *     enlighten the player. dyl/dxl negative.
 *
 */

enlighten(dyl, dyh, dxl, dxh)
int dyl, dyh, dxl, dxh;
{
int yl, yh, xl, xh, i, j;

     yl = playery+dyl;     /* enlightenment */
     yh = playery+dyh;   
     xl = playerx+dxl;   
     xh = playerx+dxh;
     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);	
}

/*
 *	loseint()		
 *		Routine to subtract 1 from your int (intelligence) if > 3
 *
 *	No arguments and no return value
 */
loseint()
{
	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
 */
isconfuse()
{
	if (c[CONFUSE]) { 
	  lprcat(" You can't aim your magic!"); 
	  beep(); 
	}
	return(c[CONFUSE]);
}

/*		Routine to return 1 if a spell doesn't affect a monster
 *	nospell(x,monst)	
 *	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.
 */
nospell(x,monst)
int x,monst;
{
	register int tmp;

	/* bad spell or monst */
	if (x>=SPNUM || monst>MAXMONST+8 || monst<0 || x<0) return(0);	

	if ((tmp=spelweird[monst-1][x])==0) 
	  return(0);
	cursors();  
	lprc('\n');  
	lprintf(spelmes[tmp],monster[monst].name);  
	return(1);
}

/*
 *		Function to return full damage against a monster (aka web)
 *	fullhit(xx)		
 *		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
 */
fullhit(xx)
int xx;
{
	register 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 );
}

/*
 *		Routine to direct spell damage 1 square in 1 dir
 *	direct(spnum,dam,str,arg)	
 *	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.
 */
direct(spnum,dam,str,arg)
int spnum,dam,arg;
char *str;
{
  int x,y;
  register 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);
}

/*
 *				Function to perform missile attacks
 *	godirect(spnum,dam,str,delay,cshow)		
 *		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.
 *      (Erstunken und erlogen - globale Vaiablen! ws, 12.8.93)
 */
godirect(spnum,dam,str,delay,cshow)
     int spnum,dam,delay;
     char *str,cshow;
{
  register char *p;
  register 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])!=0)	/* is there a monster there? */
    {
      ifblind(x,y);
      if (m == LUCIFER || (m>=DEMONLORD && rnd(100)<10) ) {
	dx *= -1;	
	dy *= -1;	
	cursors();
	lprc('\n');
	lprintf("\nthe %s returns your puny missile!", monster[m].name);
      } 
      else {
	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 OLRS:	
            cursors(); 
	    lprc('\n'); 
	    lprintf(str,objectname[OLRS]);
	    if (dam < 60+c[HARDGAME]){     /* not enough damage? */
	      dam = 0;                     /* absorb */
	    }else{
	      beep();
	      cursors(); 
	      item[x][y] = 0;
	      show1cell(x, y);
	      nap(1200);
	      item[x][y] = OLRS;
	      show1cell(x, y);
	      beep();
	      cursors(); 
	      lprcat("\nYou curse: \"DEATH, TAXES, and SEGMENTATION FAULT!\"");
	      addtax(45054L, TRUE);
	      nap(1200);
	      dam *= 2;	
	      dx  *= -1;	
	      dy  *= -1;	
	    };
	    break;

    case OWALL:	
            cursors(); 
	    lprc('\n'); 
	    lprintf(str,"wall");
	    if (dam>=50+c[HARDGAME]) /* enough damage? */
	      if (level<MAXLEVEL+MAXVLEVEL-3) /* 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 (dam>44) {
	      if (c[HARDGAME] > 3 && rnd(60)<30)
		goto god2;
	      lprcat("  The statue crumbles");
	      *p=OBOOK; 
	      iarg[x][y]=level;
	      goto god;
	    }
	    goto god2;
	    
    case OTHRONE:	
	    cursors(); 
	    lprc('\n'); 
	    lprintf(str,"throne");
	    if (dam>33) {
	      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);
  }
}

/*
 *		Routine to put "monster" or the monster name into lastmosnt
 *	ifblind(x,y)	
 *	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.
 */
ifblind(x,y)
int x,y;
{
	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.
 */
tdirect(spnum)
int spnum;
{
	int x,y;
	register 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;
      }


makewall(spnum)
     int spnum;
{
  int x,y;
  
  if (spnum<0 || spnum>=SPNUM) return; /* bad args */
  if (isconfuse()) return;
  dirsub(&x,&y);
  
  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 (item[x][y]==0){	/* is it free of items? */
	if (mitem[x][y]==0){  /*is it free of monsters? */
	  if ((level!=1) || (x!=33) || (y!=MAXY-1)) {
	    item[x][y]=OWALL;
	    know[x][y]=1;
	    show1cell(x,y);
	  }
	  else lprcat("\nyou can't make a wall there!");
	}
	else lprcat("\nthere's a monster there!");
      }
      else lprcat("\nthere's something there already!");
    }
    else lprcat("\nthere's a wall there already!");
  
}

/*
 *	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 each square in dam,
 *	  and the lprintf string to identify the spell in str.
 *	Returns no value.
 */
omnidirect(spnum,dam,str)
     int spnum,dam;
     char *str;
{
  register 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; 
	}
    }
}

/*
 *		Routine to ask for direction, then modify x,y for it
 *	static dirsub(x,y)		
 *	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 char* arr_dir= {"jlkhuynb"};

dirsub(x,y)
     int *x,*y;
{
  char *cinx;
  register int i;
  
  lprcat("\nIn What Direction? ");
  i = 0;
  do {
    cinx = index(arr_dir, getcharacter());
    if( cinx ) i = cinx-arr_dir +1;
  } while ( i == 0 );
  *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.
 */
vxy(x,y)
     int *x,*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.
 */
dirpoly(spnum)
     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 */
}

/*
 *		Function to hit a monster at the designated coordinates
 *	hitmonster(x,y) 	
 *	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.
 */
hitmonster(x,y)
     int x,y;
{
  register int tmp,monst,damag,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);
  
  /* this couldn't have been right, could it? */
  /*tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + c[WCLASS/4] -12*/
  tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + c[WCLASS]/4 -12
    - c[HARDGAME];
  
  cursors();
  
  /* need at least random chance to hit */
  if ((rnd(20) < tmp) || (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);
  /*	lprintf(" (tmp = %d)", tmp); */
  
  if (flag)	/* if the monster was hit */
    if (iven[c[WIELD]] != OSWORDofSLASHING)
      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 >= DEMONLORD) && 
	(c[LANCEDEATH]) && 
	(hitp[x][y]) )
      lprintf("\nYour lance of death tickles the %s!",lastmonst);
  }

#if 0 /* it's just too messy. ws 16.11.93 */ 
  if (monst == VAMPIRE) 
    if (hitp[x][y]<25)
    {	mitem[x][y]=LEMMING; 
	know[x][y]=0; 
      }
#endif
  
  if (monst == METAMORPH)
    if (hitp[x][y]<25 && hitp[x][y] > 0)
      mitem[x][y]=BRONZEDRAGON+rund(9);
  
  if (mitem[x][y]==LEMMING)
    if (rnd(100)<=40) createmonster(LEMMING);
}

/*
 *		Function to just hit a monster at a given coordinates
 *	hitm(x,y,amt)
 *		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)
 */
hitm(x,y,amt)
     int x,y;
     register amt;
{
  register 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 half damage curse adjust damage points */
  if (c[HALFDAM]) amt >>= 1;	
  if (amt<=0) amt2 = amt = 1;
  
  lasthx=x;
  lasthy=y;
  
  /* make sure hitting monst breaks stealth condition */
  stealth[x][y]=1;	
  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:
                if (c[SLAYING])
			amt *= 3;
		break;
  }
  /* invincible monster fix is here */
  if (hitp[x][y] > monster[monst].hitpoints)
    hitp[x][y] = monster[monst].hitpoints;

  if (monst >= DEMONLORD) {
    if (c[LANCEDEATH]) 
      amt=300;
    if (iven[c[WIELD]] == OSLAYER)
      amt=10000;
  }
  
  if ((hpoints = hitp[x][y]) <= amt) {
    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();
    hitp[x][y] = 0;
    return(hpoints);
  }
  hitp[x][y] = hpoints-amt;	
  return(amt2);
}

/*
 *			Function for the monster to hit the player from (x,y)
 *	hitplayer(x,y) 		
 *		int x,y;
 *
 *	Function for the monster to hit the player with monster at location x,y
 *	Returns nothing of value.
 */
hitplayer(x,y)
int x,y;
{
	register int dam,tmp,mster,bias;

	vxy(&x,&y);	/* verify coordinates are within range */

	lastnum = mster = mitem[x][y];

	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 (mster < DEMONLORD)
	  if ( (c[INVISIBILITY]) && (rnd(33)<20) ) {
	    lprintf("\nThe %s misses wildly",lastmonst);	
	    return;
	  }

	if ( (mster < DEMONLORD) && (mster != PLATINUMDRAGON) && (c[CHARMCOUNT]) ) 
	  if (rnd(30)+5*monster[mster].level-c[CHARISMA]<30) {
	    lprintf("\nThe %s is awestruck at your magnificence!",lastmonst);
	    return;
	  }

	if (mster==LEMMING) return;
	else {
	  dam = monster[mster].damage;
	  dam += rnd( (int) ((dam<1)?1:dam) ) + monster[mster].level;
	}
	
	/* demon lords/prince/god of hellfire damage is reduced if wielding
	   Slayer */
	if ( (mster >= DEMONLORD) && (iven[c[WIELD]]==OSLAYER) )
	  dam=(int) (1 - (0.1 * rnd(5)) * dam);
	
	/*	spirit naga's and poltergeist's damage is halved if scarab of 
	  	negate spirit	*/
	if (c[NEGATESPIRIT] || c[SPIRITPRO])  
	  if ((mster ==POLTERGEIST) || (mster ==SPIRITNAGA)) 
	    dam = (int) dam/2; 
	
	/*	halved if undead and cube of undead control	*/
	if (c[CUBEofUNDEAD] || c[UNDEADPRO]) 
	  if ((mster ==VAMPIRE) || (mster ==WRAITH) || (mster ==ZOMBIE)) 
	    dam = (int) dam/2;
	
	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.
 */
dropsomething(monst)
     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); 
		break;

	case LEPRECHAUN: 
		if (rnd(101)>=75) creategem();
		if (rnd(5)==1) dropsomething(LEPRECHAUN);   
		break;
	case LEMMING:
/*		createitem(OGOLDPILE,1);
*/		break;
	}
}

/*
 *	dropgold(amount) 	Function to drop some gold around player
 *		int amount;
 *
 *	Enter with the number of gold pieces to drop
 *	Returns nothing of value.
 */
dropgold(amount)
register int amount;
{
	if (amount > 250) createitem(OMAXGOLD, (long)amount);  
	else  createitem(OGOLDPILE,(long)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.
 */
something(lev)
int lev;
{
	register int j;
	int i;

	if (lev<0 || lev>MAXLEVEL+MAXVLEVEL) 
	  return;	/* correct level? */
	if (rnd(101)<8) 
	  something(lev); /* possibly more than one item */
	j = newobject(lev,&i);		
	createitem(j,(long)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 };

newobject(lev,i)
register int lev,*i;
{
	register 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())==0)  return(0);  
		break;
	case 20: 
	case 21: 
	case 22: 
		if ((*i=newleather())==0)  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 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 30: 
	case 33: 
		*i=rund(lev/2+1);   
		break;
	case 34: 
		*i=newchain();   	
		break;
	case 36: 
		*i=newplate();   	
		break;
	case 37: 
		*i=newsword();		
		break; 
	}
	return(j);
}

/*
 *			Function to process special attacks from monsters
 *  spattack(atckno,xx,yy) 	
 *  	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 };
spattack(x,xx,yy)
int x,xx,yy;
{
	register int i,j=0,k,m;
	register char *p=0;

	vxy(&xx,&yy);	/* verify x & y coordinates */

	/* cancel only works 5% of time for demon prince
		   and god */
	if (c[CANCELLATION]) 
	  if (mitem[xx][yy] >= DEMONPRINCE) {
	    if (rnd(100) >= 95) return(0);
	  }
	  else return(0);
	
	/* staff of power cancels demonlords/wraiths/vampires 75% of time */
	if (mitem[xx][yy] != LUCIFER) {
	  if ( (mitem[xx][yy] >= DEMONLORD) ||
	      (mitem[xx][yy] == WRAITH) ||
	      (mitem[xx][yy] == VAMPIRE) )
	    if ( (has_object(OPSTAFF) >= 0) && (rnd(100) < 75) )
	      return(0);
	}
	
	/* if have cube of undead control,  wraiths and vampires do nothing */	
	if ( (mitem[xx][yy]==WRAITH) || (mitem[xx][yy]==VAMPIRE) )
	  if ( (c[CUBEofUNDEAD]) || (c[UNDEADPRO]) )
	    return(0);
	
	switch(x) {
	case 1:	/* rust your armor, j=1 when rusting has occurred */
		m = k = c[WEAR];
		if ((i=c[SHIELD]) != -1) {
		  --ivenarg[i];  /* avoid TC bug again... */
		  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 */
		    {
		      --ivenarg[k];
		      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 are lucky you have leather on";
			  break;
		  case OSSPLATE:	
		          p = "\nThe %s hit you -- You are fortunate to have stainless steel armor!";
			  break;
		  case OELVENCHAIN:
			  p = "\nThe %s hit you -- You are very lucky to have such strong elven chain!";
			  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();
		  if (--c[STRENGTH] < 3)
		    c[STRENGTH] = 3;
		}
		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();
		if (mitem[xx][yy]==DEMONPRINCE) losemspells(1);
		if (mitem[xx][yy]==LUCIFER) {
		  loselevel();
		  losemspells(2);
		  for (i=0;i<=5;i++) 
		    if (c[i]-- < 3) c[i]=3;
		}
		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(NINVT);  
		  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 with a spell of disenchantment! ",lastmonst);
		    srcount=0; 
		    beep(); 
		    (void) show3(i, 0);  
		    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);
}

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

/*
 *	Routine to annihilate all monsters around player (playerx,playery)
 *	annihilate() 	
 *
 *	Gives player experience, but no dropped objects
 *	Returns the experience gained from all monsters killed
 */
annihilate()
{
	int i,j;
	register long k;
	register 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) {
		  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);
}

/*
 *		Function to create a new sphere of annihilation
 *	newsphere(x,y,dir,lifetime)  
 *		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)
 */
newsphere(x,y,dir,life)
int x,y,dir,life;
{
	int m, i, kept;
	struct sphere *sp;

	if ( ((sp=(struct sphere *)malloc(sizeof(struct sphere)))) ){
	  if (dir>=9) dir=0;         	/* no movement if direction not found */
	  if (level==0){        	/* don't go out of bounds */
	    vxy(&x,&y);
	  } else {
	    x = min( max (x, 1), MAXX-2);
	    y = min( max (y, 1), MAXY-2);
	  };

	  kept = FALSE;
	  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();
	    rmsp(sp, x, y);
	    rmsphere(x,y);          	/* remove other sphere */
	    sphboom(x,y); 	        /* blow up stuff around sphere */
	  }else if (item[x][y] == OLRS ){
	    beep();
	    cursors(); 
	    rmsp(sp, x, y);
	    nap(2200);
	    item[x][y] = OLRS;
	    show1cell(x, y);
	    beep();
	    cursors(); 
	    lprcat("\nYou curse: \"DEATH and TAXES!\""); 
	    addtax(19930813L, TRUE);
	  }else if ( has_object(OSPHTALISMAN) >= 0 ) {
	    kept = TRUE;
	  }else if ((m=mitem[x][y]) >= DEMONLORD) {  	    /* demons dispel spheres */
	    i = hitp[x][y];
	    know[x][y]=1; 
	    show1cell(x,y);	            /* show the demon (ha ha) */
	    cursors(); 
	    lprintf("\nThe %s dispels the sphere!",monster[m].name);
	    beep(); 
	    rmsp(sp, x, y);
	    mitem[x][y] = m;
	    hitp[x][y] = i;
	    know[x][y]=0; 
	  }else if (m==DISENCHANTRESS) {   /* disenchantress cancels spheres */
	    cursors(); 
	    lprintf("\nThe %s causes cancellation of the sphere!",monster[m].name); 
	    beep();
	    rmsp(sp, x,y);                    /* remove sphere */
	    sphboom(x,y);	              /* blow up stuff around sphere */
	  }else if (c[CANCELLATION]) {    /* cancellation cancels spheres */
	    cursors();
	    lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); 
	    beep();
	    rmsp(sp, x,y);                 /* remove sphere */
	    sphboom(x,y);	               /* blow up stuff around sphere */
	  }else if (playerx==x && playery==y) {  /* collision of sphere and player! */
	    cursors();
	    lprcat("\nYou have been enveloped by the zone of nothingness!\n");
	    beep(); 
	    rmsp(sp, x,y);         	/* remove sphere */
	    nap(4000);  
	    died(258);
	  }else{
	    kept = TRUE;
	  };
	  if ( kept ){
	    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;
	    };
	    ++c[SPHCAST];
	  };
	};
}

/*
 *	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)
 */
rmsphere(x,y)
int x,y;
{
	register struct sphere *sp,*sp2;

	for (sp=spheres, sp2=0; sp; sp=sp->p){
	  if (   (level==sp->lev)       	/* is sphere on this level? */
	      && ((x==sp->x) && (y==sp->y)) )
	      /* locate sphere at this location */
	  {
	    if (sp==spheres) { /* delete from list */
	      spheres=sp->p; 
	    } else { 
	      sp2->p = sp->p;  
	    };
	    rmsp(sp, x, y);
	    break;
	  };
	  sp2 = sp;
	};
}


rmsp(sp, x, y)
     register struct sphere *sp;
     int x, y;
{
        item[x][y]= mitem[x][y]= 0;  
	know[x][y]=1;
	show1cell(x,y);  	/* show the now missing sphere */
	--c[SPHCAST];	
	free( (char *) sp);
}

/*
 *	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
 */
sphboom(x,y)
int x,y;
{
	register int i,j,k;

	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);
	    for (k=0;k<26;k++) if (iven[k]==OSPHTALISMAN) return;
	    if (playerx==j && playery==i)
	    {
	      cursors(); 
	      beep();
	      lprcat("\nYou were too close to the sphere!");
	      nap(3000);
	      died(283); /* player killed in explosion */
	    }
	  }
}

/*
 *		Function to ask for monster and genocide from game
 *	genmonst()		
 *
 *	This is done by setting a flag in the monster[] structure
 */
genmonst()
{
	register int i,j;
	cursors();  
	lprcat("\nGenocide what monster? ");
	for (i=0; (!isalpha(i)) && (i!=' '); i=getcharacter());
	lprc(i);
	for (j=0; j<MAXMONST; j++)	/* search for the monster type */
	  if (monstnamelist[j]==i)	/* have we found it? */
	    if (monster[j].genocided==0) {
	      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!");
}
