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