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