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