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