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