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