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