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