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