monster.c revision 1.8
1/*	$NetBSD: monster.c,v 1.8 2004/01/27 20:30:30 jsm 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.8 2004/01/27 20:30:30 jsm 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(%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 = lgetchar()) == 'D') {
237		seemagic(-1);
238		cursors();
239		lprcat(eys);
240	}
241	if (a == '\33')
242		goto over;	/* to escape casting a spell	 */
243	if ((b = lgetchar()) == '\33')
244		goto over;	/* to escape casting a spell	 */
245	if ((d = lgetchar()) == '\33') {
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/*
985 * static dirsub(x,y)		Routine to ask for direction, then modify x,y for it
986 * 	int *x,*y;
987 *
988 * Function to ask for a direction and modify an x,y for that direction
989 * Enter with the origination coordinates in (x,y).
990 * Returns index into diroffx[] (0-8).
991 */
992static int
993dirsub(x, y)
994	int            *x, *y;
995{
996	int    i;
997	lprcat("\nIn What Direction? ");
998	for (i = 0;;)
999		switch (lgetchar()) {
1000		case 'b':
1001			i++;
1002		case 'n':
1003			i++;
1004		case 'y':
1005			i++;
1006		case 'u':
1007			i++;
1008		case 'h':
1009			i++;
1010		case 'k':
1011			i++;
1012		case 'l':
1013			i++;
1014		case 'j':
1015			i++;
1016			goto out;
1017		};
1018out:
1019	*x = playerx + diroffx[i];
1020	*y = playery + diroffy[i];
1021	vxy(x, y);
1022	return (i);
1023}
1024
1025/*
1026 * vxy(x,y)	   Routine to verify/fix coordinates for being within bounds
1027 * 	int *x,*y;
1028 *
1029 * Function to verify x & y are within the bounds for a level
1030 * If *x or *y is not within the absolute bounds for a level, fix them so that
1031 *   they are on the level.
1032 * Returns TRUE if it was out of bounds, and the *x & *y in the calling
1033 * routine are affected.
1034 */
1035int
1036vxy(x, y)
1037	int            *x, *y;
1038{
1039	int             flag = 0;
1040	if (*x < 0) {
1041		*x = 0;
1042		flag++;
1043	}
1044	if (*y < 0) {
1045		*y = 0;
1046		flag++;
1047	}
1048	if (*x >= MAXX) {
1049		*x = MAXX - 1;
1050		flag++;
1051	}
1052	if (*y >= MAXY) {
1053		*y = MAXY - 1;
1054		flag++;
1055	}
1056	return (flag);
1057}
1058
1059/*
1060 * dirpoly(spnum)	Routine to ask for a direction and polymorph a monst
1061 * 	int spnum;
1062 *
1063 * Subroutine to polymorph a monster and ask for the direction its in
1064 * Enter with the spell number in spmun.
1065 * Returns no value.
1066 */
1067void
1068dirpoly(spnum)
1069	int             spnum;
1070{
1071	int             x, y, m;
1072	if (spnum < 0 || spnum >= SPNUM)
1073		return;		/* bad args */
1074	if (isconfuse())
1075		return;		/* if he is confused, he can't aim his magic */
1076	dirsub(&x, &y);
1077	if (mitem[x][y] == 0) {
1078		lprcat("  There wasn't anything there!");
1079		return;
1080	}
1081	ifblind(x, y);
1082	if (nospell(spnum, mitem[x][y])) {
1083		lasthx = x;
1084		lasthy = y;
1085		return;
1086	}
1087	while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided);
1088	hitp[x][y] = monster[m].hitpoints;
1089	show1cell(x, y);	/* show the new monster */
1090}
1091
1092/*
1093 * hitmonster(x,y) 	Function to hit a monster at the designated coordinates
1094 * 	int x,y;
1095 *
1096 * This routine is used for a bash & slash type attack on a monster
1097 * Enter with the coordinates of the monster in (x,y).
1098 * Returns no value.
1099 */
1100void
1101hitmonster(x, y)
1102	int             x, y;
1103{
1104	int    tmp, monst, damag = 0, flag;
1105	if (c[TIMESTOP])
1106		return;		/* not if time stopped */
1107	vxy(&x, &y);		/* verify coordinates are within range */
1108	if ((monst = mitem[x][y]) == 0)
1109		return;
1110	hit3flag = 1;
1111	ifblind(x, y);
1112	tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
1113	    c[WCLASS] / 4 - 12;
1114	cursors();
1115	/* need at least random chance to hit */
1116	if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
1117		lprcat("\nYou hit");
1118		flag = 1;
1119		damag = fullhit(1);
1120		if (damag < 9999)
1121			damag = rnd(damag) + 1;
1122	} else {
1123		lprcat("\nYou missed");
1124		flag = 0;
1125	}
1126	lprcat(" the ");
1127	lprcat(lastmonst);
1128	if (flag)		/* if the monster was hit */
1129		if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
1130			if (c[WIELD] > 0)
1131				if (ivenarg[c[WIELD]] > -10) {
1132					lprintf("\nYour weapon is dulled by the %s", lastmonst);
1133					beep();
1134					--ivenarg[c[WIELD]];
1135				}
1136	if (flag)
1137		hitm(x, y, damag);
1138	if (monst == VAMPIRE)
1139		if (hitp[x][y] < 25) {
1140			mitem[x][y] = BAT;
1141			know[x][y] = 0;
1142		}
1143}
1144
1145/*
1146 * hitm(x,y,amt)	Function to just hit a monster at a given coordinates
1147 * 	int x,y,amt;
1148 *
1149 * Returns the number of hitpoints the monster absorbed
1150 * This routine is used to specifically damage a monster at a location (x,y)
1151 * Called by hitmonster(x,y)
1152 */
1153int
1154hitm(x, y, amt)
1155	int x, y;
1156	int amt;
1157{
1158	int    monst;
1159	int    hpoints, amt2;
1160	vxy(&x, &y);		/* verify coordinates are within range */
1161	amt2 = amt;		/* save initial damage so we can return it */
1162	monst = mitem[x][y];
1163	if (c[HALFDAM])
1164		amt >>= 1;	/* if half damage curse adjust damage points */
1165	if (amt <= 0)
1166		amt2 = amt = 1;
1167	lasthx = x;
1168	lasthy = y;
1169	stealth[x][y] = 1;	/* make sure hitting monst breaks stealth
1170				 * condition */
1171	c[HOLDMONST] = 0;	/* hit a monster breaks hold monster spell	 */
1172	switch (monst) {	/* if a dragon and orb(s) of dragon slaying	 */
1173	case WHITEDRAGON:
1174	case REDDRAGON:
1175	case GREENDRAGON:
1176	case BRONZEDRAGON:
1177	case PLATINUMDRAGON:
1178	case SILVERDRAGON:
1179		amt *= 1 + (c[SLAYING] << 1);
1180		break;
1181	}
1182	/* invincible monster fix is here */
1183	if (hitp[x][y] > monster[monst].hitpoints)
1184		hitp[x][y] = monster[monst].hitpoints;
1185	if ((hpoints = hitp[x][y]) <= amt) {
1186#ifdef EXTRA
1187		c[MONSTKILLED]++;
1188#endif
1189		lprintf("\nThe %s died!", lastmonst);
1190		raiseexperience((long) monster[monst].experience);
1191		amt = monster[monst].gold;
1192		if (amt > 0)
1193			dropgold(rnd(amt) + amt);
1194		dropsomething(monst);
1195		disappear(x, y);
1196		bottomline();
1197		return (hpoints);
1198	}
1199	hitp[x][y] = hpoints - amt;
1200	return (amt2);
1201}
1202
1203/*
1204 * hitplayer(x,y) 	Function for the monster to hit the player from (x,y)
1205 * 	int x,y;
1206 *
1207 * Function for the monster to hit the player with monster at location x,y
1208 * Returns nothing of value.
1209 */
1210void
1211hitplayer(x, y)
1212	int             x, y;
1213{
1214	int    dam, tmp, mster, bias;
1215	vxy(&x, &y);		/* verify coordinates are within range */
1216	lastnum = mster = mitem[x][y];
1217	/*
1218	 * spirit naga's and poltergeist's do nothing if scarab of negate
1219	 * spirit
1220	 */
1221	if (c[NEGATESPIRIT] || c[SPIRITPRO])
1222		if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
1223			return;
1224	/* if undead and cube of undead control	 */
1225	if (c[CUBEofUNDEAD] || c[UNDEADPRO])
1226		if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
1227			return;
1228	if ((know[x][y] & 1) == 0) {
1229		know[x][y] = 1;
1230		show1cell(x, y);
1231	}
1232	bias = (c[HARDGAME]) + 1;
1233	hitflag = hit2flag = hit3flag = 1;
1234	yrepcount = 0;
1235	cursors();
1236	ifblind(x, y);
1237	if (c[INVISIBILITY])
1238		if (rnd(33) < 20) {
1239			lprintf("\nThe %s misses wildly", lastmonst);
1240			return;
1241		}
1242	if (c[CHARMCOUNT])
1243		if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
1244			lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
1245			return;
1246		}
1247	if (mster == BAT)
1248		dam = 1;
1249	else {
1250		dam = monster[mster].damage;
1251		dam += rnd((int) ((dam < 1) ? 1 : dam)) + monster[mster].level;
1252	}
1253	tmp = 0;
1254	if (monster[mster].attack > 0)
1255		if (((dam + bias + 8) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1256			if (spattack(monster[mster].attack, x, y)) {
1257				flushall();
1258				return;
1259			}
1260			tmp = 1;
1261			bias -= 2;
1262			cursors();
1263		}
1264	if (((dam + bias) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
1265		lprintf("\n  The %s hit you ", lastmonst);
1266		tmp = 1;
1267		if ((dam -= c[AC]) < 0)
1268			dam = 0;
1269		if (dam > 0) {
1270			losehp(dam);
1271			bottomhp();
1272			flushall();
1273		}
1274	}
1275	if (tmp == 0)
1276		lprintf("\n  The %s missed ", lastmonst);
1277}
1278
1279/*
1280 * dropsomething(monst) 	Function to create an object when a monster dies
1281 * 	int monst;
1282 *
1283 * Function to create an object near the player when certain monsters are killed
1284 * Enter with the monster number
1285 * Returns nothing of value.
1286 */
1287void
1288dropsomething(monst)
1289	int             monst;
1290{
1291	switch (monst) {
1292	case ORC:
1293	case NYMPH:
1294	case ELF:
1295	case TROGLODYTE:
1296	case TROLL:
1297	case ROTHE:
1298	case VIOLETFUNGI:
1299	case PLATINUMDRAGON:
1300	case GNOMEKING:
1301	case REDDRAGON:
1302		something(level);
1303		return;
1304
1305	case LEPRECHAUN:
1306		if (rnd(101) >= 75)
1307			creategem();
1308		if (rnd(5) == 1)
1309			dropsomething(LEPRECHAUN);
1310		return;
1311	}
1312}
1313
1314/*
1315 * dropgold(amount) 	Function to drop some gold around player
1316 * 	int amount;
1317 *
1318 * Enter with the number of gold pieces to drop
1319 * Returns nothing of value.
1320 */
1321void
1322dropgold(amount)
1323	int    amount;
1324{
1325	if (amount > 250)
1326		createitem(OMAXGOLD, amount / 100);
1327	else
1328		createitem(OGOLDPILE, amount);
1329}
1330
1331/*
1332 * something(level) 	Function to create a random item around player
1333 * 	int level;
1334 *
1335 * Function to create an item from a designed probability around player
1336 * Enter with the cave level on which something is to be dropped
1337 * Returns nothing of value.
1338 */
1339void
1340something(level)
1341	int             level;
1342{
1343	int    j;
1344	int             i;
1345	if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
1346		return;		/* correct level? */
1347	if (rnd(101) < 8)
1348		something(level);	/* possibly more than one item */
1349	j = newobject(level, &i);
1350	createitem(j, i);
1351}
1352
1353/*
1354 * newobject(lev,i) 	Routine to return a randomly selected new object
1355 * 	int lev,*i;
1356 *
1357 * Routine to return a randomly selected object to be created
1358 * Returns the object number created, and sets *i for its argument
1359 * Enter with the cave level and a pointer to the items arg
1360 */
1361static char     nobjtab[] = {
1362	0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
1363	OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
1364	OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
1365	OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
1366	OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
1367	OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
1368	OLONGSWORD};
1369
1370int
1371newobject(lev, i)
1372	int    lev, *i;
1373{
1374	int    tmp = 32, j;
1375	if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
1376		return (0);	/* correct level? */
1377	if (lev > 6)
1378		tmp = 37;
1379	else if (lev > 4)
1380		tmp = 35;
1381	j = nobjtab[tmp = rnd(tmp)];	/* the object type */
1382	switch (tmp) {
1383	case 1:
1384	case 2:
1385	case 3:
1386	case 4:
1387		*i = newscroll();
1388		break;
1389	case 5:
1390	case 6:
1391	case 7:
1392	case 8:
1393		*i = newpotion();
1394		break;
1395	case 9:
1396	case 10:
1397	case 11:
1398	case 12:
1399		*i = rnd((lev + 1) * 10) + lev * 10 + 10;
1400		break;
1401	case 13:
1402	case 14:
1403	case 15:
1404	case 16:
1405		*i = lev;
1406		break;
1407	case 17:
1408	case 18:
1409	case 19:
1410		if (!(*i = newdagger()))
1411			return (0);
1412		break;
1413	case 20:
1414	case 21:
1415	case 22:
1416		if (!(*i = newleather()))
1417			return (0);
1418		break;
1419	case 23:
1420	case 32:
1421	case 35:
1422		*i = rund(lev / 3 + 1);
1423		break;
1424	case 24:
1425	case 26:
1426		*i = rnd(lev / 4 + 1);
1427		break;
1428	case 25:
1429		*i = rund(lev / 4 + 1);
1430		break;
1431	case 27:
1432		*i = rnd(lev / 2 + 1);
1433		break;
1434	case 30:
1435	case 33:
1436		*i = rund(lev / 2 + 1);
1437		break;
1438	case 28:
1439		*i = rund(lev / 3 + 1);
1440		if (*i == 0)
1441			return (0);
1442		break;
1443	case 29:
1444	case 31:
1445		*i = rund(lev / 2 + 1);
1446		if (*i == 0)
1447			return (0);
1448		break;
1449	case 34:
1450		*i = newchain();
1451		break;
1452	case 36:
1453		*i = newplate();
1454		break;
1455	case 37:
1456		*i = newsword();
1457		break;
1458	}
1459	return (j);
1460}
1461
1462/*
1463 *  spattack(atckno,xx,yy) Function to process special attacks from monsters
1464 *  	int atckno,xx,yy;
1465 *
1466 * Enter with the special attack number, and the coordinates (xx,yy)
1467 * 	of the monster that is special attacking
1468 * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
1469 *
1470 * atckno   monster     effect
1471 * ---------------------------------------------------
1472 * 0	none
1473 * 1	rust monster	eat armor
1474 * 2	hell hound	breathe light fire
1475 * 3	dragon		breathe fire
1476 * 4	giant centipede	weakening sing
1477 * 5	white dragon	cold breath
1478 * 6	wraith		drain level
1479 * 7	waterlord	water gusher
1480 * 8	leprechaun	steal gold
1481 * 9	disenchantress	disenchant weapon or armor
1482 * 10	ice lizard	hits with barbed tail
1483 * 11	umber hulk	confusion
1484 * 12	spirit naga	cast spells	taken from special attacks
1485 * 13	platinum dragon	psionics
1486 * 14	nymph		steal objects
1487 * 15	bugbear		bite
1488 * 16	osequip		bite
1489 *
1490 * char rustarm[ARMORTYPES][2];
1491 * special array for maximum rust damage to armor from rustmonster
1492 * format is: { armor type , minimum attribute
1493 */
1494#define ARMORTYPES 6
1495static char     rustarm[ARMORTYPES][2] = {
1496	{ OSTUDLEATHER, -2 },
1497	{ ORING, -4 },
1498	{ OCHAIN, -5 },
1499	{ OSPLINT, -6 },
1500	{ OPLATE, -8 },
1501	{ OPLATEARMOR, -9}
1502};
1503static char     spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
1504int
1505spattack(x, xx, yy)
1506	int             x, xx, yy;
1507{
1508	int    i, j = 0, k, m;
1509	char  *p = 0;
1510	if (c[CANCELLATION])
1511		return (0);
1512	vxy(&xx, &yy);		/* verify x & y coordinates */
1513	switch (x) {
1514	case 1:		/* rust your armor, j=1 when rusting has occurred */
1515		m = k = c[WEAR];
1516		if ((i = c[SHIELD]) != -1) {
1517			if (--ivenarg[i] < -1)
1518				ivenarg[i] = -1;
1519			else
1520				j = 1;
1521		}
1522		if ((j == 0) && (k != -1)) {
1523			m = iven[k];
1524			for (i = 0; i < ARMORTYPES; i++)
1525				/* find his armor in table */
1526				if (m == rustarm[i][0]) {
1527					if (--ivenarg[k] < rustarm[i][1])
1528						ivenarg[k] = rustarm[i][1];
1529					else
1530						j = 1;
1531					break;
1532				}
1533		}
1534		if (j == 0)	/* if rusting did not occur */
1535			switch (m) {
1536			case OLEATHER:
1537				p = "\nThe %s hit you -- Your lucky you have leather on";
1538				break;
1539			case OSSPLATE:
1540				p = "\nThe %s hit you -- Your fortunate to have stainless steel armor!";
1541				break;
1542			}
1543		else {
1544			beep();
1545			p = "\nThe %s hit you -- your armor feels weaker";
1546		}
1547		break;
1548
1549	case 2:
1550		i = rnd(15) + 8 - c[AC];
1551spout:		p = "\nThe %s breathes fire at you!";
1552		if (c[FIRERESISTANCE])
1553			p = "\nThe %s's flame doesn't phase you!";
1554		else
1555spout2:	if (p) {
1556			lprintf(p, lastmonst);
1557			beep();
1558		}
1559		checkloss(i);
1560		return (0);
1561
1562	case 3:
1563		i = rnd(20) + 25 - c[AC];
1564		goto spout;
1565
1566	case 4:
1567		if (c[STRENGTH] > 3) {
1568			p = "\nThe %s stung you!  You feel weaker";
1569			beep();
1570			--c[STRENGTH];
1571		} else
1572			p = "\nThe %s stung you!";
1573		break;
1574
1575	case 5:
1576		p = "\nThe %s blasts you with his cold breath";
1577		i = rnd(15) + 18 - c[AC];
1578		goto spout2;
1579
1580	case 6:
1581		lprintf("\nThe %s drains you of your life energy!", lastmonst);
1582		loselevel();
1583		beep();
1584		return (0);
1585
1586	case 7:
1587		p = "\nThe %s got you with a gusher!";
1588		i = rnd(15) + 25 - c[AC];
1589		goto spout2;
1590
1591	case 8:
1592		if (c[NOTHEFT])
1593			return (0);	/* he has a device of no theft */
1594		if (c[GOLD]) {
1595			p = "\nThe %s hit you -- Your purse feels lighter";
1596			if (c[GOLD] > 32767)
1597				c[GOLD] >>= 1;
1598			else
1599				c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
1600			if (c[GOLD] < 0)
1601				c[GOLD] = 0;
1602		} else
1603			p = "\nThe %s couldn't find any gold to steal";
1604		lprintf(p, lastmonst);
1605		disappear(xx, yy);
1606		beep();
1607		bottomgold();
1608		return (1);
1609
1610	case 9:
1611		for (j = 50;;) {/* disenchant */
1612			i = rund(26);
1613			m = iven[i];	/* randomly select item */
1614			if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
1615				if ((ivenarg[i] -= 3) < 0)
1616					ivenarg[i] = 0;
1617				lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
1618				srcount = 0;
1619				beep();
1620				show3(i);
1621				bottomline();
1622				return (0);
1623			}
1624			if (--j <= 0) {
1625				p = "\nThe %s nearly misses";
1626				break;
1627			}
1628			break;
1629		}
1630		break;
1631
1632	case 10:
1633		p = "\nThe %s hit you with his barbed tail";
1634		i = rnd(25) - c[AC];
1635		goto spout2;
1636
1637	case 11:
1638		p = "\nThe %s has confused you";
1639		beep();
1640		c[CONFUSE] += 10 + rnd(10);
1641		break;
1642
1643	case 12:		/* performs any number of other special
1644				 * attacks	 */
1645		return (spattack(spsel[rund(10)], xx, yy));
1646
1647	case 13:
1648		p = "\nThe %s flattens you with his psionics!";
1649		i = rnd(15) + 30 - c[AC];
1650		goto spout2;
1651
1652	case 14:
1653		if (c[NOTHEFT])
1654			return (0);	/* he has device of no theft */
1655		if (emptyhanded() == 1) {
1656			p = "\nThe %s couldn't find anything to steal";
1657			break;
1658		}
1659		lprintf("\nThe %s picks your pocket and takes:", lastmonst);
1660		beep();
1661		if (stealsomething() == 0)
1662			lprcat("  nothing");
1663		disappear(xx, yy);
1664		bottomline();
1665		return (1);
1666
1667	case 15:
1668		i = rnd(10) + 5 - c[AC];
1669spout3:	p = "\nThe %s bit you!";
1670		goto spout2;
1671
1672	case 16:
1673		i = rnd(15) + 10 - c[AC];
1674		goto spout3;
1675	};
1676	if (p) {
1677		lprintf(p, lastmonst);
1678		bottomline();
1679	}
1680	return (0);
1681}
1682
1683/*
1684 * checkloss(x) Routine to subtract hp from user and flag bottomline display
1685 * 	int x;
1686 *
1687 * Routine to subtract hitpoints from the user and flag the bottomline display
1688 * Enter with the number of hit points to lose
1689 * Note: if x > c[HP] this routine could kill the player!
1690 */
1691void
1692checkloss(x)
1693	int             x;
1694{
1695	if (x > 0) {
1696		losehp(x);
1697		bottomhp();
1698	}
1699}
1700
1701/*
1702 * annihilate() 	Routine to annihilate all monsters around player (playerx,playery)
1703 *
1704 * Gives player experience, but no dropped objects
1705 * Returns the experience gained from all monsters killed
1706 */
1707int
1708annihilate()
1709{
1710	int             i, j;
1711	long   k;
1712	u_char  *p;
1713	for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
1714		for (j = playery - 1; j <= playery + 1; j++)
1715			if (!vxy(&i, &j)) {	/* if not out of bounds */
1716				if (*(p = &mitem[i][j])) {	/* if a monster there */
1717					if (*p < DEMONLORD + 2) {
1718						k += monster[*p].experience;
1719						*p = know[i][j] = 0;
1720					} else {
1721						lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
1722						hitp[i][j] = (hitp[i][j] >> 1) + 1;	/* lose half hit points */
1723					}
1724				}
1725			}
1726	if (k > 0) {
1727		lprcat("\nYou hear loud screams of agony!");
1728		raiseexperience((long) k);
1729	}
1730	return (k);
1731}
1732
1733/*
1734 * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
1735 * 	int x,y,dir,lifetime;
1736 *
1737 * Enter with the coordinates of the sphere in x,y
1738 *   the direction (0-8 diroffx format) in dir, and the lifespan of the
1739 *   sphere in lifetime (in turns)
1740 * Returns the number of spheres currently in existence
1741 */
1742int
1743newsphere(x, y, dir, life)
1744	int             x, y, dir, life;
1745{
1746	int             m;
1747	struct sphere  *sp;
1748	if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
1749		return (c[SPHCAST]);	/* can't malloc, therefore failure */
1750	if (dir >= 9)
1751		dir = 0;	/* no movement if direction not found */
1752	if (level == 0)
1753		vxy(&x, &y);	/* don't go out of bounds */
1754	else {
1755		if (x < 1)
1756			x = 1;
1757		if (x >= MAXX - 1)
1758			x = MAXX - 2;
1759		if (y < 1)
1760			y = 1;
1761		if (y >= MAXY - 1)
1762			y = MAXY - 2;
1763	}
1764	if ((m = mitem[x][y]) >= DEMONLORD + 4) {	/* demons dispel spheres */
1765		know[x][y] = 1;
1766		show1cell(x, y);/* show the demon (ha ha) */
1767		cursors();
1768		lprintf("\nThe %s dispels the sphere!", monster[m].name);
1769		beep();
1770		rmsphere(x, y);	/* remove any spheres that are here */
1771		return (c[SPHCAST]);
1772	}
1773	if (m == DISENCHANTRESS) {	/* disenchantress cancels spheres */
1774		cursors();
1775		lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
1776		beep();
1777boom:		sphboom(x, y);	/* blow up stuff around sphere */
1778		rmsphere(x, y);	/* remove any spheres that are here */
1779		return (c[SPHCAST]);
1780	}
1781	if (c[CANCELLATION]) {	/* cancellation cancels spheres */
1782		cursors();
1783		lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
1784		beep();
1785		goto boom;
1786	}
1787	if (item[x][y] == OANNIHILATION) {	/* collision of spheres
1788						 * detonates spheres */
1789		cursors();
1790		lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
1791		beep();
1792		rmsphere(x, y);
1793		goto boom;
1794	}
1795	if (playerx == x && playery == y) {	/* collision of sphere and
1796						 * player! */
1797		cursors();
1798		lprcat("\nYou have been enveloped by the zone of nothingness!\n");
1799		beep();
1800		rmsphere(x, y);	/* remove any spheres that are here */
1801		nap(4000);
1802		died(258);
1803	}
1804	item[x][y] = OANNIHILATION;
1805	mitem[x][y] = 0;
1806	know[x][y] = 1;
1807	show1cell(x, y);	/* show the new sphere */
1808	sp->x = x;
1809	sp->y = y;
1810	sp->lev = level;
1811	sp->dir = dir;
1812	sp->lifetime = life;
1813	sp->p = 0;
1814	if (spheres == 0)
1815		spheres = sp;	/* if first node in the sphere list */
1816	else {			/* add sphere to beginning of linked list */
1817		sp->p = spheres;
1818		spheres = sp;
1819	}
1820	return (++c[SPHCAST]);	/* one more sphere in the world */
1821}
1822
1823/*
1824 * rmsphere(x,y)		Function to delete a sphere of annihilation from list
1825 * 	int x,y;
1826 *
1827 * Enter with the coordinates of the sphere (on current level)
1828 * Returns the number of spheres currently in existence
1829 */
1830int
1831rmsphere(x, y)
1832	int             x, y;
1833{
1834	struct sphere *sp, *sp2 = 0;
1835	for (sp = spheres; sp; sp2 = sp, sp = sp->p)
1836		if (level == sp->lev)	/* is sphere on this level? */
1837			if ((x == sp->x) && (y == sp->y)) {	/* locate sphere at this
1838								 * location */
1839				item[x][y] = mitem[x][y] = 0;
1840				know[x][y] = 1;
1841				show1cell(x, y);	/* show the now missing
1842							 * sphere */
1843				--c[SPHCAST];
1844				if (sp == spheres) {
1845					sp2 = sp;
1846					spheres = sp->p;
1847					free((char *) sp2);
1848				} else {
1849					sp2->p = sp->p;
1850					free((char *) sp);
1851				}
1852				break;
1853			}
1854	return (c[SPHCAST]);	/* return number of spheres in the world */
1855}
1856
1857/*
1858 * sphboom(x,y)	Function to perform the effects of a sphere detonation
1859 * 	int x,y;
1860 *
1861 * Enter with the coordinates of the blast, Returns no value
1862 */
1863void
1864sphboom(x, y)
1865	int             x, y;
1866{
1867	int    i, j;
1868	if (c[HOLDMONST])
1869		c[HOLDMONST] = 1;
1870	if (c[CANCELLATION])
1871		c[CANCELLATION] = 1;
1872	for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
1873		for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
1874			item[j][i] = mitem[j][i] = 0;
1875			show1cell(j, i);
1876			if (playerx == j && playery == i) {
1877				cursors();
1878				beep();
1879				lprcat("\nYou were too close to the sphere!");
1880				nap(3000);
1881				died(283);	/* player killed in explosion */
1882			}
1883		}
1884}
1885
1886/*
1887 * genmonst()		Function to ask for monster and genocide from game
1888 *
1889 * This is done by setting a flag in the monster[] structure
1890 */
1891void
1892genmonst()
1893{
1894	int    i, j;
1895	cursors();
1896	lprcat("\nGenocide what monster? ");
1897	for (i = 0; (!isalpha(i)) && (i != ' '); i = lgetchar());
1898	lprc(i);
1899	for (j = 0; j < MAXMONST; j++)	/* search for the monster type */
1900		if (monstnamelist[j] == i) {	/* have we found it? */
1901			monster[j].genocided = 1;	/* genocided from game */
1902			lprintf("  There will be no more %s's", monster[j].name);
1903			/* now wipe out monsters on this level */
1904			newcavelevel(level);
1905			draws(0, MAXX, 0, MAXY);
1906			bot_linex();
1907			return;
1908		}
1909	lprcat("  You sense failure!");
1910}
1911