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