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