Home | History | Annotate | Line # | Download | only in larn
monster.c revision 1.13
      1 /*	$NetBSD: monster.c,v 1.13 2008/01/28 05:38:54 dholland Exp $	*/
      2 
      3 /*
      4  * monster.c	Larn is copyrighted 1986 by Noah Morgan.
      5  *
      6  * This file contains the following functions:
      7  * ----------------------------------------------------------------------------
      8  *
      9  * createmonster(monstno) 	Function to create a monster next to the player
     10  * int monstno;
     11  *
     12  * int cgood(x,y,itm,monst)Function to check location for emptiness
     13  * int x,y,itm,monst;
     14  *
     15  * createitem(it,arg) 		Routine to place an item next to the player
     16  * int it,arg;
     17  *
     18  * cast() 			Subroutine called by parse to cast a spell for the user
     19  *
     20  * speldamage(x) 	Function to perform spell functions cast by the player
     21  * int x;
     22  *
     23  * loseint()		Routine to decrement your int (intelligence) if > 3
     24  *
     25  * isconfuse() 	Routine to check to see if player is confused
     26  *
     27  * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster
     28  * int x,monst;
     29  *
     30  * fullhit(xx)		Function to return full damage against a monst (aka web)
     31  * int xx;
     32  *
     33  * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir
     34  * int spnum,dam,arg;
     35  * char *str;
     36  *
     37  * godirect(spnum,dam,str,delay,cshow)	Function to perform missile attacks
     38  * int spnum,dam,delay;
     39  * char *str,cshow;
     40  *
     41  * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt
     42  * int x,y;
     43  *
     44  * tdirect(spnum)		Routine to teleport away a monster
     45  * int spnum;
     46  *
     47  * omnidirect(sp,dam,str)  Routine to damage all monsters 1 square from player
     48  * int sp,dam;
     49  * char *str;
     50  *
     51  * dirsub(x,y)		Routine to ask for direction, then modify x,y for it
     52  * int *x,*y;
     53  *
     54  * vxy(x,y)	  	Routine to verify/fix (*x,*y) for being within bounds
     55  * int *x,*y;
     56  *
     57  * dirpoly(spnum)	Routine to ask for a direction and polymorph a monst
     58  * int spnum;
     59  *
     60  * hitmonster(x,y) Function to hit a monster at the designated coordinates
     61  * int x,y;
     62  *
     63  * hitm(x,y,amt)	Function to just hit a monster at a given coordinates
     64  * int x,y,amt;
     65  *
     66  * hitplayer(x,y) 	Function for the monster to hit the player from (x,y)
     67  * int x,y;
     68  *
     69  * dropsomething(monst) Function to create an object when a monster dies
     70  * int monst;
     71  *
     72  * dropgold(amount) 	Function to drop some gold around player
     73  * int amount;
     74  *
     75  * something(level) 	Function to create a random item around player
     76  * int level;
     77  *
     78  * newobject(lev,i) 	Routine to return a randomly selected new object
     79  * int lev,*i;
     80  *
     81  *  spattack(atckno,xx,yy)  Function to process special attacks from monsters
     82  *   int atckno,xx,yy;
     83  *
     84  * checkloss(x) Routine to subtract hp from user and flag bottomline display
     85  * int x;
     86  *
     87  * annihilate()   Routine to annihilate monsters around player, playerx,playery
     88  *
     89  * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
     90  * int x,y,dir,lifetime;
     91  *
     92  * rmsphere(x,y)	Function to delete a sphere of annihilation from list
     93  * int x,y;
     94  *
     95  * sphboom(x,y)	Function to perform the effects of a sphere detonation
     96  * int x,y;
     97  *
     98  * genmonst()		Function to ask for monster and genocide from game
     99  *
    100  */
    101 #include <sys/cdefs.h>
    102 #ifndef lint
    103 __RCSID("$NetBSD: monster.c,v 1.13 2008/01/28 05:38:54 dholland 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(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(%ld)\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(int x, int y, int theitem, int monst)
    173 {
    174 #define itm __lose
    175 	if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
    176 		/* within bounds? */
    177 		if (item[x][y] != OWALL) /* can't make anything on walls */
    178 			/* is it free of items? */
    179 			if (theitem == 0 || (item[x][y] == 0))
    180 				/* is it free of monsters? */
    181 				if (monst == 0 || (mitem[x][y] == 0))
    182 					if ((level != 1) || (x != 33) ||
    183 					    (y != MAXY - 1))
    184 						/* not exit to level 1 */
    185 						return (1);
    186 	return (0);
    187 }
    188 
    189 /*
    190  * createitem(it,arg) 	Routine to place an item next to the player
    191  * 	int it,arg;
    192  *
    193  * Enter with the item number and its argument (iven[], ivenarg[])
    194  * Returns no value, thus we don't know about createitem() failures.
    195  */
    196 void
    197 createitem(it, arg)
    198 	int             it, arg;
    199 {
    200 	int    x, y, k, i;
    201 	if (it >= MAXOBJ)
    202 		return;		/* no such object */
    203 	for (k = rnd(8), i = -8; i < 0; i++, k++) {	/* choose direction,
    204 							 * then try all */
    205 		if (k > 8)
    206 			k = 1;	/* wraparound the diroff arrays */
    207 		x = playerx + diroffx[k];
    208 		y = playery + diroffy[k];
    209 		if (cgood(x, y, 1, 0)) {	/* if we can create here */
    210 			item[x][y] = it;
    211 			know[x][y] = 0;
    212 			iarg[x][y] = arg;
    213 			return;
    214 		}
    215 	}
    216 }
    217 
    218 /*
    219  * cast() 		Subroutine called by parse to cast a spell for the user
    220  *
    221  * No arguments and no return value.
    222  */
    223 static char     eys[] = "\nEnter your spell: ";
    224 void
    225 cast()
    226 {
    227 	int    i, j, a, b, d;
    228 	cursors();
    229 	if (c[SPELLS] <= 0) {
    230 		lprcat("\nYou don't have any spells!");
    231 		return;
    232 	}
    233 	lprcat(eys);
    234 	--c[SPELLS];
    235 	while ((a = lgetchar()) == 'D') {
    236 		seemagic(-1);
    237 		cursors();
    238 		lprcat(eys);
    239 	}
    240 	if (a == '\33')
    241 		goto over;	/* to escape casting a spell	 */
    242 	if ((b = lgetchar()) == '\33')
    243 		goto over;	/* to escape casting a spell	 */
    244 	if ((d = lgetchar()) == '\33') {
    245 over:		lprcat(aborted);
    246 		c[SPELLS]++;
    247 		return;
    248 	}			/* to escape casting a spell	 */
    249 #ifdef EXTRA
    250 	c[SPELLSCAST]++;
    251 #endif
    252 	for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++)	/* seq search for his
    253 							 * spell, hash? */
    254 		if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
    255 			if (spelknow[i]) {
    256 				speldamage(i);
    257 				j = 1;
    258 				i = SPNUM;
    259 			}
    260 	if (j == -1)
    261 		lprcat("  Nothing Happened ");
    262 	bottomline();
    263 }
    264 
    265 /*
    266  * speldamage(x) 		Function to perform spell functions cast by the player
    267  * 	int x;
    268  *
    269  * Enter with the spell number, returns no value.
    270  * Please insure that there are 2 spaces before all messages here
    271  */
    272 void
    273 speldamage(int x)
    274 {
    275 	int    i, j, clev;
    276 	int             xl, xh, yl, yh;
    277 	u_char  *p, *kn, *pm;
    278 
    279 	if (x >= SPNUM)
    280 		return;		/* no such spell */
    281 	if (c[TIMESTOP]) {
    282 		lprcat("  It didn't seem to work");
    283 		return;
    284 	}			/* not if time stopped */
    285 	clev = c[LEVEL];
    286 	if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
    287 		lprcat("  It didn't work!");
    288 		return;
    289 	}
    290 	if (clev * 3 + 2 < x) {
    291 		lprcat("  Nothing happens.  You seem inexperienced at this");
    292 		return;
    293 	}
    294 	switch (x) {
    295 		/* ----- LEVEL 1 SPELLS ----- */
    296 
    297 	case 0:
    298 		if (c[PROTECTIONTIME] == 0)
    299 			c[MOREDEFENSES] += 2;	/* protection field +2 */
    300 		c[PROTECTIONTIME] += 250;
    301 		return;
    302 
    303 	case 1:
    304 		i = rnd(((clev + 1) << 1)) + clev + 3;
    305 		godirect(x, i, (clev >= 2) ? "  Your missiles hit the %s" : "  Your missile hit the %s", 100, '+');	/* magic missile */
    306 
    307 		return;
    308 
    309 	case 2:
    310 		if (c[DEXCOUNT] == 0)
    311 			c[DEXTERITY] += 3;	/* dexterity	 */
    312 		c[DEXCOUNT] += 400;
    313 		return;
    314 
    315 	case 3:		/* sleep		 */
    316 		i = rnd(3) + 1;
    317 		direct(x, fullhit(i),
    318 		       "  While the %s slept, you smashed it %ld times", i);
    319 		return;
    320 
    321 	case 4:		/* charm monster	 */
    322 		c[CHARMCOUNT] += c[CHARISMA] << 1;
    323 		return;
    324 
    325 	case 5:
    326 		godirect(x, rnd(10) + 15 + clev, "  The sound damages the %s", 70, '@');	/* sonic spear */
    327 		return;
    328 
    329 		/* ----- LEVEL 2 SPELLS ----- */
    330 
    331 	case 6:		/* web 			*/
    332 		i = rnd(3) + 2;
    333 		direct(x, fullhit(i),
    334 		       "  While the %s is entangled, you hit %ld times", i);
    335 		return;
    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 %ld 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 	const 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 (that's 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 	const char     *str, cshow;
    779 {
    780 	u_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 by 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(int x, int y)
    901 {
    902 	const char *p;
    903 
    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(int spnum, int dam, const char *str)
    960 {
    961 	int    x, y, m;
    962 
    963 	if (spnum < 0 || spnum >= SPNUM || str == 0)
    964 		return;		/* bad args */
    965 	for (x = playerx - 1; x < playerx + 2; x++)
    966 		for (y = playery - 1; y < playery + 2; y++) {
    967 			if ((m = mitem[x][y]) != 0) {
    968 				if (nospell(spnum, m) == 0) {
    969 					ifblind(x, y);
    970 					cursors();
    971 					lprc('\n');
    972 					lprintf(str, lastmonst);
    973 					hitm(x, y, dam);
    974 					nap(800);
    975 				} else {
    976 					lasthx = x;
    977 					lasthy = y;
    978 				}
    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 (lgetchar()) {
    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 nagas and poltergeists 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(int cavelevel)
   1340 {
   1341 	int    j;
   1342 	int             i;
   1343 	if (cavelevel < 0 || cavelevel > MAXLEVEL + MAXVLEVEL)
   1344 		return;		/* correct level? */
   1345 	if (rnd(101) < 8)
   1346 		something(cavelevel);	/* possibly more than one item */
   1347 	j = newobject(cavelevel, &i);
   1348 	createitem(j, i);
   1349 }
   1350 
   1351 /*
   1352  * newobject(lev,i) 	Routine to return a randomly selected new object
   1353  * 	int lev,*i;
   1354  *
   1355  * Routine to return a randomly selected object to be created
   1356  * Returns the object number created, and sets *i for its argument
   1357  * Enter with the cave level and a pointer to the items arg
   1358  */
   1359 static char     nobjtab[] = {
   1360 	0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
   1361 	OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
   1362 	OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
   1363 	OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
   1364 	OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
   1365 	OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
   1366 	OLONGSWORD};
   1367 
   1368 int
   1369 newobject(lev, i)
   1370 	int    lev, *i;
   1371 {
   1372 	int    tmp = 32, j;
   1373 	if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
   1374 		return (0);	/* correct level? */
   1375 	if (lev > 6)
   1376 		tmp = 37;
   1377 	else if (lev > 4)
   1378 		tmp = 35;
   1379 	j = nobjtab[tmp = rnd(tmp)];	/* the object type */
   1380 	switch (tmp) {
   1381 	case 1:
   1382 	case 2:
   1383 	case 3:
   1384 	case 4:
   1385 		*i = newscroll();
   1386 		break;
   1387 	case 5:
   1388 	case 6:
   1389 	case 7:
   1390 	case 8:
   1391 		*i = newpotion();
   1392 		break;
   1393 	case 9:
   1394 	case 10:
   1395 	case 11:
   1396 	case 12:
   1397 		*i = rnd((lev + 1) * 10) + lev * 10 + 10;
   1398 		break;
   1399 	case 13:
   1400 	case 14:
   1401 	case 15:
   1402 	case 16:
   1403 		*i = lev;
   1404 		break;
   1405 	case 17:
   1406 	case 18:
   1407 	case 19:
   1408 		if (!(*i = newdagger()))
   1409 			return (0);
   1410 		break;
   1411 	case 20:
   1412 	case 21:
   1413 	case 22:
   1414 		if (!(*i = newleather()))
   1415 			return (0);
   1416 		break;
   1417 	case 23:
   1418 	case 32:
   1419 	case 35:
   1420 		*i = rund(lev / 3 + 1);
   1421 		break;
   1422 	case 24:
   1423 	case 26:
   1424 		*i = rnd(lev / 4 + 1);
   1425 		break;
   1426 	case 25:
   1427 		*i = rund(lev / 4 + 1);
   1428 		break;
   1429 	case 27:
   1430 		*i = rnd(lev / 2 + 1);
   1431 		break;
   1432 	case 30:
   1433 	case 33:
   1434 		*i = rund(lev / 2 + 1);
   1435 		break;
   1436 	case 28:
   1437 		*i = rund(lev / 3 + 1);
   1438 		if (*i == 0)
   1439 			return (0);
   1440 		break;
   1441 	case 29:
   1442 	case 31:
   1443 		*i = rund(lev / 2 + 1);
   1444 		if (*i == 0)
   1445 			return (0);
   1446 		break;
   1447 	case 34:
   1448 		*i = newchain();
   1449 		break;
   1450 	case 36:
   1451 		*i = newplate();
   1452 		break;
   1453 	case 37:
   1454 		*i = newsword();
   1455 		break;
   1456 	}
   1457 	return (j);
   1458 }
   1459 
   1460 /*
   1461  *  spattack(atckno,xx,yy) Function to process special attacks from monsters
   1462  *  	int atckno,xx,yy;
   1463  *
   1464  * Enter with the special attack number, and the coordinates (xx,yy)
   1465  * 	of the monster that is special attacking
   1466  * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
   1467  *
   1468  * atckno   monster     effect
   1469  * ---------------------------------------------------
   1470  * 0	none
   1471  * 1	rust monster	eat armor
   1472  * 2	hell hound	breathe light fire
   1473  * 3	dragon		breathe fire
   1474  * 4	giant centipede	weakening sing
   1475  * 5	white dragon	cold breath
   1476  * 6	wraith		drain level
   1477  * 7	waterlord	water gusher
   1478  * 8	leprechaun	steal gold
   1479  * 9	disenchantress	disenchant weapon or armor
   1480  * 10	ice lizard	hits with barbed tail
   1481  * 11	umber hulk	confusion
   1482  * 12	spirit naga	cast spells	taken from special attacks
   1483  * 13	platinum dragon	psionics
   1484  * 14	nymph		steal objects
   1485  * 15	bugbear		bite
   1486  * 16	osequip		bite
   1487  *
   1488  * char rustarm[ARMORTYPES][2];
   1489  * special array for maximum rust damage to armor from rustmonster
   1490  * format is: { armor type , minimum attribute
   1491  */
   1492 #define ARMORTYPES 6
   1493 static char     rustarm[ARMORTYPES][2] = {
   1494 	{ OSTUDLEATHER, -2 },
   1495 	{ ORING, -4 },
   1496 	{ OCHAIN, -5 },
   1497 	{ OSPLINT, -6 },
   1498 	{ OPLATE, -8 },
   1499 	{ OPLATEARMOR, -9}
   1500 };
   1501 static char     spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
   1502 int
   1503 spattack(x, xx, yy)
   1504 	int             x, xx, yy;
   1505 {
   1506 	int    i, j = 0, k, m;
   1507 	const char *p = NULL;
   1508 
   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 		}
   1521 		if ((j == 0) && (k != -1)) {
   1522 			m = iven[k];
   1523 			for (i = 0; i < ARMORTYPES; i++)
   1524 				/* find his armor in table */
   1525 				if (m == rustarm[i][0]) {
   1526 					if (--ivenarg[k] < rustarm[i][1])
   1527 						ivenarg[k] = rustarm[i][1];
   1528 					else
   1529 						j = 1;
   1530 					break;
   1531 				}
   1532 		}
   1533 		if (j == 0)	/* if rusting did not occur */
   1534 			switch (m) {
   1535 			case OLEATHER:
   1536 				p = "\nThe %s hit you -- You're lucky you have leather on";
   1537 				break;
   1538 			case OSSPLATE:
   1539 				p = "\nThe %s hit you -- You're fortunate to have stainless steel armor!";
   1540 				break;
   1541 			}
   1542 		else {
   1543 			beep();
   1544 			p = "\nThe %s hit you -- your armor feels weaker";
   1545 		}
   1546 		break;
   1547 
   1548 	case 2:
   1549 		i = rnd(15) + 8 - c[AC];
   1550 spout:		p = "\nThe %s breathes fire at you!";
   1551 		if (c[FIRERESISTANCE])
   1552 			p = "\nThe %s's flame doesn't faze you!";
   1553 		else
   1554 spout2:	if (p) {
   1555 			lprintf(p, lastmonst);
   1556 			beep();
   1557 		}
   1558 		checkloss(i);
   1559 		return (0);
   1560 
   1561 	case 3:
   1562 		i = rnd(20) + 25 - c[AC];
   1563 		goto spout;
   1564 
   1565 	case 4:
   1566 		if (c[STRENGTH] > 3) {
   1567 			p = "\nThe %s stung you!  You feel weaker";
   1568 			beep();
   1569 			--c[STRENGTH];
   1570 		} else
   1571 			p = "\nThe %s stung you!";
   1572 		break;
   1573 
   1574 	case 5:
   1575 		p = "\nThe %s blasts you with his cold breath";
   1576 		i = rnd(15) + 18 - c[AC];
   1577 		goto spout2;
   1578 
   1579 	case 6:
   1580 		lprintf("\nThe %s drains you of your life energy!", lastmonst);
   1581 		loselevel();
   1582 		beep();
   1583 		return (0);
   1584 
   1585 	case 7:
   1586 		p = "\nThe %s got you with a gusher!";
   1587 		i = rnd(15) + 25 - c[AC];
   1588 		goto spout2;
   1589 
   1590 	case 8:
   1591 		if (c[NOTHEFT])
   1592 			return (0);	/* he has a device of no theft */
   1593 		if (c[GOLD]) {
   1594 			p = "\nThe %s hit you -- Your purse feels lighter";
   1595 			if (c[GOLD] > 32767)
   1596 				c[GOLD] >>= 1;
   1597 			else
   1598 				c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
   1599 			if (c[GOLD] < 0)
   1600 				c[GOLD] = 0;
   1601 		} else
   1602 			p = "\nThe %s couldn't find any gold to steal";
   1603 		lprintf(p, lastmonst);
   1604 		disappear(xx, yy);
   1605 		beep();
   1606 		bottomgold();
   1607 		return (1);
   1608 
   1609 	case 9:
   1610 		for (j = 50;;) {/* disenchant */
   1611 			i = rund(26);
   1612 			m = iven[i];	/* randomly select item */
   1613 			if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
   1614 				if ((ivenarg[i] -= 3) < 0)
   1615 					ivenarg[i] = 0;
   1616 				lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
   1617 				srcount = 0;
   1618 				beep();
   1619 				show3(i);
   1620 				bottomline();
   1621 				return (0);
   1622 			}
   1623 			if (--j <= 0) {
   1624 				p = "\nThe %s nearly misses";
   1625 				break;
   1626 			}
   1627 			break;
   1628 		}
   1629 		break;
   1630 
   1631 	case 10:
   1632 		p = "\nThe %s hit you with his barbed tail";
   1633 		i = rnd(25) - c[AC];
   1634 		goto spout2;
   1635 
   1636 	case 11:
   1637 		p = "\nThe %s has confused you";
   1638 		beep();
   1639 		c[CONFUSE] += 10 + rnd(10);
   1640 		break;
   1641 
   1642 	case 12:		/* performs any number of other special
   1643 				 * attacks	 */
   1644 		return (spattack(spsel[rund(10)], xx, yy));
   1645 
   1646 	case 13:
   1647 		p = "\nThe %s flattens you with his psionics!";
   1648 		i = rnd(15) + 30 - c[AC];
   1649 		goto spout2;
   1650 
   1651 	case 14:
   1652 		if (c[NOTHEFT])
   1653 			return (0);	/* he has device of no theft */
   1654 		if (emptyhanded() == 1) {
   1655 			p = "\nThe %s couldn't find anything to steal";
   1656 			break;
   1657 		}
   1658 		lprintf("\nThe %s picks your pocket and takes:", lastmonst);
   1659 		beep();
   1660 		if (stealsomething() == 0)
   1661 			lprcat("  nothing");
   1662 		disappear(xx, yy);
   1663 		bottomline();
   1664 		return (1);
   1665 
   1666 	case 15:
   1667 		i = rnd(10) + 5 - c[AC];
   1668 spout3:	p = "\nThe %s bit you!";
   1669 		goto spout2;
   1670 
   1671 	case 16:
   1672 		i = rnd(15) + 10 - c[AC];
   1673 		goto spout3;
   1674 	};
   1675 	if (p) {
   1676 		lprintf(p, lastmonst);
   1677 		bottomline();
   1678 	}
   1679 	return (0);
   1680 }
   1681 
   1682 /*
   1683  * checkloss(x) Routine to subtract hp from user and flag bottomline display
   1684  * 	int x;
   1685  *
   1686  * Routine to subtract hitpoints from the user and flag the bottomline display
   1687  * Enter with the number of hit points to lose
   1688  * Note: if x > c[HP] this routine could kill the player!
   1689  */
   1690 void
   1691 checkloss(x)
   1692 	int             x;
   1693 {
   1694 	if (x > 0) {
   1695 		losehp(x);
   1696 		bottomhp();
   1697 	}
   1698 }
   1699 
   1700 /*
   1701  * annihilate() 	Routine to annihilate all monsters around player (playerx,playery)
   1702  *
   1703  * Gives player experience, but no dropped objects
   1704  * Returns the experience gained from all monsters killed
   1705  */
   1706 int
   1707 annihilate()
   1708 {
   1709 	int             i, j;
   1710 	long   k;
   1711 	u_char  *p;
   1712 	for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
   1713 		for (j = playery - 1; j <= playery + 1; j++)
   1714 			if (!vxy(&i, &j)) {	/* if not out of bounds */
   1715 				if (*(p = &mitem[i][j])) {	/* if a monster there */
   1716 					if (*p < DEMONLORD + 2) {
   1717 						k += monster[*p].experience;
   1718 						*p = know[i][j] = 0;
   1719 					} else {
   1720 						lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
   1721 						hitp[i][j] = (hitp[i][j] >> 1) + 1;	/* lose half hit points */
   1722 					}
   1723 				}
   1724 			}
   1725 	if (k > 0) {
   1726 		lprcat("\nYou hear loud screams of agony!");
   1727 		raiseexperience((long) k);
   1728 	}
   1729 	return (k);
   1730 }
   1731 
   1732 /*
   1733  * newsphere(x,y,dir,lifetime)  Function to create a new sphere of annihilation
   1734  * 	int x,y,dir,lifetime;
   1735  *
   1736  * Enter with the coordinates of the sphere in x,y
   1737  *   the direction (0-8 diroffx format) in dir, and the lifespan of the
   1738  *   sphere in lifetime (in turns)
   1739  * Returns the number of spheres currently in existence
   1740  */
   1741 int
   1742 newsphere(x, y, dir, life)
   1743 	int             x, y, dir, life;
   1744 {
   1745 	int             m;
   1746 	struct sphere  *sp;
   1747 	if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
   1748 		return (c[SPHCAST]);	/* can't malloc, therefore failure */
   1749 	if (dir >= 9)
   1750 		dir = 0;	/* no movement if direction not found */
   1751 	if (level == 0)
   1752 		vxy(&x, &y);	/* don't go out of bounds */
   1753 	else {
   1754 		if (x < 1)
   1755 			x = 1;
   1756 		if (x >= MAXX - 1)
   1757 			x = MAXX - 2;
   1758 		if (y < 1)
   1759 			y = 1;
   1760 		if (y >= MAXY - 1)
   1761 			y = MAXY - 2;
   1762 	}
   1763 	if ((m = mitem[x][y]) >= DEMONLORD + 4) {	/* demons dispel spheres */
   1764 		know[x][y] = 1;
   1765 		show1cell(x, y);/* show the demon (ha ha) */
   1766 		cursors();
   1767 		lprintf("\nThe %s dispels the sphere!", monster[m].name);
   1768 		beep();
   1769 		rmsphere(x, y);	/* remove any spheres that are here */
   1770 		free(sp);
   1771 		return (c[SPHCAST]);
   1772 	}
   1773 	if (m == DISENCHANTRESS) {	/* disenchantress cancels spheres */
   1774 		cursors();
   1775 		lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
   1776 		beep();
   1777 boom:		sphboom(x, y);	/* blow up stuff around sphere */
   1778 		rmsphere(x, y);	/* remove any spheres that are here */
   1779 		free(sp);
   1780 		return (c[SPHCAST]);
   1781 	}
   1782 	if (c[CANCELLATION]) {	/* cancellation cancels spheres */
   1783 		cursors();
   1784 		lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
   1785 		beep();
   1786 		goto boom;
   1787 	}
   1788 	if (item[x][y] == OANNIHILATION) {	/* collision of spheres
   1789 						 * detonates spheres */
   1790 		cursors();
   1791 		lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
   1792 		beep();
   1793 		rmsphere(x, y);
   1794 		goto boom;
   1795 	}
   1796 	if (playerx == x && playery == y) {	/* collision of sphere and
   1797 						 * player! */
   1798 		cursors();
   1799 		lprcat("\nYou have been enveloped by the zone of nothingness!\n");
   1800 		beep();
   1801 		rmsphere(x, y);	/* remove any spheres that are here */
   1802 		nap(4000);
   1803 		died(258);
   1804 	}
   1805 	item[x][y] = OANNIHILATION;
   1806 	mitem[x][y] = 0;
   1807 	know[x][y] = 1;
   1808 	show1cell(x, y);	/* show the new sphere */
   1809 	sp->x = x;
   1810 	sp->y = y;
   1811 	sp->lev = level;
   1812 	sp->dir = dir;
   1813 	sp->lifetime = life;
   1814 	sp->p = 0;
   1815 	if (spheres == 0)
   1816 		spheres = sp;	/* if first node in the sphere list */
   1817 	else {			/* add sphere to beginning of linked list */
   1818 		sp->p = spheres;
   1819 		spheres = sp;
   1820 	}
   1821 	return (++c[SPHCAST]);	/* one more sphere in the world */
   1822 }
   1823 
   1824 /*
   1825  * rmsphere(x,y)		Function to delete a sphere of annihilation from list
   1826  * 	int x,y;
   1827  *
   1828  * Enter with the coordinates of the sphere (on current level)
   1829  * Returns the number of spheres currently in existence
   1830  */
   1831 int
   1832 rmsphere(x, y)
   1833 	int             x, y;
   1834 {
   1835 	struct sphere *sp, *sp2 = 0;
   1836 	for (sp = spheres; sp; sp2 = sp, sp = sp->p)
   1837 		if (level == sp->lev)	/* is sphere on this level? */
   1838 			if ((x == sp->x) && (y == sp->y)) {	/* locate sphere at this
   1839 								 * location */
   1840 				item[x][y] = mitem[x][y] = 0;
   1841 				know[x][y] = 1;
   1842 				show1cell(x, y);	/* show the now missing
   1843 							 * sphere */
   1844 				--c[SPHCAST];
   1845 				if (sp == spheres) {
   1846 					sp2 = sp;
   1847 					spheres = sp->p;
   1848 					free((char *) sp2);
   1849 				} else {
   1850 					if (sp2)
   1851 						sp2->p = sp->p;
   1852 					free((char *) sp);
   1853 				}
   1854 				break;
   1855 			}
   1856 	return (c[SPHCAST]);	/* return number of spheres in the world */
   1857 }
   1858 
   1859 /*
   1860  * sphboom(x,y)	Function to perform the effects of a sphere detonation
   1861  * 	int x,y;
   1862  *
   1863  * Enter with the coordinates of the blast, Returns no value
   1864  */
   1865 void
   1866 sphboom(x, y)
   1867 	int             x, y;
   1868 {
   1869 	int    i, j;
   1870 	if (c[HOLDMONST])
   1871 		c[HOLDMONST] = 1;
   1872 	if (c[CANCELLATION])
   1873 		c[CANCELLATION] = 1;
   1874 	for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
   1875 		for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
   1876 			item[j][i] = mitem[j][i] = 0;
   1877 			show1cell(j, i);
   1878 			if (playerx == j && playery == i) {
   1879 				cursors();
   1880 				beep();
   1881 				lprcat("\nYou were too close to the sphere!");
   1882 				nap(3000);
   1883 				died(283);	/* player killed in explosion */
   1884 			}
   1885 		}
   1886 }
   1887 
   1888 /*
   1889  * genmonst()		Function to ask for monster and genocide from game
   1890  *
   1891  * This is done by setting a flag in the monster[] structure
   1892  */
   1893 void
   1894 genmonst()
   1895 {
   1896 	int    i, j;
   1897 	cursors();
   1898 	lprcat("\nGenocide what monster? ");
   1899 	for (i = 0; (!isalpha(i)) && (i != ' '); i = lgetchar());
   1900 	lprc(i);
   1901 	for (j = 0; j < MAXMONST; j++)	/* search for the monster type */
   1902 		if (monstnamelist[j] == i) {	/* have we found it? */
   1903 			monster[j].genocided = 1;	/* genocided from game */
   1904 			lprintf("  There will be no more %s's", monster[j].name);
   1905 			/* now wipe out monsters on this level */
   1906 			newcavelevel(level);
   1907 			draws(0, MAXX, 0, MAXY);
   1908 			bot_linex();
   1909 			return;
   1910 		}
   1911 	lprcat("  You sense failure!");
   1912 }
   1913