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