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