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