monster.c revision 1.5
1/*	$NetBSD: monster.c,v 1.5 1997/10/18 20:03:29 christos Exp $	*/
2
3/*
4 * monster.c	Larn is copyrighted 1986 by Noah Morgan.
5 *
6 * This file contains the following functions:
7 * ----------------------------------------------------------------------------
8 *
9 * createmonster(monstno) 	Function to create a monster next to the player
10 * int monstno;
11 *
12 * int cgood(x,y,itm,monst)Function to check location for emptiness
13 * int x,y,itm,monst;
14 *
15 * createitem(it,arg) 		Routine to place an item next to the player
16 * int it,arg;
17 *
18 * cast() 			Subroutine called by parse to cast a spell for the user
19 *
20 * speldamage(x) 	Function to perform spell functions cast by the player
21 * int x;
22 *
23 * loseint()		Routine to decrement your int (intelligence) if > 3
24 *
25 * isconfuse() 	Routine to check to see if player is confused
26 *
27 * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster
28 * int x,monst;
29 *
30 * fullhit(xx)		Function to return full damage against a monst (aka web)
31 * int xx;
32 *
33 * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir
34 * int spnum,dam,arg;
35 * char *str;
36 *
37 * godirect(spnum,dam,str,delay,cshow)	Function to perform missile attacks
38 * int spnum,dam,delay;
39 * char *str,cshow;
40 *
41 * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt
42 * int x,y;
43 *
44 * tdirect(spnum)		Routine to teleport away a monster
45 * int spnum;
46 *
47 * omnidirect(sp,dam,str)  Routine to damage all monsters 1 square from player
48 * int sp,dam;
49 * char *str;
50 *
51 * dirsub(x,y)		Routine to ask for direction, then modify x,y for it
52 * int *x,*y;
53 *
54 * vxy(x,y)	  	Routine to verify/fix (*x,*y) for being within bounds
55 * int *x,*y;
56 *
57 * dirpoly(spnum)	Routine to ask for a direction and polymorph a monst
58 * int spnum;
59 *
60 * hitmonster(x,y) Function to hit a monster at the designated coordinates
61 * int x,y;
62 *
63 * hitm(x,y,amt)	Function to just hit a monster at a given coordinates
64 * int x,y,amt;
65 *
66 * hitplayer(x,y) 	Function for the monster to hit the player from (x,y)
67 * int x,y;
68 *
69 * dropsomething(monst) Function to create an object when a monster dies
70 * int monst;
71 *
72 * dropgold(amount) 	Function to drop some gold around player
73 * int amount;
74 *
75 * something(level) 	Function to create a random item around player
76 * int level;
77 *
78 * newobject(lev,i) 	Routine to return a randomly selected new object
79 * int lev,*i;
80 *
81 *  spattack(atckno,xx,yy)  Function to process special attacks from monsters
82 *   int atckno,xx,yy;
83 *
84 * checkloss(x) Routine to subtract hp from user and flag bottomline display
85 * int x;
86 *
87 * annihilate()   Routine to annihilate monsters around player, playerx,playery
88 *
89 * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
90 * int x,y,dir,lifetime;
91 *
92 * rmsphere(x,y)	Function to delete a sphere of annihilation from list
93 * int x,y;
94 *
95 * sphboom(x,y)	Function to perform the effects of a sphere detonation
96 * int x,y;
97 *
98 * genmonst()		Function to ask for monster and genocide from game
99 *
100 */
101#include <sys/cdefs.h>
102#ifndef lint
103__RCSID("$NetBSD: monster.c,v 1.5 1997/10/18 20:03:29 christos 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 __P((int *, int *));
118/*
119 * createmonster(monstno)	Function to create a monster next to the player
120 * 	int monstno;
121 *
122 * Enter with the monster number (1 to MAXMONST+8)
123 * Returns no value.
124 */
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(%d)\n", (long) mon);
134		nap(3000);
135		return;
136	}
137	while (monster[mon].genocided && mon < MAXMONST)
138		mon++;		/* genocided? */
139	for (k = rnd(8), i = -8; i < 0; i++, k++) {	/* choose direction,
140							 * then try all */
141		if (k > 8)
142			k = 1;	/* wraparound the diroff arrays */
143		x = playerx + diroffx[k];
144		y = playery + diroffy[k];
145		if (cgood(x, y, 0, 1)) {	/* if we can create here */
146			mitem[x][y] = mon;
147			hitp[x][y] = monster[mon].hitpoints;
148			stealth[x][y] = know[x][y] = 0;
149			switch (mon) {
150			case ROTHE:
151			case POLTERGEIST:
152			case VAMPIRE:
153				stealth[x][y] = 1;
154			};
155			return;
156		}
157	}
158}
159
160/*
161 * int cgood(x,y,itm,monst)	  Function to check location for emptiness
162 * 	int x,y,itm,monst;
163 *
164 * Routine to return TRUE if a location does not have itm or monst there
165 * returns FALSE (0) otherwise
166 * Enter with itm or monst TRUE or FALSE if checking it
167 * Example:  if itm==TRUE check for no item at this location
168 * 		  if monst==TRUE check for no monster at this location
169 * This routine will return FALSE if at a wall or the dungeon exit on level 1
170 */
171int
172cgood(x, y, itm, monst)
173	int    x, y;
174	int             itm, monst;
175{
176	if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
177		/* within bounds? */
178		if (item[x][y] != OWALL) /* can't make anything on walls */
179			/* is it free of items? */
180			if (itm == 0 || (item[x][y] == 0))
181				/* is it free of monsters? */
182				if (monst == 0 || (mitem[x][y] == 0))
183					if ((level != 1) || (x != 33) ||
184					    (y != MAXY - 1))
185						/* not exit to level 1 */
186						return (1);
187	return (0);
188}
189
190/*
191 * createitem(it,arg) 	Routine to place an item next to the player
192 * 	int it,arg;
193 *
194 * Enter with the item number and its argument (iven[], ivenarg[])
195 * Returns no value, thus we don't know about createitem() failures.
196 */
197void
198createitem(it, arg)
199	int             it, arg;
200{
201	int    x, y, k, i;
202	if (it >= MAXOBJ)
203		return;		/* no such object */
204	for (k = rnd(8), i = -8; i < 0; i++, k++) {	/* choose direction,
205							 * then try all */
206		if (k > 8)
207			k = 1;	/* wraparound the diroff arrays */
208		x = playerx + diroffx[k];
209		y = playery + diroffy[k];
210		if (cgood(x, y, 1, 0)) {	/* if we can create here */
211			item[x][y] = it;
212			know[x][y] = 0;
213			iarg[x][y] = arg;
214			return;
215		}
216	}
217}
218
219/*
220 * cast() 		Subroutine called by parse to cast a spell for the user
221 *
222 * No arguments and no return value.
223 */
224static char     eys[] = "\nEnter your spell: ";
225void
226cast()
227{
228	int    i, j, a, b, d;
229	cursors();
230	if (c[SPELLS] <= 0) {
231		lprcat("\nYou don't have any spells!");
232		return;
233	}
234	lprcat(eys);
235	--c[SPELLS];
236	while ((a = getchar()) == 'D') {
237		seemagic(-1);
238		cursors();
239		lprcat(eys);
240	}
241	if (a == '\33')
242		goto over;	/* to escape casting a spell	 */
243	if ((b = getchar()) == '\33')
244		goto over;	/* to escape casting a spell	 */
245	if ((d = getchar()) == '\33') {
246over:		lprcat(aborted);
247		c[SPELLS]++;
248		return;
249	}			/* to escape casting a spell	 */
250#ifdef EXTRA
251	c[SPELLSCAST]++;
252#endif
253	for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++)	/* seq search for his
254							 * spell, hash? */
255		if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
256			if (spelknow[i]) {
257				speldamage(i);
258				j = 1;
259				i = SPNUM;
260			}
261	if (j == -1)
262		lprcat("  Nothing Happened ");
263	bottomline();
264}
265
266/*
267 * speldamage(x) 		Function to perform spell functions cast by the player
268 * 	int x;
269 *
270 * Enter with the spell number, returns no value.
271 * Please insure that there are 2 spaces before all messages here
272 */
273void
274speldamage(x)
275	int             x;
276{
277	int    i, j, clev;
278	int             xl, xh, yl, yh;
279	char  *p, *kn, *pm;
280	if (x >= SPNUM)
281		return;		/* no such spell */
282	if (c[TIMESTOP]) {
283		lprcat("  It didn't seem to work");
284		return;
285	}			/* not if time stopped */
286	clev = c[LEVEL];
287	if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
288		lprcat("  It didn't work!");
289		return;
290	}
291	if (clev * 3 + 2 < x) {
292		lprcat("  Nothing happens.  You seem inexperienced at this");
293		return;
294	}
295	switch (x) {
296		/* ----- LEVEL 1 SPELLS ----- */
297
298	case 0:
299		if (c[PROTECTIONTIME] == 0)
300			c[MOREDEFENSES] += 2;	/* protection field +2 */
301		c[PROTECTIONTIME] += 250;
302		return;
303
304	case 1:
305		i = rnd(((clev + 1) << 1)) + clev + 3;
306		godirect(x, i, (clev >= 2) ? "  Your missiles hit the %s" : "  Your missile hit the %s", 100, '+');	/* magic missile */
307
308		return;
309
310	case 2:
311		if (c[DEXCOUNT] == 0)
312			c[DEXTERITY] += 3;	/* dexterity	 */
313		c[DEXCOUNT] += 400;
314		return;
315
316	case 3:
317		i = rnd(3) + 1;
318		p = "  While the %s slept, you smashed it %d times";
319ws:		direct(x, fullhit(i), p, i);	/* sleep	 */
320		return;
321
322	case 4:		/* charm monster	 */
323		c[CHARMCOUNT] += c[CHARISMA] << 1;
324		return;
325
326	case 5:
327		godirect(x, rnd(10) + 15 + clev, "  The sound damages the %s", 70, '@');	/* sonic spear */
328		return;
329
330		/* ----- LEVEL 2 SPELLS ----- */
331
332	case 6:
333		i = rnd(3) + 2;
334		p = "  While the %s is entangled, you hit %d times";
335		goto ws;	/* web */
336
337	case 7:
338		if (c[STRCOUNT] == 0)
339			c[STREXTRA] += 3;	/* strength	 */
340		c[STRCOUNT] += 150 + rnd(100);
341		return;
342
343	case 8:
344		yl = playery - 5;	/* enlightenment */
345		yh = playery + 6;
346		xl = playerx - 15;
347		xh = playerx + 16;
348		vxy(&xl, &yl);
349		vxy(&xh, &yh);	/* check bounds */
350		for (i = yl; i <= yh; i++)	/* enlightenment	 */
351			for (j = xl; j <= xh; j++)
352				know[j][i] = 1;
353		draws(xl, xh + 1, yl, yh + 1);
354		return;
355
356	case 9:
357		raisehp(20 + (clev << 1));
358		return;		/* healing */
359
360	case 10:
361		c[BLINDCOUNT] = 0;
362		return;		/* cure blindness	 */
363
364	case 11:
365		createmonster(makemonst(level + 1) + 8);
366		return;
367
368	case 12:
369		if (rnd(11) + 7 <= c[WISDOM])
370			direct(x, rnd(20) + 20 + clev, "  The %s believed!", 0);
371		else
372			lprcat("  It didn't believe the illusions!");
373		return;
374
375	case 13:		/* if he has the amulet of invisibility then
376				 * add more time */
377		for (j = i = 0; i < 26; i++)
378			if (iven[i] == OAMULET)
379				j += 1 + ivenarg[i];
380		c[INVISIBILITY] += (j << 7) + 12;
381		return;
382
383		/* ----- LEVEL 3 SPELLS ----- */
384
385	case 14:
386		godirect(x, rnd(25 + clev) + 25 + clev, "  The fireball hits the %s", 40, '*');
387		return;		/* fireball */
388
389	case 15:
390		godirect(x, rnd(25) + 20 + clev, "  Your cone of cold strikes the %s", 60, 'O');	/* cold */
391		return;
392
393	case 16:
394		dirpoly(x);
395		return;		/* polymorph */
396
397	case 17:
398		c[CANCELLATION] += 5 + clev;
399		return;		/* cancellation	 */
400
401	case 18:
402		c[HASTESELF] += 7 + clev;
403		return;		/* haste self	 */
404
405	case 19:
406		omnidirect(x, 30 + rnd(10), "  The %s gasps for air");	/* cloud kill */
407		return;
408
409	case 20:
410		xh = min(playerx + 1, MAXX - 2);
411		yh = min(playery + 1, MAXY - 2);
412		for (i = max(playerx - 1, 1); i <= xh; i++)	/* vaporize rock */
413			for (j = max(playery - 1, 1); j <= yh; j++) {
414				kn = &know[i][j];
415				pm = &mitem[i][j];
416				switch (*(p = &item[i][j])) {
417				case OWALL:
418					if (level < MAXLEVEL + MAXVLEVEL - 1)
419						*p = *kn = 0;
420					break;
421
422				case OSTATUE:
423					if (c[HARDGAME] < 3) {
424						*p = OBOOK;
425						iarg[i][j] = level;
426						*kn = 0;
427					}
428					break;
429
430				case OTHRONE:
431					*pm = GNOMEKING;
432					*kn = 0;
433					*p = OTHRONE2;
434					hitp[i][j] = monster[GNOMEKING].hitpoints;
435					break;
436
437				case OALTAR:
438					*pm = DEMONPRINCE;
439					*kn = 0;
440					hitp[i][j] = monster[DEMONPRINCE].hitpoints;
441					break;
442				};
443				switch (*pm) {
444				case XORN:
445					ifblind(i, j);
446					hitm(i, j, 200);
447					break;	/* Xorn takes damage from vpr */
448				}
449			}
450		return;
451
452		/* ----- LEVEL 4 SPELLS ----- */
453
454	case 21:
455		direct(x, 100 + clev, "  The %s shrivels up", 0);	/* dehydration */
456		return;
457
458	case 22:
459		godirect(x, rnd(25) + 20 + (clev << 1), "  A lightning bolt hits the %s", 1, '~');	/* lightning */
460		return;
461
462	case 23:
463		i = min(c[HP] - 1, c[HPMAX] / 2);	/* drain life */
464		direct(x, i + i, "", 0);
465		c[HP] -= i;
466		return;
467
468	case 24:
469		if (c[GLOBE] == 0)
470			c[MOREDEFENSES] += 10;
471		c[GLOBE] += 200;
472		loseint();	/* globe of invulnerability */
473		return;
474
475	case 25:
476		omnidirect(x, 32 + clev, "  The %s struggles for air in your flood!");	/* flood */
477		return;
478
479	case 26:
480		if (rnd(151) == 63) {
481			beep();
482			lprcat("\nYour heart stopped!\n");
483			nap(4000);
484			died(270);
485			return;
486		}
487		if (c[WISDOM] > rnd(10) + 10)
488			direct(x, 2000, "  The %s's heart stopped", 0);	/* finger of death */
489		else
490			lprcat("  It didn't work");
491		return;
492
493		/* ----- LEVEL 5 SPELLS ----- */
494
495	case 27:
496		c[SCAREMONST] += rnd(10) + clev;
497		return;		/* scare monster */
498
499	case 28:
500		c[HOLDMONST] += rnd(10) + clev;
501		return;		/* hold monster */
502
503	case 29:
504		c[TIMESTOP] += rnd(20) + (clev << 1);
505		return;		/* time stop */
506
507	case 30:
508		tdirect(x);
509		return;		/* teleport away */
510
511	case 31:
512		omnidirect(x, 35 + rnd(10) + clev, "  The %s cringes from the flame");	/* magic fire */
513		return;
514
515		/* ----- LEVEL 6 SPELLS ----- */
516
517	case 32:
518		if ((rnd(23) == 5) && (wizard == 0)) {	/* sphere of
519							 * annihilation */
520			beep();
521			lprcat("\nYou have been enveloped by the zone of nothingness!\n");
522			nap(4000);
523			died(258);
524			return;
525		}
526		xl = playerx;
527		yl = playery;
528		loseint();
529		i = dirsub(&xl, &yl);	/* get direction of sphere */
530		newsphere(xl, yl, i, rnd(20) + 11);	/* make a sphere */
531		return;
532
533	case 33:
534		genmonst();
535		spelknow[33] = 0;	/* genocide */
536		loseint();
537		return;
538
539	case 34:		/* summon demon */
540		if (rnd(100) > 30) {
541			direct(x, 150, "  The demon strikes at the %s", 0);
542			return;
543		}
544		if (rnd(100) > 15) {
545			lprcat("  Nothing seems to have happened");
546			return;
547		}
548		lprcat("  The demon turned on you and vanished!");
549		beep();
550		i = rnd(40) + 30;
551		lastnum = 277;
552		losehp(i);	/* must say killed by a demon */
553		return;
554
555	case 35:		/* walk through walls */
556		c[WTW] += rnd(10) + 5;
557		return;
558
559	case 36:		/* alter reality */
560		{
561			struct isave   *save;	/* pointer to item save
562						 * structure */
563			int             sc;
564			sc = 0;	/* # items saved */
565			save = (struct isave *) malloc(sizeof(struct isave) * MAXX * MAXY * 2);
566			for (j = 0; j < MAXY; j++)
567				for (i = 0; i < MAXX; i++) {	/* save all items and
568								 * monsters */
569					xl = item[i][j];
570					if (xl && xl != OWALL && xl != OANNIHILATION) {
571						save[sc].type = 0;
572						save[sc].id = item[i][j];
573						save[sc++].arg = iarg[i][j];
574					}
575					if (mitem[i][j]) {
576						save[sc].type = 1;
577						save[sc].id = mitem[i][j];
578						save[sc++].arg = hitp[i][j];
579					}
580					item[i][j] = OWALL;
581					mitem[i][j] = 0;
582					if (wizard)
583						know[i][j] = 1;
584					else
585						know[i][j] = 0;
586				}
587			eat(1, 1);
588			if (level == 1)
589				item[33][MAXY - 1] = 0;
590			for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
591				item[i][j] = 0;
592			while (sc > 0) {	/* put objects back in level */
593				--sc;
594				if (save[sc].type == 0) {
595					int             trys;
596					for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1));
597					if (trys) {
598						item[i][j] = save[sc].id;
599						iarg[i][j] = save[sc].arg;
600					}
601				} else {	/* put monsters back in */
602					int             trys;
603					for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1));
604					if (trys) {
605						mitem[i][j] = save[sc].id;
606						hitp[i][j] = save[sc].arg;
607					}
608				}
609			}
610			loseint();
611			draws(0, MAXX, 0, MAXY);
612			if (wizard == 0)
613				spelknow[36] = 0;
614			free((char *) save);
615			positionplayer();
616			return;
617		}
618
619	case 37:		/* permanence */
620		adjusttime(-99999L);
621		spelknow[37] = 0;	/* forget */
622		loseint();
623		return;
624
625	default:
626		lprintf("  spell %d not available!", (long) x);
627		beep();
628		return;
629	};
630}
631
632/*
633 * loseint()		Routine to subtract 1 from your int (intelligence) if > 3
634 *
635 * No arguments and no return value
636 */
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	char           *str;
717{
718	int             x, y;
719	int    m;
720	if (spnum < 0 || spnum >= SPNUM || str == 0)
721		return;		/* bad arguments */
722	if (isconfuse())
723		return;
724	dirsub(&x, &y);
725	m = mitem[x][y];
726	if (item[x][y] == OMIRROR) {
727		if (spnum == 3) {	/* sleep */
728			lprcat("You fall asleep! ");
729			beep();
730	fool:
731			arg += 2;
732			while (arg-- > 0) {
733				parse2();
734				nap(1000);
735			}
736			return;
737		} else if (spnum == 6) {	/* web */
738			lprcat("You get stuck in your own web! ");
739			beep();
740			goto fool;
741		} else {
742			lastnum = 278;
743			lprintf(str, "spell caster (thats you)", (long) arg);
744			beep();
745			losehp(dam);
746			return;
747		}
748	}
749	if (m == 0) {
750		lprcat("  There wasn't anything there!");
751		return;
752	}
753	ifblind(x, y);
754	if (nospell(spnum, m)) {
755		lasthx = x;
756		lasthy = y;
757		return;
758	}
759	lprintf(str, lastmonst, (long) arg);
760	hitm(x, y, dam);
761}
762
763/*
764 * godirect(spnum,dam,str,delay,cshow)		Function to perform missile attacks
765 * 	int spnum,dam,delay;
766 * 	char *str,cshow;
767 *
768 * Function to hit in a direction from a missile weapon and have it keep
769 * on going in that direction until its power is exhausted
770 * Enter with the spell number in spnum, the power of the weapon in hp,
771 *   lprintf format string in str, the # of milliseconds to delay between
772 *   locations in delay, and the character to represent the weapon in cshow.
773 * Returns no value.
774 */
775void
776godirect(spnum, dam, str, delay, cshow)
777	int             spnum, dam, delay;
778	char           *str, cshow;
779{
780	char  *p;
781	int    x, y, m;
782	int             dx, dy;
783	if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0)
784		return;		/* bad args */
785	if (isconfuse())
786		return;
787	dirsub(&dx, &dy);
788	x = dx;
789	y = dy;
790	dx = x - playerx;
791	dy = y - playery;
792	x = playerx;
793	y = playery;
794	while (dam > 0) {
795		x += dx;
796		y += dy;
797		if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
798			dam = 0;
799			break;	/* out of bounds */
800		}
801		if ((x == playerx) && (y == playery)) {	/* if energy hits player */
802			cursors();
803			lprcat("\nYou are hit my your own magic!");
804			beep();
805			lastnum = 278;
806			losehp(dam);
807			return;
808		}
809		if (c[BLINDCOUNT] == 0) {	/* if not blind show effect */
810			cursor(x + 1, y + 1);
811			lprc(cshow);
812			nap(delay);
813			show1cell(x, y);
814		}
815		if ((m = mitem[x][y])) {	/* is there a monster there? */
816			ifblind(x, y);
817			if (nospell(spnum, m)) {
818				lasthx = x;
819				lasthy = y;
820				return;
821			}
822			cursors();
823			lprc('\n');
824			lprintf(str, lastmonst);
825			dam -= hitm(x, y, dam);
826			show1cell(x, y);
827			nap(1000);
828			x -= dx;
829			y -= dy;
830		} else
831			switch (*(p = &item[x][y])) {
832			case OWALL:
833				cursors();
834				lprc('\n');
835				lprintf(str, "wall");
836				if (dam >= 50 + c[HARDGAME])	/* enough damage? */
837					if (level < MAXLEVEL + MAXVLEVEL - 1)	/* not on V3 */
838						if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
839							lprcat("  The wall crumbles");
840					god3:		*p = 0;
841					god:		know[x][y] = 0;
842							show1cell(x, y);
843						}
844		god2:		dam = 0;
845				break;
846
847			case OCLOSEDDOOR:
848				cursors();
849				lprc('\n');
850				lprintf(str, "door");
851				if (dam >= 40) {
852					lprcat("  The door is blasted apart");
853					goto god3;
854				}
855				goto god2;
856
857			case OSTATUE:
858				cursors();
859				lprc('\n');
860				lprintf(str, "statue");
861				if (c[HARDGAME] < 3)
862					if (dam > 44) {
863						lprcat("  The statue crumbles");
864						*p = OBOOK;
865						iarg[x][y] = level;
866						goto god;
867					}
868				goto god2;
869
870			case OTHRONE:
871				cursors();
872				lprc('\n');
873				lprintf(str, "throne");
874				if (dam > 39) {
875					mitem[x][y] = GNOMEKING;
876					hitp[x][y] = monster[GNOMEKING].hitpoints;
877					*p = OTHRONE2;
878					goto god;
879				}
880				goto god2;
881
882			case OMIRROR:
883				dx *= -1;
884				dy *= -1;
885				break;
886			};
887		dam -= 3 + (c[HARDGAME] >> 1);
888	}
889}
890
891/*
892 * ifblind(x,y)	Routine to put "monster" or the monster name into lastmosnt
893 * 	int x,y;
894 *
895 * Subroutine to copy the word "monster" into lastmonst if the player is blind
896 * Enter with the coordinates (x,y) of the monster
897 * Returns no value.
898 */
899void
900ifblind(x, y)
901	int             x, y;
902{
903	char           *p;
904	vxy(&x, &y);		/* verify correct x,y coordinates */
905	if (c[BLINDCOUNT]) {
906		lastnum = 279;
907		p = "monster";
908	} else {
909		lastnum = mitem[x][y];
910		p = monster[lastnum].name;
911	}
912	strcpy(lastmonst, p);
913}
914
915/*
916 * tdirect(spnum)		Routine to teleport away a monster
917 * 	int spnum;
918 *
919 * Routine to ask for a direction to a spell and then teleport away monster
920 * Enter with the spell number that wants to teleport away
921 * Returns no value.
922 */
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(spnum, dam, str)
960	int             spnum, dam;
961	char           *str;
962{
963	int    x, y, m;
964	if (spnum < 0 || spnum >= SPNUM || str == 0)
965		return;		/* bad args */
966	for (x = playerx - 1; x < playerx + 2; x++)
967		for (y = playery - 1; y < playery + 2; y++) {
968			if ((m = mitem[x][y]) != 0)
969				if (nospell(spnum, m) == 0) {
970					ifblind(x, y);
971					cursors();
972					lprc('\n');
973					lprintf(str, lastmonst);
974					hitm(x, y, dam);
975					nap(800);
976				} else {
977					lasthx = x;
978					lasthy = y;
979				}
980		}
981}
982
983/*
984 * 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 (getchar()) {
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 naga's and poltergeist's 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(level)
1340	int             level;
1341{
1342	int    j;
1343	int             i;
1344	if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
1345		return;		/* correct level? */
1346	if (rnd(101) < 8)
1347		something(level);	/* possibly more than one item */
1348	j = newobject(level, &i);
1349	createitem(j, i);
1350}
1351
1352/*
1353 * newobject(lev,i) 	Routine to return a randomly selected new object
1354 * 	int lev,*i;
1355 *
1356 * Routine to return a randomly selected object to be created
1357 * Returns the object number created, and sets *i for its argument
1358 * Enter with the cave level and a pointer to the items arg
1359 */
1360static char     nobjtab[] = {
1361	0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
1362	OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1363	OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
1364	OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
1365	OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
1366	OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1367	OLONGSWORD};
1368
1369int
1370newobject(lev, i)
1371	int    lev, *i;
1372{
1373	int    tmp = 32, j;
1374	if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
1375		return (0);	/* correct level? */
1376	if (lev > 6)
1377		tmp = 37;
1378	else if (lev > 4)
1379		tmp = 35;
1380	j = nobjtab[tmp = rnd(tmp)];	/* the object type */
1381	switch (tmp) {
1382	case 1:
1383	case 2:
1384	case 3:
1385	case 4:
1386		*i = newscroll();
1387		break;
1388	case 5:
1389	case 6:
1390	case 7:
1391	case 8:
1392		*i = newpotion();
1393		break;
1394	case 9:
1395	case 10:
1396	case 11:
1397	case 12:
1398		*i = rnd((lev + 1) * 10) + lev * 10 + 10;
1399		break;
1400	case 13:
1401	case 14:
1402	case 15:
1403	case 16:
1404		*i = lev;
1405		break;
1406	case 17:
1407	case 18:
1408	case 19:
1409		if (!(*i = newdagger()))
1410			return (0);
1411		break;
1412	case 20:
1413	case 21:
1414	case 22:
1415		if (!(*i = newleather()))
1416			return (0);
1417		break;
1418	case 23:
1419	case 32:
1420	case 35:
1421		*i = rund(lev / 3 + 1);
1422		break;
1423	case 24:
1424	case 26:
1425		*i = rnd(lev / 4 + 1);
1426		break;
1427	case 25:
1428		*i = rund(lev / 4 + 1);
1429		break;
1430	case 27:
1431		*i = rnd(lev / 2 + 1);
1432		break;
1433	case 30:
1434	case 33:
1435		*i = rund(lev / 2 + 1);
1436		break;
1437	case 28:
1438		*i = rund(lev / 3 + 1);
1439		if (*i == 0)
1440			return (0);
1441		break;
1442	case 29:
1443	case 31:
1444		*i = rund(lev / 2 + 1);
1445		if (*i == 0)
1446			return (0);
1447		break;
1448	case 34:
1449		*i = newchain();
1450		break;
1451	case 36:
1452		*i = newplate();
1453		break;
1454	case 37:
1455		*i = newsword();
1456		break;
1457	}
1458	return (j);
1459}
1460
1461/*
1462 *  spattack(atckno,xx,yy) Function to process special attacks from monsters
1463 *  	int atckno,xx,yy;
1464 *
1465 * Enter with the special attack number, and the coordinates (xx,yy)
1466 * 	of the monster that is special attacking
1467 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1468 *
1469 * atckno   monster     effect
1470 * ---------------------------------------------------
1471 * 0	none
1472 * 1	rust monster	eat armor
1473 * 2	hell hound	breathe light fire
1474 * 3	dragon		breathe fire
1475 * 4	giant centipede	weakening sing
1476 * 5	white dragon	cold breath
1477 * 6	wraith		drain level
1478 * 7	waterlord	water gusher
1479 * 8	leprechaun	steal gold
1480 * 9	disenchantress	disenchant weapon or armor
1481 * 10	ice lizard	hits with barbed tail
1482 * 11	umber hulk	confusion
1483 * 12	spirit naga	cast spells	taken from special attacks
1484 * 13	platinum dragon	psionics
1485 * 14	nymph		steal objects
1486 * 15	bugbear		bite
1487 * 16	osequip		bite
1488 *
1489 * char rustarm[ARMORTYPES][2];
1490 * special array for maximum rust damage to armor from rustmonster
1491 * format is: { armor type , minimum attribute
1492 */
1493#define ARMORTYPES 6
1494static char     rustarm[ARMORTYPES][2] = {
1495	{ OSTUDLEATHER, -2 },
1496	{ ORING, -4 },
1497	{ OCHAIN, -5 },
1498	{ OSPLINT, -6 },
1499	{ OPLATE, -8 },
1500	{ OPLATEARMOR, -9}
1501};
1502static char     spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
1503int
1504spattack(x, xx, yy)
1505	int             x, xx, yy;
1506{
1507	int    i, j = 0, k, m;
1508	char  *p = 0;
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		if ((j == 0) && (k != -1)) {
1521			m = iven[k];
1522			for (i = 0; i < ARMORTYPES; i++)
1523				/* find his armor in table */
1524				if (m == rustarm[i][0]) {
1525					if (--ivenarg[k] < rustarm[i][1])
1526						ivenarg[k] = rustarm[i][1];
1527					else
1528						j = 1;
1529					break;
1530				}
1531		}
1532		if (j == 0)	/* if rusting did not occur */
1533			switch (m) {
1534			case OLEATHER:
1535				p = "\nThe %s hit you -- Your lucky you have leather on";
1536				break;
1537			case OSSPLATE:
1538				p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1539				break;
1540			}
1541		else {
1542			beep();
1543			p = "\nThe %s hit you -- your armor feels weaker";
1544		}
1545		break;
1546
1547	case 2:
1548		i = rnd(15) + 8 - c[AC];
1549spout:		p = "\nThe %s breathes fire at you!";
1550		if (c[FIRERESISTANCE])
1551			p = "\nThe %s's flame doesn't phase you!";
1552		else
1553spout2:	if (p) {
1554			lprintf(p, lastmonst);
1555			beep();
1556		}
1557		checkloss(i);
1558		return (0);
1559
1560	case 3:
1561		i = rnd(20) + 25 - c[AC];
1562		goto spout;
1563
1564	case 4:
1565		if (c[STRENGTH] > 3) {
1566			p = "\nThe %s stung you!  You feel weaker";
1567			beep();
1568			--c[STRENGTH];
1569		} else
1570			p = "\nThe %s stung you!";
1571		break;
1572
1573	case 5:
1574		p = "\nThe %s blasts you with his cold breath";
1575		i = rnd(15) + 18 - c[AC];
1576		goto spout2;
1577
1578	case 6:
1579		lprintf("\nThe %s drains you of your life energy!", lastmonst);
1580		loselevel();
1581		beep();
1582		return (0);
1583
1584	case 7:
1585		p = "\nThe %s got you with a gusher!";
1586		i = rnd(15) + 25 - c[AC];
1587		goto spout2;
1588
1589	case 8:
1590		if (c[NOTHEFT])
1591			return (0);	/* he has a device of no theft */
1592		if (c[GOLD]) {
1593			p = "\nThe %s hit you -- Your purse feels lighter";
1594			if (c[GOLD] > 32767)
1595				c[GOLD] >>= 1;
1596			else
1597				c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
1598			if (c[GOLD] < 0)
1599				c[GOLD] = 0;
1600		} else
1601			p = "\nThe %s couldn't find any gold to steal";
1602		lprintf(p, lastmonst);
1603		disappear(xx, yy);
1604		beep();
1605		bottomgold();
1606		return (1);
1607
1608	case 9:
1609		for (j = 50;;) {/* disenchant */
1610			i = rund(26);
1611			m = iven[i];	/* randomly select item */
1612			if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1613				if ((ivenarg[i] -= 3) < 0)
1614					ivenarg[i] = 0;
1615				lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1616				srcount = 0;
1617				beep();
1618				show3(i);
1619				bottomline();
1620				return (0);
1621			}
1622			if (--j <= 0) {
1623				p = "\nThe %s nearly misses";
1624				break;
1625			}
1626			break;
1627		}
1628		break;
1629
1630	case 10:
1631		p = "\nThe %s hit you with his barbed tail";
1632		i = rnd(25) - c[AC];
1633		goto spout2;
1634
1635	case 11:
1636		p = "\nThe %s has confused you";
1637		beep();
1638		c[CONFUSE] += 10 + rnd(10);
1639		break;
1640
1641	case 12:		/* performs any number of other special
1642				 * attacks	 */
1643		return (spattack(spsel[rund(10)], xx, yy));
1644
1645	case 13:
1646		p = "\nThe %s flattens you with his psionics!";
1647		i = rnd(15) + 30 - c[AC];
1648		goto spout2;
1649
1650	case 14:
1651		if (c[NOTHEFT])
1652			return (0);	/* he has device of no theft */
1653		if (emptyhanded() == 1) {
1654			p = "\nThe %s couldn't find anything to steal";
1655			break;
1656		}
1657		lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1658		beep();
1659		if (stealsomething() == 0)
1660			lprcat("  nothing");
1661		disappear(xx, yy);
1662		bottomline();
1663		return (1);
1664
1665	case 15:
1666		i = rnd(10) + 5 - c[AC];
1667spout3:	p = "\nThe %s bit you!";
1668		goto spout2;
1669
1670	case 16:
1671		i = rnd(15) + 10 - c[AC];
1672		goto spout3;
1673	};
1674	if (p) {
1675		lprintf(p, lastmonst);
1676		bottomline();
1677	}
1678	return (0);
1679}
1680
1681/*
1682 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1683 * 	int x;
1684 *
1685 * Routine to subtract hitpoints from the user and flag the bottomline display
1686 * Enter with the number of hit points to lose
1687 * Note: if x > c[HP] this routine could kill the player!
1688 */
1689void
1690checkloss(x)
1691	int             x;
1692{
1693	if (x > 0) {
1694		losehp(x);
1695		bottomhp();
1696	}
1697}
1698
1699/*
1700 * annihilate() 	Routine to annihilate all monsters around player (playerx,playery)
1701 *
1702 * Gives player experience, but no dropped objects
1703 * Returns the experience gained from all monsters killed
1704 */
1705int
1706annihilate()
1707{
1708	int             i, j;
1709	long   k;
1710	u_char  *p;
1711	for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1712		for (j = playery - 1; j <= playery + 1; j++)
1713			if (!vxy(&i, &j))	/* if not out of bounds */
1714				if (*(p = &mitem[i][j]))	/* if a monster there */
1715					if (*p < DEMONLORD + 2) {
1716						k += monster[*p].experience;
1717						*p = know[i][j] = 0;
1718					} else {
1719						lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
1720						hitp[i][j] = (hitp[i][j] >> 1) + 1;	/* lose half hit points */
1721					}
1722	if (k > 0) {
1723		lprcat("\nYou hear loud screams of agony!");
1724		raiseexperience((long) k);
1725	}
1726	return (k);
1727}
1728
1729/*
1730 * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
1731 * 	int x,y,dir,lifetime;
1732 *
1733 * Enter with the coordinates of the sphere in x,y
1734 *   the direction (0-8 diroffx format) in dir, and the lifespan of the
1735 *   sphere in lifetime (in turns)
1736 * Returns the number of spheres currently in existence
1737 */
1738int
1739newsphere(x, y, dir, life)
1740	int             x, y, dir, life;
1741{
1742	int             m;
1743	struct sphere  *sp;
1744	if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
1745		return (c[SPHCAST]);	/* can't malloc, therefore failure */
1746	if (dir >= 9)
1747		dir = 0;	/* no movement if direction not found */
1748	if (level == 0)
1749		vxy(&x, &y);	/* don't go out of bounds */
1750	else {
1751		if (x < 1)
1752			x = 1;
1753		if (x >= MAXX - 1)
1754			x = MAXX - 2;
1755		if (y < 1)
1756			y = 1;
1757		if (y >= MAXY - 1)
1758			y = MAXY - 2;
1759	}
1760	if ((m = mitem[x][y]) >= DEMONLORD + 4) {	/* demons dispel spheres */
1761		know[x][y] = 1;
1762		show1cell(x, y);/* show the demon (ha ha) */
1763		cursors();
1764		lprintf("\nThe %s dispels the sphere!", monster[m].name);
1765		beep();
1766		rmsphere(x, y);	/* remove any spheres that are here */
1767		return (c[SPHCAST]);
1768	}
1769	if (m == DISENCHANTRESS) {	/* disenchantress cancels spheres */
1770		cursors();
1771		lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1772		beep();
1773boom:		sphboom(x, y);	/* blow up stuff around sphere */
1774		rmsphere(x, y);	/* remove any spheres that are here */
1775		return (c[SPHCAST]);
1776	}
1777	if (c[CANCELLATION]) {	/* cancellation cancels spheres */
1778		cursors();
1779		lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1780		beep();
1781		goto boom;
1782	}
1783	if (item[x][y] == OANNIHILATION) {	/* collision of spheres
1784						 * detonates spheres */
1785		cursors();
1786		lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1787		beep();
1788		rmsphere(x, y);
1789		goto boom;
1790	}
1791	if (playerx == x && playery == y) {	/* collision of sphere and
1792						 * player! */
1793		cursors();
1794		lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1795		beep();
1796		rmsphere(x, y);	/* remove any spheres that are here */
1797		nap(4000);
1798		died(258);
1799	}
1800	item[x][y] = OANNIHILATION;
1801	mitem[x][y] = 0;
1802	know[x][y] = 1;
1803	show1cell(x, y);	/* show the new sphere */
1804	sp->x = x;
1805	sp->y = y;
1806	sp->lev = level;
1807	sp->dir = dir;
1808	sp->lifetime = life;
1809	sp->p = 0;
1810	if (spheres == 0)
1811		spheres = sp;	/* if first node in the sphere list */
1812	else {			/* add sphere to beginning of linked list */
1813		sp->p = spheres;
1814		spheres = sp;
1815	}
1816	return (++c[SPHCAST]);	/* one more sphere in the world */
1817}
1818
1819/*
1820 * rmsphere(x,y)		Function to delete a sphere of annihilation from list
1821 * 	int x,y;
1822 *
1823 * Enter with the coordinates of the sphere (on current level)
1824 * Returns the number of spheres currently in existence
1825 */
1826int
1827rmsphere(x, y)
1828	int             x, y;
1829{
1830	struct sphere *sp, *sp2 = 0;
1831	for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1832		if (level == sp->lev)	/* is sphere on this level? */
1833			if ((x == sp->x) && (y == sp->y)) {	/* locate sphere at this
1834								 * location */
1835				item[x][y] = mitem[x][y] = 0;
1836				know[x][y] = 1;
1837				show1cell(x, y);	/* show the now missing
1838							 * sphere */
1839				--c[SPHCAST];
1840				if (sp == spheres) {
1841					sp2 = sp;
1842					spheres = sp->p;
1843					free((char *) sp2);
1844				} else {
1845					sp2->p = sp->p;
1846					free((char *) sp);
1847				}
1848				break;
1849			}
1850	return (c[SPHCAST]);	/* return number of spheres in the world */
1851}
1852
1853/*
1854 * sphboom(x,y)	Function to perform the effects of a sphere detonation
1855 * 	int x,y;
1856 *
1857 * Enter with the coordinates of the blast, Returns no value
1858 */
1859void
1860sphboom(x, y)
1861	int             x, y;
1862{
1863	int    i, j;
1864	if (c[HOLDMONST])
1865		c[HOLDMONST] = 1;
1866	if (c[CANCELLATION])
1867		c[CANCELLATION] = 1;
1868	for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1869		for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1870			item[j][i] = mitem[j][i] = 0;
1871			show1cell(j, i);
1872			if (playerx == j && playery == i) {
1873				cursors();
1874				beep();
1875				lprcat("\nYou were too close to the sphere!");
1876				nap(3000);
1877				died(283);	/* player killed in explosion */
1878			}
1879		}
1880}
1881
1882/*
1883 * genmonst()		Function to ask for monster and genocide from game
1884 *
1885 * This is done by setting a flag in the monster[] structure
1886 */
1887void
1888genmonst()
1889{
1890	int    i, j;
1891	cursors();
1892	lprcat("\nGenocide what monster? ");
1893	for (i = 0; (!isalpha(i)) && (i != ' '); i = getchar());
1894	lprc(i);
1895	for (j = 0; j < MAXMONST; j++)	/* search for the monster type */
1896		if (monstnamelist[j] == i) {	/* have we found it? */
1897			monster[j].genocided = 1;	/* genocided from game */
1898			lprintf("  There will be no more %s's", monster[j].name);
1899			/* now wipe out monsters on this level */
1900			newcavelevel(level);
1901			draws(0, MAXX, 0, MAXY);
1902			bot_linex();
1903			return;
1904		}
1905	lprcat("  You sense failure!");
1906}
1907