xref: /original-bsd/games/larn/monster.c (revision 87febec0)
1 /*
2  *	monster.c		Larn is copyrighted 1986 by Noah Morgan.
3  *
4  *	This file contains the following functions:
5  *	----------------------------------------------------------------------------
6  *
7  *	createmonster(monstno) 		Function to create a monster next to the player
8  *		int monstno;
9  *
10  *	int cgood(x,y,itm,monst)	Function to check location for emptiness
11  *		int x,y,itm,monst;
12  *
13  *	createitem(it,arg) 			Routine to place an item next to the player
14  *		int it,arg;
15  *
16  *	cast() 				Subroutine called by parse to cast a spell for the user
17  *
18  *	speldamage(x) 		Function to perform spell functions cast by the player
19  *		int x;
20  *
21  *	loseint()			Routine to decrement your int (intelligence) if > 3
22  *
23  *	isconfuse() 		Routine to check to see if player is confused
24  *
25  *	nospell(x,monst)	Routine to return 1 if a spell doesn't affect a monster
26  *		int x,monst;
27  *
28  *	fullhit(xx)			Function to return full damage against a monst (aka web)
29  *		int xx;
30  *
31  *	direct(spnum,dam,str,arg)	Routine to direct spell damage 1 square in 1 dir
32  *		int spnum,dam,arg;
33  *		char *str;
34  *
35  *	godirect(spnum,dam,str,delay,cshow)		Function to perform missile attacks
36  *		int spnum,dam,delay;
37  *		char *str,cshow;
38  *
39  *	ifblind(x,y)	Routine to put "monster" or the monster name into lastmosnt
40  *		int x,y;
41  *
42  *	tdirect(spnum)			Routine to teleport away a monster
43  *		int spnum;
44  *
45  *	omnidirect(sp,dam,str)  Routine to damage all monsters 1 square from player
46  *		int sp,dam;
47  *		char *str;
48  *
49  *	dirsub(x,y)			Routine to ask for direction, then modify x,y for it
50  *		int *x,*y;
51  *
52  *	vxy(x,y)		  	Routine to verify/fix (*x,*y) for being within bounds
53  *		int *x,*y;
54  *
55  *	dirpoly(spnum)		Routine to ask for a direction and polymorph a monst
56  *		int spnum;
57  *
58  *	hitmonster(x,y) 	Function to hit a monster at the designated coordinates
59  *		int x,y;
60  *
61  *	hitm(x,y,amt)		Function to just hit a monster at a given coordinates
62  *		int x,y,amt;
63  *
64  *	hitplayer(x,y) 		Function for the monster to hit the player from (x,y)
65  *		int x,y;
66  *
67  *	dropsomething(monst) 	Function to create an object when a monster dies
68  *		int monst;
69  *
70  *	dropgold(amount) 		Function to drop some gold around player
71  *		int amount;
72  *
73  *	something(level) 		Function to create a random item around player
74  *		int level;
75  *
76  *	newobject(lev,i) 		Routine to return a randomly selected new object
77  *		int lev,*i;
78  *
79  *  spattack(atckno,xx,yy) 	Function to process special attacks from monsters
80  *  	int atckno,xx,yy;
81  *
82  *	checkloss(x) 	Routine to subtract hp from user and flag bottomline display
83  *		int x;
84  *
85  *	annihilate()   Routine to annihilate monsters around player, playerx,playery
86  *
87  *	newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
88  *		int x,y,dir,lifetime;
89  *
90  *	rmsphere(x,y)		Function to delete a sphere of annihilation from list
91  *		int x,y;
92  *
93  *	sphboom(x,y)		Function to perform the effects of a sphere detonation
94  *		int x,y;
95  *
96  *	genmonst()			Function to ask for monster and genocide from game
97  *
98  */
99 #include "header.h"
100 
101 struct isave	/* used for altar reality */
102 	{
103 	char type;	/* 0=item,  1=monster */
104 	char id;	/* item number or monster number */
105 	short arg;	/* the type of item or hitpoints of monster */
106 	};
107 
108 /*
109  *	createmonster(monstno) 		Function to create a monster next to the player
110  *		int monstno;
111  *
112  *	Enter with the monster number (1 to MAXMONST+8)
113  *	Returns no value.
114  */
115 createmonster(mon)
116 	int mon;
117 	{
118 	register int x,y,k,i;
119 	if (mon<1 || mon>MAXMONST+8)	/* check for monster number out of bounds */
120 		{
121 		beep(); lprintf("\ncan't createmonst(%d)\n",(long)mon); nap(3000); return;
122 		}
123 	while (monster[mon].genocided && mon<MAXMONST) mon++; /* genocided? */
124 	for (k=rnd(8), i= -8; i<0; i++,k++)	/* choose direction, then try all */
125 		{
126 		if (k>8) k=1;	/* wraparound the diroff arrays */
127 		x = playerx + diroffx[k];		y = playery + diroffy[k];
128 		if (cgood(x,y,0,1))	/* if we can create here */
129 			{
130 			mitem[x][y] = mon;
131 			hitp[x][y] = monster[mon].hitpoints;
132 			stealth[x][y]=know[x][y]=0;
133 			switch(mon)
134 				{
135 				case ROTHE: case POLTERGEIST: case VAMPIRE: stealth[x][y]=1;
136 				};
137 			return;
138 			}
139 		}
140 	}
141 
142 /*
143  *	int cgood(x,y,itm,monst)	  Function to check location for emptiness
144  *		int x,y,itm,monst;
145  *
146  *	Routine to return TRUE if a location does not have itm or monst there
147  *	returns FALSE (0) otherwise
148  *	Enter with itm or monst TRUE or FALSE if checking it
149  *	Example:  if itm==TRUE check for no item at this location
150  *			  if monst==TRUE check for no monster at this location
151  *	This routine will return FALSE if at a wall or the dungeon exit on level 1
152  */
153 int cgood(x,y,itm,monst)
154 	register int x,y;
155 	int itm,monst;
156 	{
157 	if ((y>=0) && (y<=MAXY-1) && (x>=0) && (x<=MAXX-1)) /* within bounds? */
158 	  if (item[x][y]!=OWALL)	/* can't make anything on walls */
159 		if (itm==0 || (item[x][y]==0))	/* is it free of items? */
160 		  if (monst==0 || (mitem[x][y]==0))	/* is it free of monsters? */
161 		    if ((level!=1) || (x!=33) || (y!=MAXY-1)) /* not exit to level 1 */
162 			  return(1);
163 	return(0);
164 	}
165 
166 /*
167  *	createitem(it,arg) 		Routine to place an item next to the player
168  *		int it,arg;
169  *
170  *	Enter with the item number and its argument (iven[], ivenarg[])
171  *	Returns no value, thus we don't know about createitem() failures.
172  */
173 createitem(it,arg)
174 	int it,arg;
175 	{
176 	register int x,y,k,i;
177 	if (it >= MAXOBJ) return;	/* no such object */
178 	for (k=rnd(8), i= -8; i<0; i++,k++)	/* choose direction, then try all */
179 		{
180 		if (k>8) k=1;	/* wraparound the diroff arrays */
181 		x = playerx + diroffx[k];		y = playery + diroffy[k];
182 		if (cgood(x,y,1,0))	/* if we can create here */
183 			{
184 			item[x][y] = it;  know[x][y]=0;  iarg[x][y]=arg;  return;
185 			}
186 		}
187 	}
188 
189 /*
190  *	cast() 		Subroutine called by parse to cast a spell for the user
191  *
192  *	No arguments and no return value.
193  */
194 static char eys[] = "\nEnter your spell: ";
195 cast()
196 	{
197 	register int i,j,a,b,d;
198 	cursors();
199 	if (c[SPELLS]<=0) {	lprcat("\nYou don't have any spells!");	return;	}
200 	lprcat(eys);		--c[SPELLS];
201 	while ((a=getchar())=='D')
202 		{ seemagic(-1); cursors();  lprcat(eys); }
203 	if (a=='\33') goto over; /*	to escape casting a spell	*/
204 	if ((b=getchar())=='\33') goto over; /*	to escape casting a spell	*/
205 	if ((d=getchar())=='\33')
206 		{ over: lprcat(aborted); c[SPELLS]++; return; } /*	to escape casting a spell	*/
207 #ifdef EXTRA
208 	c[SPELLSCAST]++;
209 #endif
210 	for (lprc('\n'),j= -1,i=0; i<SPNUM; i++) /*seq search for his spell, hash?*/
211 		if ((spelcode[i][0]==a) && (spelcode[i][1]==b) && (spelcode[i][2]==d))
212 			if (spelknow[i])
213 				{  speldamage(i);  j = 1;  i=SPNUM; }
214 
215 	if (j == -1) lprcat("  Nothing Happened ");
216 	bottomline();
217 	}
218 
219 /*
220  *	speldamage(x) 		Function to perform spell functions cast by the player
221  *		int x;
222  *
223  *	Enter with the spell number, returns no value.
224  *	Please insure that there are 2 spaces before all messages here
225  */
226 speldamage(x)
227 	int x;
228 	{
229 	register int i,j,clev;
230 	int xl,xh,yl,yh;
231 	register char *p,*kn,*pm;
232 	if (x>=SPNUM) return;	/* no such spell */
233 	if (c[TIMESTOP])  { lprcat("  It didn't seem to work"); return; }  /* not if time stopped */
234 	clev = c[LEVEL];
235 	if ((rnd(23)==7) || (rnd(18) > c[INTELLIGENCE]))
236 		{ lprcat("  It didn't work!");  return; }
237 	if (clev*3+2 < x) { lprcat("  Nothing happens.  You seem inexperienced at this"); return; }
238 
239 	switch(x)
240 		{
241 /* ----- LEVEL 1 SPELLS ----- */
242 
243 		case 0:	if (c[PROTECTIONTIME]==0)	c[MOREDEFENSES]+=2; /* protection field +2 */
244 				c[PROTECTIONTIME] += 250;   return;
245 
246 		case 1: i = rnd(((clev+1)<<1)) + clev + 3;
247 				godirect(x,i,(clev>=2)?"  Your missiles hit the %s":"  Your missile hit the %s",100,'+'); /* magic missile */
248 
249 				return;
250 
251 		case 2:	if (c[DEXCOUNT]==0)	c[DEXTERITY]+=3; /*	dexterity	*/
252 				c[DEXCOUNT] += 400;  	return;
253 
254 		case 3: i=rnd(3)+1;
255 				p="  While the %s slept, you smashed it %d times";
256 			ws:	direct(x,fullhit(i),p,i); /*	sleep	*/	return;
257 
258 		case 4:	/*	charm monster	*/	c[CHARMCOUNT] += c[CHARISMA]<<1;	return;
259 
260 		case 5:	godirect(x,rnd(10)+15+clev,"  The sound damages the %s",70,'@'); /*	sonic spear */
261 				return;
262 
263 /* ----- LEVEL 2 SPELLS ----- */
264 
265 		case 6: i=rnd(3)+2;	p="  While the %s is entangled, you hit %d times";
266 				goto ws; /* web */
267 
268 		case 7:	if (c[STRCOUNT]==0) c[STREXTRA]+=3;	/*	strength	*/
269 				c[STRCOUNT] += 150+rnd(100);    return;
270 
271 		case 8:	yl = playery-5;     /* enlightenment */
272 				yh = playery+6;   xl = playerx-15;   xh = playerx+16;
273 				vxy(&xl,&yl);   vxy(&xh,&yh); /* check bounds */
274 				for (i=yl; i<=yh; i++) /* enlightenment	*/
275 					for (j=xl; j<=xh; j++)	know[j][i]=1;
276 				draws(xl,xh+1,yl,yh+1);	return;
277 
278 		case 9:	raisehp(20+(clev<<1));  return;  /* healing */
279 
280 		case 10:	c[BLINDCOUNT]=0;	return;	/* cure blindness	*/
281 
282 		case 11:	createmonster(makemonst(level+1)+8);  return;
283 
284 		case 12:	if (rnd(11)+7 <= c[WISDOM]) direct(x,rnd(20)+20+clev,"  The %s believed!",0);
285 					else lprcat("  It didn't believe the illusions!");
286 					return;
287 
288 		case 13:	/* if he has the amulet of invisibility then add more time */
289 					for (j=i=0; i<26; i++)
290 						if (iven[i]==OAMULET) j+= 1+ivenarg[i];
291 					c[INVISIBILITY] += (j<<7)+12;   return;
292 
293 /* ----- LEVEL 3 SPELLS ----- */
294 
295 		case 14:	godirect(x,rnd(25+clev)+25+clev,"  The fireball hits the %s",40,'*'); return; /*	fireball */
296 
297 		case 15:	godirect(x,rnd(25)+20+clev,"  Your cone of cold strikes the %s",60,'O');	/*	cold */
298 					return;
299 
300 		case 16:	dirpoly(x);  return;	/*	polymorph */
301 
302 		case 17:	c[CANCELLATION]+= 5+clev;	return;	/*	cancellation	*/
303 
304 		case 18:	c[HASTESELF]+= 7+clev;  return;  /*	haste self	*/
305 
306 		case 19:	omnidirect(x,30+rnd(10),"  The %s gasps for air");	/* cloud kill */
307 					return;
308 
309 		case 20:	xh = min(playerx+1,MAXX-2);		yh = min(playery+1,MAXY-2);
310 					for (i=max(playerx-1,1); i<=xh; i++) /* vaporize rock */
311 					  for (j=max(playery-1,1); j<=yh; j++)
312 						{
313 						kn = &know[i][j];    pm = &mitem[i][j];
314 						switch(*(p= &item[i][j]))
315 						  {
316 						  case OWALL: if (level < MAXLEVEL+MAXVLEVEL-1)
317 											*p = *kn = 0;
318 										break;
319 
320 						  case OSTATUE: if (c[HARDGAME]<3)
321 											 {
322 											 *p=OBOOK; iarg[i][j]=level;  *kn=0;
323 											 }
324 										break;
325 
326 						  case OTHRONE: *pm=GNOMEKING;  *kn=0;  *p= OTHRONE2;
327 										hitp[i][j]=monster[GNOMEKING].hitpoints; break;
328 
329 						  case OALTAR:	*pm=DEMONPRINCE;  *kn=0;
330 										hitp[i][j]=monster[DEMONPRINCE].hitpoints; break;
331 						  };
332 						switch(*pm)
333 							{
334 							case XORN:	ifblind(i,j);  hitm(i,j,200); break; /* Xorn takes damage from vpr */
335 							}
336 						}
337 					return;
338 
339 /* ----- LEVEL 4 SPELLS ----- */
340 
341 		case 21:	direct(x,100+clev,"  The %s shrivels up",0); /* dehydration */
342 					return;
343 
344 		case 22:	godirect(x,rnd(25)+20+(clev<<1),"  A lightning bolt hits the %s",1,'~');	/*	lightning */
345 					return;
346 
347 		case 23:	i=min(c[HP]-1,c[HPMAX]/2);	/* drain life */
348 					direct(x,i+i,"",0);	c[HP] -= i;  	return;
349 
350 		case 24:	if (c[GLOBE]==0) c[MOREDEFENSES] += 10;
351 					c[GLOBE] += 200;  loseint();  /* globe of invulnerability */
352 					return;
353 
354 		case 25:	omnidirect(x,32+clev,"  The %s struggles for air in your flood!"); /* flood */
355 					return;
356 
357 		case 26:	if (rnd(151)==63) { beep(); lprcat("\nYour heart stopped!\n"); nap(4000);  died(270); return; }
358 					if (c[WISDOM]>rnd(10)+10) direct(x,2000,"  The %s's heart stopped",0); /* finger of death */
359 					else lprcat("  It didn't work"); return;
360 
361 /* ----- LEVEL 5 SPELLS ----- */
362 
363 		case 27:	c[SCAREMONST] += rnd(10)+clev;  return;  /* scare monster */
364 
365 		case 28:	c[HOLDMONST] += rnd(10)+clev;  return;  /* hold monster */
366 
367 		case 29:	c[TIMESTOP] += rnd(20)+(clev<<1);  return;  /* time stop */
368 
369 		case 30:	tdirect(x);  return;  /* teleport away */
370 
371 		case 31:	omnidirect(x,35+rnd(10)+clev,"  The %s cringes from the flame"); /* magic fire */
372 					return;
373 
374 /* ----- LEVEL 6 SPELLS ----- */
375 
376 		case 32:	if ((rnd(23)==5) && (wizard==0)) /* sphere of annihilation */
377 						{
378 						beep(); lprcat("\nYou have been enveloped by the zone of nothingness!\n");
379 						nap(4000);  died(258); return;
380 						}
381 					xl=playerx; yl=playery;
382 					loseint();
383 					i=dirsub(&xl,&yl); /* get direction of sphere */
384 					newsphere(xl,yl,i,rnd(20)+11);	/* make a sphere */
385 					return;
386 
387 		case 33:	genmonst();  spelknow[33]=0;  /* genocide */
388 					loseint();
389 					return;
390 
391 		case 34:	/* summon demon */
392 					if (rnd(100) > 30) { direct(x,150,"  The demon strikes at the %s",0);  return; }
393 					if (rnd(100) > 15) { lprcat("  Nothing seems to have happened");  return; }
394 					lprcat("  The demon turned on you and vanished!"); beep();
395 					i=rnd(40)+30;  lastnum=277;
396 					losehp(i); /* must say killed by a demon */ return;
397 
398 		case 35:	/* walk through walls */
399 					c[WTW] += rnd(10)+5;	return;
400 
401 		case 36:	/* alter reality */
402 					{
403 					struct isave *save;	/* pointer to item save structure */
404 					int sc;	sc=0;	/* # items saved */
405 					save = (struct isave *)malloc(sizeof(struct isave)*MAXX*MAXY*2);
406 					for (j=0; j<MAXY; j++)
407 						for (i=0; i<MAXX; i++) /* save all items and monsters */
408 							{
409 							xl = item[i][j];
410 							if (xl && xl!=OWALL && xl!=OANNIHILATION)
411 								{
412 								save[sc].type=0;  save[sc].id=item[i][j];
413 								save[sc++].arg=iarg[i][j];
414 								}
415 							if (mitem[i][j])
416 								{
417 								save[sc].type=1;  save[sc].id=mitem[i][j];
418 								save[sc++].arg=hitp[i][j];
419 								}
420 							item[i][j]=OWALL;   mitem[i][j]=0;
421 							if (wizard) know[i][j]=1; else know[i][j]=0;
422 							}
423 					eat(1,1);	if (level==1) item[33][MAXY-1]=0;
424 					for (j=rnd(MAXY-2), i=1; i<MAXX-1; i++) item[i][j]=0;
425 					while (sc>0) /* put objects back in level */
426 						{
427 						--sc;
428 						if (save[sc].type == 0)
429 							{
430 							int trys;
431 							for (trys=100, i=j=1; --trys>0 && item[i][j]; i=rnd(MAXX-1), j=rnd(MAXY-1));
432 							if (trys) { item[i][j]=save[sc].id; iarg[i][j]=save[sc].arg; }
433 							}
434 						else
435 							{ /* put monsters back in */
436 							int trys;
437 							for (trys=100, i=j=1; --trys>0 && (item[i][j]==OWALL || mitem[i][j]); i=rnd(MAXX-1), j=rnd(MAXY-1));
438 							if (trys) { mitem[i][j]=save[sc].id; hitp[i][j]=save[sc].arg; }
439 							}
440 						}
441 					loseint();
442 					draws(0,MAXX,0,MAXY);  if (wizard==0) spelknow[36]=0;
443 					free((char*)save);	 positionplayer();  return;
444 					}
445 
446 		case 37:	/* permanence */ adjtime(-99999L);  spelknow[37]=0; /* forget */
447 					loseint();
448 					return;
449 
450 		default:	lprintf("  spell %d not available!",(long)x); beep();  return;
451 		};
452 	}
453 
454 /*
455  *	loseint()		Routine to subtract 1 from your int (intelligence) if > 3
456  *
457  *	No arguments and no return value
458  */
459 loseint()
460 	{
461 	if (--c[INTELLIGENCE]<3)  c[INTELLIGENCE]=3;
462 	}
463 
464 /*
465  *	isconfuse() 		Routine to check to see if player is confused
466  *
467  *	This routine prints out a message saying "You can't aim your magic!"
468  *	returns 0 if not confused, non-zero (time remaining confused) if confused
469  */
470 isconfuse()
471 	{
472 	if (c[CONFUSE]) { lprcat(" You can't aim your magic!"); beep(); }
473 	return(c[CONFUSE]);
474 	}
475 
476 /*
477  *	nospell(x,monst)	Routine to return 1 if a spell doesn't affect a monster
478  *		int x,monst;
479  *
480  *	Subroutine to return 1 if the spell can't affect the monster
481  *	  otherwise returns 0
482  *	Enter with the spell number in x, and the monster number in monst.
483  */
484 nospell(x,monst)
485 	int x,monst;
486 	{
487 	register int tmp;
488 	if (x>=SPNUM || monst>=MAXMONST+8 || monst<0 || x<0) return(0);	/* bad spell or monst */
489 	if ((tmp=spelweird[monst-1][x])==0) return(0);
490 	cursors();  lprc('\n');  lprintf(spelmes[tmp],monster[monst].name);  return(1);
491 	}
492 
493 /*
494  *	fullhit(xx)		Function to return full damage against a monster (aka web)
495  *		int xx;
496  *
497  *	Function to return hp damage to monster due to a number of full hits
498  *	Enter with the number of full hits being done
499  */
500 fullhit(xx)
501 	int xx;
502 	{
503 	register int i;
504 	if (xx<0 || xx>20) return(0);	/* fullhits are out of range */
505 	if (c[LANCEDEATH]) return(10000);	/* lance of death */
506 	i = xx * ((c[WCLASS]>>1)+c[STRENGTH]+c[STREXTRA]-c[HARDGAME]-12+c[MOREDAM]);
507 	return( (i>=1) ? i : xx );
508 	}
509 
510 /*
511  *	direct(spnum,dam,str,arg)	Routine to direct spell damage 1 square in 1 dir
512  *		int spnum,dam,arg;
513  *		char *str;
514  *
515  *	Routine to ask for a direction to a spell and then hit the monster
516  *	Enter with the spell number in spnum, the damage to be done in dam,
517  *	  lprintf format string in str, and lprintf's argument in arg.
518  *	Returns no value.
519  */
520 direct(spnum,dam,str,arg)
521 	int spnum,dam,arg;
522 	char *str;
523 	{
524 	int x,y;
525 	register int m;
526 	if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad arguments */
527 	if (isconfuse()) return;
528 	dirsub(&x,&y);
529 	m = mitem[x][y];
530 	if (item[x][y]==OMIRROR)
531 		{
532 		if (spnum==3) /* sleep */
533 			{
534 			lprcat("You fall asleep! "); beep();
535 		fool:
536 			arg += 2;
537 			while (arg-- > 0) { parse2(); nap(1000); }
538 			return;
539 			}
540 		else if (spnum==6) /* web */
541 			{
542 			lprcat("You get stuck in your own web! "); beep();
543 			goto fool;
544 			}
545 		else
546 			{
547 			lastnum=278;
548 			lprintf(str,"spell caster (thats you)",(long)arg);
549 			beep(); losehp(dam); return;
550 			}
551 		}
552 	if (m==0)
553 		{	lprcat("  There wasn't anything there!");	return;  }
554 	ifblind(x,y);
555 	if (nospell(spnum,m)) { lasthx=x;  lasthy=y; return; }
556 	lprintf(str,lastmonst,(long)arg);       hitm(x,y,dam);
557 	}
558 
559 /*
560  *	godirect(spnum,dam,str,delay,cshow)		Function to perform missile attacks
561  *		int spnum,dam,delay;
562  *		char *str,cshow;
563  *
564  *	Function to hit in a direction from a missile weapon and have it keep
565  *	on going in that direction until its power is exhausted
566  *	Enter with the spell number in spnum, the power of the weapon in hp,
567  *	  lprintf format string in str, the # of milliseconds to delay between
568  *	  locations in delay, and the character to represent the weapon in cshow.
569  *	Returns no value.
570  */
571 godirect(spnum,dam,str,delay,cshow)
572 	int spnum,dam,delay;
573 	char *str,cshow;
574 	{
575 	register char *p;
576 	register int x,y,m;
577 	int dx,dy;
578 	if (spnum<0 || spnum>=SPNUM || str==0 || delay<0) return; /* bad args */
579 	if (isconfuse()) return;
580 	dirsub(&dx,&dy);	x=dx;	y=dy;
581 	dx = x-playerx;		dy = y-playery;		x = playerx;	y = playery;
582 	while (dam>0)
583 		{
584 		x += dx;    y += dy;
585 	    if ((x > MAXX-1) || (y > MAXY-1) || (x < 0) || (y < 0))
586 			{
587 			dam=0;	break;  /* out of bounds */
588 			}
589 		if ((x==playerx) && (y==playery)) /* if energy hits player */
590 			{
591 			cursors(); lprcat("\nYou are hit my your own magic!"); beep();
592 			lastnum=278;  losehp(dam);  return;
593 			}
594 		if (c[BLINDCOUNT]==0) /* if not blind show effect */
595 			{
596 			cursor(x+1,y+1); lprc(cshow); nap(delay); show1cell(x,y);
597 			}
598 		if ((m=mitem[x][y]))	/* is there a monster there? */
599 			{
600 			ifblind(x,y);
601 			if (nospell(spnum,m)) { lasthx=x;  lasthy=y; return; }
602 			cursors(); lprc('\n');
603 			lprintf(str,lastmonst);		dam -= hitm(x,y,dam);
604 			show1cell(x,y);  nap(1000);		x -= dx;	y -= dy;
605 			}
606 		else switch (*(p= &item[x][y]))
607 			{
608 			case OWALL:	cursors(); lprc('\n'); lprintf(str,"wall");
609 						if (dam>=50+c[HARDGAME]) /* enough damage? */
610 						 if (level<MAXLEVEL+MAXVLEVEL-1) /* not on V3 */
611 						  if ((x<MAXX-1) && (y<MAXY-1) && (x) && (y))
612 							{
613 							lprcat("  The wall crumbles");
614 					god3:	*p=0;
615 					god:	know[x][y]=0;
616 							show1cell(x,y);
617 							}
618 				god2:	dam = 0;	break;
619 
620 			case OCLOSEDDOOR:	cursors(); lprc('\n'); lprintf(str,"door");
621 						if (dam>=40)
622 							{
623 							lprcat("  The door is blasted apart");
624 							goto god3;
625 							}
626 						goto god2;
627 
628 			case OSTATUE:	cursors(); lprc('\n'); lprintf(str,"statue");
629 						if (c[HARDGAME]<3)
630 						  if (dam>44)
631 							{
632 							lprcat("  The statue crumbles");
633 							*p=OBOOK; iarg[x][y]=level;
634 							goto god;
635 							}
636 						goto god2;
637 
638 			case OTHRONE:	cursors(); lprc('\n'); lprintf(str,"throne");
639 					if (dam>39)
640 						{
641 						mitem[x][y]=GNOMEKING; hitp[x][y]=monster[GNOMEKING].hitpoints;
642 						*p = OTHRONE2;
643 						goto god;
644 						}
645 					goto god2;
646 
647 			case OMIRROR:	dx *= -1;	dy *= -1;	break;
648 			};
649 		dam -= 3 + (c[HARDGAME]>>1);
650 		}
651 	}
652 
653 /*
654  *	ifblind(x,y)	Routine to put "monster" or the monster name into lastmosnt
655  *		int x,y;
656  *
657  *	Subroutine to copy the word "monster" into lastmonst if the player is blind
658  *	Enter with the coordinates (x,y) of the monster
659  *	Returns no value.
660  */
661 ifblind(x,y)
662 	int x,y;
663 	{
664 	char *p;
665 	vxy(&x,&y);	/* verify correct x,y coordinates */
666 	if (c[BLINDCOUNT]) { lastnum=279;  p="monster"; }
667 		else { lastnum=mitem[x][y];  p=monster[lastnum].name; }
668 	strcpy(lastmonst,p);
669 	}
670 
671 /*
672  *	tdirect(spnum)		Routine to teleport away a monster
673  *		int spnum;
674  *
675  *	Routine to ask for a direction to a spell and then teleport away monster
676  *	Enter with the spell number that wants to teleport away
677  *	Returns no value.
678  */
679 tdirect(spnum)
680 	int spnum;
681 	{
682 	int x,y;
683 	register int m;
684 	if (spnum<0 || spnum>=SPNUM) return; /* bad args */
685 	if (isconfuse()) return;
686 	dirsub(&x,&y);
687 	if ((m=mitem[x][y])==0)
688 		{	lprcat("  There wasn't anything there!");	return;  }
689 	ifblind(x,y);
690 	if (nospell(spnum,m)) { lasthx=x;  lasthy=y; return; }
691 	fillmonst(m);  mitem[x][y]=know[x][y]=0;
692 	}
693 
694 /*
695  *	omnidirect(sp,dam,str)   Routine to damage all monsters 1 square from player
696  *		int sp,dam;
697  *		char *str;
698  *
699  *	Routine to cast a spell and then hit the monster in all directions
700  *	Enter with the spell number in sp, the damage done to wach square in dam,
701  *	  and the lprintf string to identify the spell in str.
702  *	Returns no value.
703  */
704 omnidirect(spnum,dam,str)
705 	int spnum,dam;
706 	char *str;
707 	{
708 	register int x,y,m;
709 	if (spnum<0 || spnum>=SPNUM || str==0) return; /* bad args */
710 	for (x=playerx-1; x<playerx+2; x++)
711 		for (y=playery-1; y<playery+2; y++)
712 			{
713 			if (m=mitem[x][y])
714 				if (nospell(spnum,m) == 0)
715 					{
716 					ifblind(x,y);
717 					cursors(); lprc('\n'); lprintf(str,lastmonst);
718 					hitm(x,y,dam);  nap(800);
719 					}
720 				else  { lasthx=x;  lasthy=y; }
721 			}
722 	}
723 
724 /*
725  *	static dirsub(x,y)		Routine to ask for direction, then modify x,y for it
726  *		int *x,*y;
727  *
728  *	Function to ask for a direction and modify an x,y for that direction
729  *	Enter with the origination coordinates in (x,y).
730  *	Returns index into diroffx[] (0-8).
731  */
732 static dirsub(x,y)
733 	int *x,*y;
734 	{
735 	register int i;
736 	lprcat("\nIn What Direction? ");
737 	for (i=0; ; )
738 		switch(getchar())
739 			{
740 			case 'b':	i++;
741 			case 'n':	i++;
742 			case 'y':	i++;
743 			case 'u':	i++;
744 			case 'h':	i++;
745 			case 'k':	i++;
746 			case 'l':	i++;
747 			case 'j':	i++;		goto out;
748 			};
749 out:
750 	*x = playerx+diroffx[i];		*y = playery+diroffy[i];
751 	vxy(x,y);  return(i);
752 	}
753 
754 /*
755  *	vxy(x,y)	   Routine to verify/fix coordinates for being within bounds
756  *		int *x,*y;
757  *
758  *	Function to verify x & y are within the bounds for a level
759  *	If *x or *y is not within the absolute bounds for a level, fix them so that
760  *	  they are on the level.
761  *	Returns TRUE if it was out of bounds, and the *x & *y in the calling
762  *	routine are affected.
763  */
764 vxy(x,y)
765 	int *x,*y;
766 	{
767 	int flag=0;
768 	if (*x<0) { *x=0; flag++; }
769 	if (*y<0) { *y=0; flag++; }
770 	if (*x>=MAXX) { *x=MAXX-1; flag++; }
771 	if (*y>=MAXY) { *y=MAXY-1; flag++; }
772 	return(flag);
773 	}
774 
775 /*
776  *	dirpoly(spnum)		Routine to ask for a direction and polymorph a monst
777  *		int spnum;
778  *
779  *	Subroutine to polymorph a monster and ask for the direction its in
780  *	Enter with the spell number in spmun.
781  *	Returns no value.
782  */
783 dirpoly(spnum)
784 	int spnum;
785 	{
786 	int x,y,m;
787 	if (spnum<0 || spnum>=SPNUM) return; /* bad args */
788 	if (isconfuse()) return;	/* if he is confused, he can't aim his magic */
789 	dirsub(&x,&y);
790 	if (mitem[x][y]==0)
791 		{	lprcat("  There wasn't anything there!");	return;  }
792 	ifblind(x,y);
793 	if (nospell(spnum,mitem[x][y])) { lasthx=x;  lasthy=y; return; }
794 	while ( monster[m = mitem[x][y] = rnd(MAXMONST+7)].genocided );
795 	hitp[x][y] = monster[m].hitpoints;
796 	show1cell(x,y);  /* show the new monster */
797 	}
798 
799 /*
800  *	hitmonster(x,y) 	Function to hit a monster at the designated coordinates
801  *		int x,y;
802  *
803  *	This routine is used for a bash & slash type attack on a monster
804  *	Enter with the coordinates of the monster in (x,y).
805  *	Returns no value.
806  */
807 hitmonster(x,y)
808 	int x,y;
809 	{
810 	register int tmp,monst,damag,flag;
811 	if (c[TIMESTOP])  return;  /* not if time stopped */
812 	vxy(&x,&y);	/* verify coordinates are within range */
813 	if ((monst = mitem[x][y]) == 0) return;
814 	hit3flag=1;  ifblind(x,y);
815 	tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] + c[WCLASS]/4 - 12;
816 	cursors();
817 	if ((rnd(20) < tmp-c[HARDGAME]) || (rnd(71) < 5)) /* need at least random chance to hit */
818 		{
819 		lprcat("\nYou hit");  flag=1;
820 		damag = fullhit(1);
821 		if (damag<9999) damag=rnd(damag)+1;
822 		}
823 	else
824 		{
825 		lprcat("\nYou missed");  flag=0;
826 		}
827 	lprcat(" the "); lprcat(lastmonst);
828 	if (flag)	/* if the monster was hit */
829 	  if ((monst==RUSTMONSTER) || (monst==DISENCHANTRESS) || (monst==CUBE))
830 		if (c[WIELD]>0)
831 		  if (ivenarg[c[WIELD]] > -10)
832 			{
833 			lprintf("\nYour weapon is dulled by the %s",lastmonst); beep();
834 			--ivenarg[c[WIELD]];
835 			}
836 	if (flag)  hitm(x,y,damag);
837 	if (monst == VAMPIRE) if (hitp[x][y]<25)  { mitem[x][y]=BAT; know[x][y]=0; }
838 	}
839 
840 /*
841  *	hitm(x,y,amt)		Function to just hit a monster at a given coordinates
842  *		int x,y,amt;
843  *
844  *	Returns the number of hitpoints the monster absorbed
845  *	This routine is used to specifically damage a monster at a location (x,y)
846  *	Called by hitmonster(x,y)
847  */
848 hitm(x,y,amt)
849 	int x,y;
850 	register amt;
851 	{
852 	register int monst;
853 	int hpoints,amt2;
854 	vxy(&x,&y);	/* verify coordinates are within range */
855 	amt2 = amt;		/* save initial damage so we can return it */
856 	monst = mitem[x][y];
857 	if (c[HALFDAM]) amt >>= 1;	/* if half damage curse adjust damage points */
858 	if (amt<=0) amt2 = amt = 1;
859 	lasthx=x;  lasthy=y;
860 	stealth[x][y]=1;	/* make sure hitting monst breaks stealth condition */
861 	c[HOLDMONST]=0;	/* hit a monster breaks hold monster spell	*/
862 	switch(monst) /* if a dragon and orb(s) of dragon slaying	*/
863 		{
864 		case WHITEDRAGON:		case REDDRAGON:			case GREENDRAGON:
865 		case BRONZEDRAGON:		case PLATINUMDRAGON:	case SILVERDRAGON:
866 			amt *= 1+(c[SLAYING]<<1);	break;
867 		}
868 /* invincible monster fix is here */
869 	if (hitp[x][y] > monster[monst].hitpoints)
870 		hitp[x][y] = monster[monst].hitpoints;
871 	if ((hpoints = hitp[x][y]) <= amt)
872 		{
873 #ifdef EXTRA
874 		c[MONSTKILLED]++;
875 #endif
876 		lprintf("\nThe %s died!",lastmonst);
877 		raiseexperience((long)monster[monst].experience);
878 		amt = monster[monst].gold;  if (amt>0) dropgold(rnd(amt)+amt);
879 		dropsomething(monst);	disappear(x,y);	bottomline();
880 		return(hpoints);
881 		}
882 	hitp[x][y] = hpoints-amt;	return(amt2);
883 	}
884 
885 /*
886  *	hitplayer(x,y) 		Function for the monster to hit the player from (x,y)
887  *		int x,y;
888  *
889  *	Function for the monster to hit the player with monster at location x,y
890  *	Returns nothing of value.
891  */
892 hitplayer(x,y)
893 	int x,y;
894 	{
895 	register int dam,tmp,mster,bias;
896 	vxy(&x,&y);	/* verify coordinates are within range */
897 	lastnum = mster = mitem[x][y];
898 /*	spirit naga's and poltergeist's do nothing if scarab of negate spirit	*/
899 	if (c[NEGATESPIRIT] || c[SPIRITPRO])  if ((mster ==POLTERGEIST) || (mster ==SPIRITNAGA))  return;
900 /*	if undead and cube of undead control	*/
901 	if (c[CUBEofUNDEAD] || c[UNDEADPRO]) if ((mster ==VAMPIRE) || (mster ==WRAITH) || (mster ==ZOMBIE)) return;
902 	if ((know[x][y]&1) == 0)
903 		{
904 		know[x][y]=1; show1cell(x,y);
905 		}
906 	bias = (c[HARDGAME]) + 1;
907 	hitflag = hit2flag = hit3flag = 1;
908 	yrepcount=0;
909 	cursors();	ifblind(x,y);
910 	if (c[INVISIBILITY]) if (rnd(33)<20)
911 		{
912 		lprintf("\nThe %s misses wildly",lastmonst);	return;
913 		}
914 	if (c[CHARMCOUNT]) if (rnd(30)+5*monster[mster].level-c[CHARISMA]<30)
915 		{
916 		lprintf("\nThe %s is awestruck at your magnificence!",lastmonst);
917 		return;
918 		}
919 	if (mster==BAT) dam=1;
920 	else
921 		{
922 		dam = monster[mster].damage;
923 		dam += rnd((int)((dam<1)?1:dam)) + monster[mster].level;
924 		}
925 	tmp = 0;
926 	if (monster[mster].attack>0)
927 	  if (((dam + bias + 8) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1))
928 		{ if (spattack(monster[mster].attack,x,y)) { flushall(); return; }
929 		  tmp = 1;  bias -= 2; cursors(); }
930 	if (((dam + bias) > c[AC]) || (rnd((int)((c[AC]>0)?c[AC]:1))==1))
931 		{
932 		lprintf("\n  The %s hit you ",lastmonst);	tmp = 1;
933 		if ((dam -= c[AC]) < 0) dam=0;
934 		if (dam > 0) { losehp(dam); bottomhp(); flushall(); }
935 		}
936 	if (tmp == 0)  lprintf("\n  The %s missed ",lastmonst);
937 	}
938 
939 /*
940  *	dropsomething(monst) 	Function to create an object when a monster dies
941  *		int monst;
942  *
943  *	Function to create an object near the player when certain monsters are killed
944  *	Enter with the monster number
945  *	Returns nothing of value.
946  */
947 dropsomething(monst)
948 	int monst;
949 	{
950 	switch(monst)
951 		{
952 		case ORC:			  case NYMPH:	   case ELF:	  case TROGLODYTE:
953 		case TROLL:			  case ROTHE:	   case VIOLETFUNGI:
954 		case PLATINUMDRAGON:  case GNOMEKING:  case REDDRAGON:
955 			something(level); return;
956 
957 		case LEPRECHAUN: if (rnd(101)>=75) creategem();
958 						 if (rnd(5)==1) dropsomething(LEPRECHAUN);   return;
959 		}
960 	}
961 
962 /*
963  *	dropgold(amount) 	Function to drop some gold around player
964  *		int amount;
965  *
966  *	Enter with the number of gold pieces to drop
967  *	Returns nothing of value.
968  */
969 dropgold(amount)
970 	register int amount;
971 	{
972 	if (amount > 250) createitem(OMAXGOLD,amount/100);  else  createitem(OGOLDPILE,amount);
973 	}
974 
975 /*
976  *	something(level) 	Function to create a random item around player
977  *		int level;
978  *
979  *	Function to create an item from a designed probability around player
980  *	Enter with the cave level on which something is to be dropped
981  *	Returns nothing of value.
982  */
983 something(level)
984 	int level;
985 	{
986 	register int j;
987 	int i;
988 	if (level<0 || level>MAXLEVEL+MAXVLEVEL) return;	/* correct level? */
989 	if (rnd(101)<8) something(level); /* possibly more than one item */
990 	j = newobject(level,&i);		createitem(j,i);
991 	}
992 
993 /*
994  *	newobject(lev,i) 	Routine to return a randomly selected new object
995  *		int lev,*i;
996  *
997  *	Routine to return a randomly selected object to be created
998  *	Returns the object number created, and sets *i for its argument
999  *	Enter with the cave level and a pointer to the items arg
1000  */
1001 static char nobjtab[] = { 0, OSCROLL,  OSCROLL,  OSCROLL,  OSCROLL, OPOTION,
1002 	OPOTION, OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1003 	OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER, OLEATHER, OLEATHER,
1004 	OLEATHER, OREGENRING, OPROTRING, OENERGYRING, ODEXRING, OSTRRING, OSPEAR,
1005 	OBELT, ORING, OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1006 	OLONGSWORD };
1007 
1008 newobject(lev,i)
1009 	register int lev,*i;
1010 	{
1011 	register int tmp=32,j;
1012 	if (level<0 || level>MAXLEVEL+MAXVLEVEL) return(0);	/* correct level? */
1013 	if (lev>6) tmp=37; else if (lev>4) tmp=35;
1014 	j = nobjtab[tmp=rnd(tmp)];	/* the object type */
1015 	switch(tmp)
1016 		{
1017 		case 1: case 2: case 3: case 4:	*i=newscroll();	break;
1018 		case 5: case 6: case 7: case 8:	*i=newpotion();	break;
1019 		case 9: case 10: case 11: case 12: *i=rnd((lev+1)*10)+lev*10+10; break;
1020 		case 13: case 14: case 15: case 16:	*i=lev;	break;
1021 		case 17: case 18: case 19: if (!(*i=newdagger()))  return(0);  break;
1022 		case 20: case 21: case 22: if (!(*i=newleather()))  return(0);  break;
1023 		case 23: case 32: case 35: *i=rund(lev/3+1); break;
1024 		case 24: case 26: *i=rnd(lev/4+1);   break;
1025 		case 25: *i=rund(lev/4+1); break;
1026 		case 27: *i=rnd(lev/2+1);   break;
1027 		case 30: case 33: *i=rund(lev/2+1);   break;
1028 		case 28: *i=rund(lev/3+1); if (*i==0) return(0); break;
1029 		case 29: case 31: *i=rund(lev/2+1); if (*i==0) return(0); break;
1030 		case 34: *i=newchain();   	break;
1031 		case 36: *i=newplate();   	break;
1032 		case 37: *i=newsword();		break;
1033 		}
1034 	return(j);
1035 	}
1036 
1037 /*
1038  *  spattack(atckno,xx,yy) 	Function to process special attacks from monsters
1039  *  	int atckno,xx,yy;
1040  *
1041  *	Enter with the special attack number, and the coordinates (xx,yy)
1042  *		of the monster that is special attacking
1043  *	Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1044  *
1045  * atckno   monster     effect
1046  * ---------------------------------------------------
1047  *	0	none
1048  *	1	rust monster	eat armor
1049  *	2	hell hound		breathe light fire
1050  *	3	dragon			breathe fire
1051  *	4	giant centipede	weakening sing
1052  *	5	white dragon	cold breath
1053  *	6	wraith			drain level
1054  *	7	waterlord		water gusher
1055  *	8	leprechaun		steal gold
1056  *	9	disenchantress	disenchant weapon or armor
1057  *	10	ice lizard		hits with barbed tail
1058  *	11	umber hulk		confusion
1059  *	12	spirit naga		cast spells	taken from special attacks
1060  *	13	platinum dragon	psionics
1061  *	14	nymph			steal objects
1062  *	15	bugbear			bite
1063  *	16	osequip			bite
1064  *
1065  *	char rustarm[ARMORTYPES][2];
1066  *	special array for maximum rust damage to armor from rustmonster
1067  *	format is: { armor type , minimum attribute
1068  */
1069 #define ARMORTYPES 6
1070 static char rustarm[ARMORTYPES][2] = { OSTUDLEATHER,-2,	ORING,-4, OCHAIN,-5,
1071 	OSPLINT,-6,		OPLATE,-8,		OPLATEARMOR,-9  };
1072 static char spsel[] = { 1, 2, 3, 5, 6, 8, 9, 11, 13, 14 };
1073 spattack(x,xx,yy)
1074 	int x,xx,yy;
1075 	{
1076 	register int i,j=0,k,m;
1077 	register char *p=0;
1078 	if (c[CANCELLATION]) return(0);
1079 	vxy(&xx,&yy);	/* verify x & y coordinates */
1080 	switch(x)
1081 		{
1082 		case 1:	/* rust your armor, j=1 when rusting has occurred */
1083 				m = k = c[WEAR];
1084 				if ((i=c[SHIELD]) != -1)
1085 					if (--ivenarg[i] < -1) ivenarg[i]= -1; else j=1;
1086 				if ((j==0) && (k != -1))
1087 				  {
1088 				  m = iven[k];
1089 				  for (i=0; i<ARMORTYPES; i++)
1090 					if (m == rustarm[i][0]) /* find his armor in table */
1091 						{
1092 						if (--ivenarg[k]< rustarm[i][1])
1093 							ivenarg[k]= rustarm[i][1]; else j=1;
1094 						break;
1095 						}
1096 				  }
1097 				if (j==0)	/* if rusting did not occur */
1098 				  switch(m)
1099 					{
1100 					case OLEATHER:	p = "\nThe %s hit you -- Your lucky you have leather on";
1101 									break;
1102 				    case OSSPLATE:	p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1103 									break;
1104 					}
1105 				else  { beep(); p = "\nThe %s hit you -- your armor feels weaker"; }
1106 				break;
1107 
1108 		case 2:		i = rnd(15)+8-c[AC];
1109 			spout:	p="\nThe %s breathes fire at you!";
1110 					if (c[FIRERESISTANCE])
1111 					  p="\nThe %s's flame doesn't phase you!";
1112 					else
1113 			spout2: if (p) { lprintf(p,lastmonst); beep(); }
1114 					checkloss(i);
1115 					return(0);
1116 
1117 		case 3:		i = rnd(20)+25-c[AC];  goto spout;
1118 
1119 		case 4:	if (c[STRENGTH]>3)
1120 					{
1121 					p="\nThe %s stung you!  You feel weaker"; beep();
1122 					--c[STRENGTH];
1123 					}
1124 				else p="\nThe %s stung you!";
1125 				break;
1126 
1127 		case 5:		p="\nThe %s blasts you with his cold breath";
1128 					i = rnd(15)+18-c[AC];  goto spout2;
1129 
1130 		case 6:		lprintf("\nThe %s drains you of your life energy!",lastmonst);
1131 					loselevel();  beep();  return(0);
1132 
1133 		case 7:		p="\nThe %s got you with a gusher!";
1134 					i = rnd(15)+25-c[AC];  goto spout2;
1135 
1136 		case 8:		if (c[NOTHEFT]) return(0); /* he has a device of no theft */
1137 					if (c[GOLD])
1138 						{
1139 						p="\nThe %s hit you -- Your purse feels lighter";
1140 						if (c[GOLD]>32767)  c[GOLD]>>=1;
1141 							else c[GOLD] -= rnd((int)(1+(c[GOLD]>>1)));
1142 						if (c[GOLD] < 0) c[GOLD]=0;
1143 						}
1144 					else  p="\nThe %s couldn't find any gold to steal";
1145 					lprintf(p,lastmonst); disappear(xx,yy); beep();
1146 					bottomgold();  return(1);
1147 
1148 		case 9:	for(j=50; ; )	/* disenchant */
1149 					{
1150 					i=rund(26);  m=iven[i]; /* randomly select item */
1151 					if (m>0 && ivenarg[i]>0 && m!=OSCROLL && m!=OPOTION)
1152 						{
1153 						if ((ivenarg[i] -= 3)<0) ivenarg[i]=0;
1154 						lprintf("\nThe %s hits you -- you feel a sense of loss",lastmonst);
1155 						srcount=0; beep(); show3(i);  bottomline();  return(0);
1156 						}
1157 					if (--j<=0)
1158 						{
1159 						p="\nThe %s nearly misses"; break;
1160 						}
1161 					break;
1162 					}
1163 				break;
1164 
1165 		case 10:   p="\nThe %s hit you with his barbed tail";
1166 				   i = rnd(25)-c[AC];  goto spout2;
1167 
1168 		case 11:	p="\nThe %s has confused you"; beep();
1169 					c[CONFUSE]+= 10+rnd(10);		break;
1170 
1171 		case 12:	/*	performs any number of other special attacks	*/
1172 					return(spattack(spsel[rund(10)],xx,yy));
1173 
1174 		case 13:	p="\nThe %s flattens you with his psionics!";
1175 					i = rnd(15)+30-c[AC];  goto spout2;
1176 
1177 		case 14:	if (c[NOTHEFT]) return(0); /* he has device of no theft */
1178 					if (emptyhanded()==1)
1179 					  {
1180 					  p="\nThe %s couldn't find anything to steal";
1181 					  break;
1182 					  }
1183 					lprintf("\nThe %s picks your pocket and takes:",lastmonst);
1184 					beep();
1185 					if (stealsomething()==0) lprcat("  nothing"); disappear(xx,yy);
1186 					bottomline();  return(1);
1187 
1188 		case 15:	i= rnd(10)+ 5-c[AC];
1189 			spout3:	p="\nThe %s bit you!";
1190 					goto spout2;
1191 
1192 		case 16:	i= rnd(15)+10-c[AC];  goto spout3;
1193 		};
1194 	if (p) { lprintf(p,lastmonst); bottomline(); }
1195 	return(0);
1196 	}
1197 
1198 /*
1199  *	checkloss(x) 	Routine to subtract hp from user and flag bottomline display
1200  *		int x;
1201  *
1202  *	Routine to subtract hitpoints from the user and flag the bottomline display
1203  *	Enter with the number of hit points to lose
1204  *	Note: if x > c[HP] this routine could kill the player!
1205  */
1206 checkloss(x)
1207 	int x;
1208 	{
1209 	if (x>0) { losehp(x);  bottomhp(); }
1210 	}
1211 
1212 /*
1213  *	annihilate() 	Routine to annihilate all monsters around player (playerx,playery)
1214  *
1215  *	Gives player experience, but no dropped objects
1216  *	Returns the experience gained from all monsters killed
1217  */
1218 annihilate()
1219 	{
1220 	int i,j;
1221 	register long k;
1222 	register char *p;
1223 	for (k=0, i=playerx-1; i<=playerx+1; i++)
1224 	  for (j=playery-1; j<=playery+1; j++)
1225 		if (!vxy(&i,&j)) /* if not out of bounds */
1226 			if (*(p= &mitem[i][j]))	/* if a monster there */
1227 				if (*p<DEMONLORD+2)
1228 					{
1229 					k += monster[*p].experience;	*p=know[i][j]=0;
1230 					}
1231 				else
1232 					{
1233 					lprintf("\nThe %s barely escapes being annihilated!",monster[*p].name);
1234 					hitp[i][j] = (hitp[i][j]>>1) + 1; /* lose half hit points*/
1235 					}
1236 	if (k>0)
1237 		{
1238 		lprcat("\nYou hear loud screams of agony!");	raiseexperience((long)k);
1239 		}
1240 	return(k);
1241 	}
1242 
1243 /*
1244  *	newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
1245  *		int x,y,dir,lifetime;
1246  *
1247  *	Enter with the coordinates of the sphere in x,y
1248  *	  the direction (0-8 diroffx format) in dir, and the lifespan of the
1249  *	  sphere in lifetime (in turns)
1250  *	Returns the number of spheres currently in existence
1251  */
1252 newsphere(x,y,dir,life)
1253 	int x,y,dir,life;
1254 	{
1255 	int m;
1256 	struct sphere *sp;
1257 	if (((sp=(struct sphere *)malloc(sizeof(struct sphere)))) == 0)
1258 		return(c[SPHCAST]);	/* can't malloc, therefore failure */
1259 	if (dir>=9) dir=0;	/* no movement if direction not found */
1260 	if (level==0) vxy(&x,&y);	/* don't go out of bounds */
1261 	else
1262 		{
1263 		if (x<1) x=1;  if (x>=MAXX-1) x=MAXX-2;
1264 		if (y<1) y=1;  if (y>=MAXY-1) y=MAXY-2;
1265 		}
1266 	if ((m=mitem[x][y]) >= DEMONLORD+4)	/* demons dispel spheres */
1267 		{
1268 		know[x][y]=1; show1cell(x,y);	/* show the demon (ha ha) */
1269 		cursors(); lprintf("\nThe %s dispels the sphere!",monster[m].name);
1270 		beep(); rmsphere(x,y);	/* remove any spheres that are here */
1271 		return(c[SPHCAST]);
1272 		}
1273 	if (m==DISENCHANTRESS) /* disenchantress cancels spheres */
1274 		{
1275 		cursors(); lprintf("\nThe %s causes cancellation of the sphere!",monster[m].name); beep();
1276 boom:	sphboom(x,y);	/* blow up stuff around sphere */
1277 		rmsphere(x,y);	/* remove any spheres that are here */
1278 		return(c[SPHCAST]);
1279 		}
1280 	if (c[CANCELLATION]) /* cancellation cancels spheres */
1281 		{
1282 		cursors(); lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!"); beep();
1283 		goto boom;
1284 		}
1285 	if (item[x][y]==OANNIHILATION) /* collision of spheres detonates spheres */
1286 		{
1287 		cursors(); lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!"); beep();
1288 		rmsphere(x,y);
1289 		goto boom;
1290 		}
1291 	if (playerx==x && playery==y) /* collision of sphere and player! */
1292 		{
1293 		cursors();
1294 		lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1295 		beep(); rmsphere(x,y);	/* remove any spheres that are here */
1296 		nap(4000);  died(258);
1297 		}
1298 	item[x][y]=OANNIHILATION;  mitem[x][y]=0;  know[x][y]=1;
1299 	show1cell(x,y);	/* show the new sphere */
1300 	sp->x=x;  sp->y=y;  sp->lev=level;  sp->dir=dir;  sp->lifetime=life;  sp->p=0;
1301 	if (spheres==0) spheres=sp;	/* if first node in the sphere list */
1302 	else	/* add sphere to beginning of linked list */
1303 		{
1304 		sp->p = spheres;	spheres = sp;
1305 		}
1306 	return(++c[SPHCAST]);	/* one more sphere in the world */
1307 	}
1308 
1309 /*
1310  *	rmsphere(x,y)		Function to delete a sphere of annihilation from list
1311  *		int x,y;
1312  *
1313  *	Enter with the coordinates of the sphere (on current level)
1314  *	Returns the number of spheres currently in existence
1315  */
1316 rmsphere(x,y)
1317 	int x,y;
1318 	{
1319 	register struct sphere *sp,*sp2=0;
1320 	for (sp=spheres; sp; sp2=sp,sp=sp->p)
1321 	  if (level==sp->lev)	/* is sphere on this level? */
1322 	    if ((x==sp->x) && (y==sp->y))	/* locate sphere at this location */
1323 			{
1324 			item[x][y]=mitem[x][y]=0;  know[x][y]=1;
1325 			show1cell(x,y);	/* show the now missing sphere */
1326 			--c[SPHCAST];
1327 			if (sp==spheres) { sp2=sp; spheres=sp->p; free((char*)sp2); }
1328 			else
1329 				{ sp2->p = sp->p;  free((char*)sp); }
1330 			break;
1331 			}
1332 	return(c[SPHCAST]);	/* return number of spheres in the world */
1333 	}
1334 
1335 /*
1336  *	sphboom(x,y)	Function to perform the effects of a sphere detonation
1337  *		int x,y;
1338  *
1339  *	Enter with the coordinates of the blast, Returns no value
1340  */
1341 sphboom(x,y)
1342 	int x,y;
1343 	{
1344 	register int i,j;
1345 	if (c[HOLDMONST]) c[HOLDMONST]=1;
1346 	if (c[CANCELLATION]) c[CANCELLATION]=1;
1347 	for (j=max(1,x-2); j<min(x+3,MAXX-1); j++)
1348 	  for (i=max(1,y-2); i<min(y+3,MAXY-1); i++)
1349 		{
1350 		item[j][i]=mitem[j][i]=0;
1351 		show1cell(j,i);
1352 		if (playerx==j && playery==i)
1353 			{
1354 			cursors(); beep();
1355 			lprcat("\nYou were too close to the sphere!");
1356 			nap(3000);
1357 			died(283); /* player killed in explosion */
1358 			}
1359 		}
1360 	}
1361 
1362 /*
1363  *	genmonst()		Function to ask for monster and genocide from game
1364  *
1365  *	This is done by setting a flag in the monster[] structure
1366  */
1367 genmonst()
1368 	{
1369 	register int i,j;
1370 	cursors();  lprcat("\nGenocide what monster? ");
1371 	for (i=0; (!isalpha(i)) && (i!=' '); i=getchar());
1372 	lprc(i);
1373 	for (j=0; j<MAXMONST; j++)	/* search for the monster type */
1374 		if (monstnamelist[j]==i)	/* have we found it? */
1375 			{
1376 			monster[j].genocided=1;	/* genocided from game */
1377 			lprintf("  There will be no more %s's",monster[j].name);
1378 			/* now wipe out monsters on this level */
1379 			newcavelevel(level); draws(0,MAXX,0,MAXY); bot_linex();
1380 			return;
1381 			}
1382 	lprcat("  You sense failure!");
1383 	}
1384 
1385