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