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