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