Home | History | Annotate | Line # | Download | only in phantasia
main.c revision 1.21
      1 /*	$NetBSD: main.c,v 1.21 2009/05/26 00:30:16 dholland Exp $	*/
      2 
      3 /*
      4  * Phantasia 3.3.2 -- Interterminal fantasy game
      5  *
      6  * Edward A. Estes
      7  * AT&T, March 12, 1986
      8  */
      9 
     10 /* DISCLAIMER:
     11  *
     12  * This game is distributed for free as is.  It is not guaranteed to work
     13  * in every conceivable environment.  It is not even guaranteed to work
     14  * in ANY environment.
     15  *
     16  * This game is distributed without notice of copyright, therefore it
     17  * may be used in any manner the recipient sees fit.  However, the
     18  * author assumes no responsibility for maintaining or revising this
     19  * game, in its original form, or any derivitives thereof.
     20  *
     21  * The author shall not be responsible for any loss, cost, or damage,
     22  * including consequential damage, caused by reliance on this material.
     23  *
     24  * The author makes no warranties, express or implied, including warranties
     25  * of merchantability or fitness for a particular purpose or use.
     26  *
     27  * AT&T is in no way connected with this game.
     28  */
     29 
     30 #include <sys/stat.h>
     31 #include <sys/types.h>
     32 #include <err.h>
     33 #include <pwd.h>
     34 
     35 /*
     36  * The program allocates as much file space as it needs to store characters,
     37  * so the possibility exists for the character file to grow without bound.
     38  * The file is purged upon normal entry to try to avoid that problem.
     39  * A similar problem exists for energy voids.  To alleviate the problem here,
     40  * the void file is cleared with every new king, and a limit is placed
     41  * on the size of the energy void file.
     42  */
     43 
     44 /*
     45  * Put one line of text into the file 'motd' for announcements, etc.
     46  */
     47 
     48 /*
     49  * The scoreboard file is updated when someone dies, and keeps track
     50  * of the highest character to date for that login.
     51  * Being purged from the character file does not cause the scoreboard
     52  * to be updated.
     53  */
     54 
     55 
     56 /*
     57  * main.c	Main routines for Phantasia
     58  */
     59 
     60 #include "include.h"
     61 #undef bool
     62 #include <curses.h>
     63 
     64 int	main(int, char **);
     65 
     66 int
     67 main(int argc, char **argv)
     68 {
     69 	bool    noheader = FALSE;	/* set if don't want header */
     70 	bool    headeronly = FALSE;	/* set if only want header */
     71 	bool    examine = FALSE;	/* set if examine a character */
     72 	time_t  seconds;		/* for time of day */
     73 	double  dtemp;			/* for temporary calculations */
     74 
     75 	initialstate();			/* init globals */
     76 
     77 	/* process arguments */
     78 	while (--argc && (*++argv)[0] == '-')
     79 		switch ((*argv)[1]) {
     80 		case 's':	/* short */
     81 			noheader = TRUE;
     82 			break;
     83 
     84 		case 'H':	/* Header */
     85 			headeronly = TRUE;
     86 			break;
     87 
     88 		case 'a':	/* all users */
     89 			activelist();
     90 			cleanup(TRUE);
     91 			/* NOTREACHED */
     92 
     93 		case 'p':	/* purge old players */
     94 			purgeoldplayers();
     95 			cleanup(TRUE);
     96 			/* NOTREACHED */
     97 
     98 		case 'S':	/* set 'Wizard' */
     99 			Wizard = !getuid();
    100 			break;
    101 
    102 		case 'x':	/* examine */
    103 			examine = TRUE;
    104 			break;
    105 
    106 		case 'm':	/* monsters */
    107 			monstlist();
    108 			cleanup(TRUE);
    109 			/* NOTREACHED */
    110 
    111 		case 'b':	/* scoreboard */
    112 			scorelist();
    113 			cleanup(TRUE);
    114 			/* NOTREACHED */
    115 		}
    116 
    117 	if (!isatty(0))		/* don't let non-tty's play */
    118 		cleanup(TRUE);
    119 	/* NOTREACHED */
    120 
    121 	playinit();		/* set up to catch signals, init curses */
    122 
    123 	if (examine) {
    124 		changestats(FALSE);
    125 		cleanup(TRUE);
    126 		/* NOTREACHED */
    127 	}
    128 	if (!noheader) {
    129 		titlelist();
    130 		purgeoldplayers();	/* clean up old characters */
    131 	}
    132 	if (headeronly)
    133 		cleanup(TRUE);
    134 	/* NOTREACHED */
    135 
    136 	do
    137 		/* get the player structure filled */
    138 	{
    139 		Fileloc = -1L;
    140 
    141 		mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? ");
    142 
    143 		switch (getanswer("NYQ", FALSE)) {
    144 		case 'Y':
    145 			Fileloc = recallplayer();
    146 			break;
    147 
    148 		case 'Q':
    149 			cleanup(TRUE);
    150 			/* NOTREACHED */
    151 
    152 		default:
    153 			Fileloc = rollnewplayer();
    154 			break;
    155 		}
    156 		clear();
    157 	}
    158 	while (Fileloc < 0L);
    159 
    160 	if (Player.p_level > 5.0)
    161 		/* low level players have long timeout */
    162 		Timeout = TRUE;
    163 
    164 	/* update some important player statistics */
    165 	strlcpy(Player.p_login, Login, sizeof(Player.p_login));
    166 	time(&seconds);
    167 	Player.p_lastused = localtime(&seconds)->tm_yday;
    168 	Player.p_status = S_PLAYING;
    169 	writerecord(&Player, Fileloc);
    170 
    171 	Statptr = &Stattable[Player.p_type];	/* initialize pointer */
    172 
    173 	/* catch interrupts */
    174 #ifdef	BSD41
    175 	sigset(SIGINT, interrupt);
    176 #endif
    177 #ifdef	BSD42
    178 	signal(SIGINT, interrupt);
    179 #endif
    180 #ifdef	SYS3
    181 	signal(SIGINT, interrupt);
    182 #endif
    183 #ifdef	SYS5
    184 	signal(SIGINT, interrupt);
    185 #endif
    186 
    187 	altercoordinates(Player.p_x, Player.p_y, A_FORCED);	/* set some flags */
    188 
    189 	clear();
    190 
    191 	for (;;)
    192 		/* loop forever, processing input */
    193 	{
    194 
    195 		adjuststats();	/* cleanup stats */
    196 
    197 		if (Throne && Player.p_crowns == 0 && Player.p_specialtype != SC_KING)
    198 			/* not allowed on throne -- move */
    199 		{
    200 			mvaddstr(5, 0, "You're not allowed in the Lord's Chamber without a crown.\n");
    201 			altercoordinates(0.0, 0.0, A_NEAR);
    202 		}
    203 		checktampered();/* check for energy voids, etc. */
    204 
    205 		if (Player.p_status != S_CLOAKED
    206 		/* not cloaked */
    207 		    && (dtemp = fabs(Player.p_x)) == fabs(Player.p_y)
    208 		/* |x| = |y| */
    209 		    && !Throne)
    210 			/* not on throne */
    211 		{
    212 			dtemp = sqrt(dtemp / 100.0);
    213 			if (floor(dtemp) == dtemp)
    214 				/* |x| / 100 == n*n; at a trading post */
    215 			{
    216 				tradingpost();
    217 				clear();
    218 			}
    219 		}
    220 		checkbattle();	/* check for player to player battle */
    221 		neatstuff();	/* gurus, medics, etc. */
    222 
    223 		if (Player.p_status == S_CLOAKED) {
    224 			/* costs 3 mana per turn to be cloaked */
    225 			if (Player.p_mana > 3.0)
    226 				Player.p_mana -= 3.0;
    227 			else
    228 				/* ran out of mana, uncloak */
    229 			{
    230 				Player.p_status = S_PLAYING;
    231 				Changed = TRUE;
    232 			}
    233 		}
    234 
    235 		if (Player.p_status != S_PLAYING && Player.p_status != S_CLOAKED)
    236 			/* change status back to S_PLAYING */
    237 		{
    238 			Player.p_status = S_PLAYING;
    239 			Changed = TRUE;
    240 		}
    241 		if (Changed)
    242 			/* update file only if important stuff has changed */
    243 		{
    244 			writerecord(&Player, Fileloc);
    245 			Changed = FALSE;
    246 			continue;
    247 		}
    248 		readmessage();	/* read message, if any */
    249 
    250 		displaystats();	/* print statistics */
    251 
    252 		move(6, 0);
    253 
    254 		if (Throne)
    255 			/* maybe make king, print prompt, etc. */
    256 			throneroom();
    257 
    258 		/* print status line */
    259 		addstr("1:Move  2:Players  3:Talk  4:Stats  5:Quit  ");
    260 		if (Player.p_level >= MEL_CLOAK && Player.p_magiclvl >= ML_CLOAK)
    261 			addstr("6:Cloak  ");
    262 		if (Player.p_level >= MEL_TELEPORT && Player.p_magiclvl >= ML_TELEPORT)
    263 			addstr("7:Teleport  ");
    264 		if (Player.p_specialtype >= SC_COUNCIL || Wizard)
    265 			addstr("8:Intervene  ");
    266 
    267 		procmain();	/* process input */
    268 	}
    269 }
    270 
    271 void
    272 initialstate(void)
    273 {
    274 	struct stat sb;
    275 	struct passwd *pw;
    276 
    277 	Beyond = FALSE;
    278 	Marsh = FALSE;
    279 	Throne = FALSE;
    280 	Changed = FALSE;
    281 	Wizard = FALSE;
    282 	Timeout = FALSE;
    283 	Users = 0;
    284 	Windows = FALSE;
    285 	Echo = TRUE;
    286 
    287 	/* setup login name */
    288 	if ((Login = getlogin()) == NULL) {
    289 		pw = getpwuid(getuid());
    290 		if (pw == NULL) {
    291 			errx(1, "Who are you?");
    292 		}
    293 		Login = pw->pw_name;
    294 	}
    295 
    296 	/* open some files */
    297 	if ((Playersfp = fopen(_PATH_PEOPLE, "r+")) == NULL)
    298 		error(_PATH_PEOPLE);
    299 	/* NOTREACHED */
    300 	if (fileno(Playersfp) < 3)
    301 		exit(1);
    302 
    303 	if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL)
    304 		error(_PATH_MONST);
    305 	/* NOTREACHED */
    306 
    307 	if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL)
    308 		error(_PATH_MESS);
    309 	/* NOTREACHED */
    310 
    311 	if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL)
    312 		error(_PATH_VOID);
    313 	if (fstat(fileno(Energyvoidfp), &sb) == -1)
    314 		error("stat");
    315 	if (sb.st_size == 0) {
    316 		/* initialize grail to new location */
    317 		Enrgyvoid.ev_active = TRUE;
    318 		Enrgyvoid.ev_x = ROLL(-1.0e6, 2.0e6);
    319 		Enrgyvoid.ev_y = ROLL(-1.0e6, 2.0e6);
    320 		writevoid(&Enrgyvoid, 0L);
    321 	}
    322 
    323 	/* NOTREACHED */
    324 
    325 	srandom((unsigned) time(NULL));	/* prime random numbers */
    326 }
    327 
    328 long
    329 rollnewplayer(void)
    330 {
    331 	int     chartype;	/* character type */
    332 	int     ch;		/* input */
    333 
    334 	initplayer(&Player);	/* initialize player structure */
    335 
    336 	clear();
    337 	mvaddstr(4, 21, "Which type of character do you want:");
    338 	mvaddstr(8, 4,
    339 "1:Magic User  2:Fighter  3:Elf  4:Dwarf  5:Halfling  6:Experimento  ");
    340 	if (Wizard) {
    341 		addstr("7:Super  ? ");
    342 		chartype = getanswer("1234567", FALSE);
    343 	} else {
    344 		addstr("?  ");
    345 		chartype = getanswer("123456", FALSE);
    346 	}
    347 
    348 	do {
    349 		genchar(chartype);	/* roll up a character */
    350 
    351 		/* print out results */
    352 		mvprintw(12, 14,
    353 		    "Strength    :  %2.0f  Quickness:  %2.0f  Mana       :  %2.0f\n",
    354 		    Player.p_strength, Player.p_quickness, Player.p_mana);
    355 		mvprintw(13, 14,
    356 		    "Energy Level:  %2.0f  Brains   :  %2.0f  Magic Level:  %2.0f\n",
    357 		    Player.p_energy, Player.p_brains, Player.p_magiclvl);
    358 
    359 		if (Player.p_type == C_EXPER || Player.p_type == C_SUPER)
    360 			break;
    361 
    362 		mvaddstr(14, 14, "Type '1' to keep >");
    363 		ch = getanswer(" ", TRUE);
    364 	}
    365 	while (ch != '1');
    366 
    367 	if (Player.p_type == C_EXPER || Player.p_type == C_SUPER)
    368 		/* get coordinates for experimento */
    369 		for (;;) {
    370 			mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? ");
    371 			getstring(Databuf, SZ_DATABUF);
    372 			sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y);
    373 
    374 			if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER)
    375 				mvaddstr(17, 0, "Invalid coordinates.  Try again.\n");
    376 			else
    377 				break;
    378 		}
    379 
    380 	for (;;)
    381 		/* name the new character */
    382 	{
    383 		mvprintw(18, 0,
    384 		    "Give your character a name [up to %d characters] ?  ", SZ_NAME - 1);
    385 		getstring(Player.p_name, SZ_NAME);
    386 		truncstring(Player.p_name);	/* remove trailing blanks */
    387 
    388 		if (Player.p_name[0] == '\0')
    389 			/* no null names */
    390 			mvaddstr(19, 0, "Invalid name.");
    391 		else
    392 			if (findname(Player.p_name, &Other) >= 0L)
    393 				/* cannot have duplicate names */
    394 				mvaddstr(19, 0, "Name already in use.");
    395 			else
    396 				/* name is acceptable */
    397 				break;
    398 
    399 		addstr("  Pick another.\n");
    400 	}
    401 
    402 	/* get a password for character */
    403 	Echo = FALSE;
    404 
    405 	do {
    406 		mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? ");
    407 		getstring(Player.p_password, SZ_PASSWORD);
    408 		mvaddstr(21, 0, "Enter again to verify: ");
    409 		getstring(Databuf, SZ_PASSWORD);
    410 	}
    411 	while (strcmp(Player.p_password, Databuf) != 0);
    412 
    413 	Echo = TRUE;
    414 
    415 	return (allocrecord());
    416 }
    417 
    418 void
    419 procmain(void)
    420 {
    421 	int     ch;		/* input */
    422 	double  x;		/* desired new x coordinate */
    423 	double  y;		/* desired new y coordinate */
    424 	double  temp;		/* for temporary calculations */
    425 	FILE   *fp;		/* for opening files */
    426 	int     loop;		/* a loop counter */
    427 	bool    hasmoved = FALSE;	/* set if player has moved */
    428 
    429 	ch = inputoption();
    430 	mvaddstr(4, 0, "\n\n");	/* clear status area */
    431 
    432 	move(7, 0);
    433 	clrtobot();		/* clear data on bottom area of screen */
    434 
    435 	if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7'))
    436 		/* valar cannot move */
    437 		ch = ' ';
    438 
    439 	switch (ch) {
    440 	case 'K':		/* move up/north */
    441 	case 'N':
    442 		x = Player.p_x;
    443 		y = Player.p_y + MAXMOVE();
    444 		hasmoved = TRUE;
    445 		break;
    446 
    447 	case 'J':		/* move down/south */
    448 	case 'S':
    449 		x = Player.p_x;
    450 		y = Player.p_y - MAXMOVE();
    451 		hasmoved = TRUE;
    452 		break;
    453 
    454 	case 'L':		/* move right/east */
    455 	case 'E':
    456 		x = Player.p_x + MAXMOVE();
    457 		y = Player.p_y;
    458 		hasmoved = TRUE;
    459 		break;
    460 
    461 	case 'H':		/* move left/west */
    462 	case 'W':
    463 		x = Player.p_x - MAXMOVE();
    464 		y = Player.p_y;
    465 		hasmoved = TRUE;
    466 		break;
    467 
    468 	default:		/* rest */
    469 		Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0
    470 		    + Player.p_level / 3.0 + 2.0;
    471 		Player.p_energy =
    472 		    MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield);
    473 
    474 		if (Player.p_status != S_CLOAKED)
    475 			/* cannot find mana if cloaked */
    476 		{
    477 			Player.p_mana += (Circle + Player.p_level) / 4.0;
    478 
    479 			if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne)
    480 				/* wandering monster */
    481 				encounter(-1);
    482 		}
    483 		break;
    484 
    485 	case 'X':		/* change/examine a character */
    486 		changestats(TRUE);
    487 		break;
    488 
    489 	case '1':		/* move */
    490 		for (loop = 3; loop; --loop) {
    491 			mvaddstr(4, 0, "X Y Coordinates ? ");
    492 			getstring(Databuf, SZ_DATABUF);
    493 
    494 			if (sscanf(Databuf, "%lf %lf", &x, &y) != 2)
    495 				mvaddstr(5, 0, "Try again\n");
    496 			else
    497 				if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE())
    498 					ILLMOVE();
    499 				else {
    500 					hasmoved = TRUE;
    501 					break;
    502 				}
    503 		}
    504 		break;
    505 
    506 	case '2':		/* players */
    507 		userlist(TRUE);
    508 		break;
    509 
    510 	case '3':		/* message */
    511 		mvaddstr(4, 0, "Message ? ");
    512 		getstring(Databuf, SZ_DATABUF);
    513 		/* we open the file for writing to erase any data which is
    514 		 * already there */
    515 		fp = fopen(_PATH_MESS, "w");
    516 		if (Databuf[0] != '\0')
    517 			fprintf(fp, "%s: %s", Player.p_name, Databuf);
    518 		fclose(fp);
    519 		break;
    520 
    521 	case '4':		/* stats */
    522 		allstatslist();
    523 		break;
    524 
    525 	case '5':		/* good-bye */
    526 		leavegame();
    527 		/* NOTREACHED */
    528 
    529 	case '6':		/* cloak */
    530 		if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK)
    531 			ILLCMD();
    532 		else
    533 			if (Player.p_status == S_CLOAKED)
    534 				Player.p_status = S_PLAYING;
    535 			else
    536 				if (Player.p_mana < MM_CLOAK)
    537 					mvaddstr(5, 0, "No mana left.\n");
    538 				else {
    539 					Changed = TRUE;
    540 					Player.p_mana -= MM_CLOAK;
    541 					Player.p_status = S_CLOAKED;
    542 				}
    543 		break;
    544 
    545 	case '7':		/* teleport */
    546 		/*
    547 	         * conditions for teleport
    548 	         *	- 20 per (level plus magic level)
    549 	         *	- OR council of the wise or valar or ex-valar
    550 	         *	- OR transport from throne
    551 	         * transports from throne cost no mana
    552 	         */
    553 		if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT)
    554 			ILLCMD();
    555 		else
    556 			for (loop = 3; loop; --loop) {
    557 				mvaddstr(4, 0, "X Y Coordinates ? ");
    558 				getstring(Databuf, SZ_DATABUF);
    559 
    560 				if (sscanf(Databuf, "%lf %lf", &x, &y) == 2) {
    561 					temp = distance(Player.p_x, x, Player.p_y, y);
    562 					if (!Throne
    563 					/* can transport anywhere from throne */
    564 					    && Player.p_specialtype <= SC_COUNCIL
    565 					/* council, valar can transport
    566 					 * anywhere */
    567 					    && temp > (Player.p_level + Player.p_magiclvl) * 20.0)
    568 						/* can only move 20 per exp.
    569 						 * level + mag. level */
    570 						ILLMOVE();
    571 					else {
    572 						temp = (temp / 75.0 + 1.0) * 20.0;	/* mana used */
    573 
    574 						if (!Throne && temp > Player.p_mana)
    575 							mvaddstr(5, 0, "Not enough power for that distance.\n");
    576 						else {
    577 							if (!Throne)
    578 								Player.p_mana -= temp;
    579 							hasmoved = TRUE;
    580 							break;
    581 						}
    582 					}
    583 				}
    584 			}
    585 		break;
    586 
    587 	case 'C':
    588 	case '9':		/* monster */
    589 		if (Throne)
    590 			/* no monsters while on throne */
    591 			mvaddstr(5, 0, "No monsters in the chamber!\n");
    592 		else
    593 			if (Player.p_specialtype != SC_VALAR)
    594 				/* the valar cannot call monsters */
    595 			{
    596 				Player.p_sin += 1e-6;
    597 				encounter(-1);
    598 			}
    599 		break;
    600 
    601 	case '0':		/* decree */
    602 		if (Wizard || (Player.p_specialtype == SC_KING && Throne))
    603 			/* kings must be on throne to decree */
    604 			dotampered();
    605 		else
    606 			ILLCMD();
    607 		break;
    608 
    609 	case '8':		/* intervention */
    610 		if (Wizard || Player.p_specialtype >= SC_COUNCIL)
    611 			dotampered();
    612 		else
    613 			ILLCMD();
    614 		break;
    615 	}
    616 
    617 	if (hasmoved)
    618 		/* player has moved -- alter coordinates, and do random
    619 		 * monster */
    620 	{
    621 		altercoordinates(x, y, A_SPECIFIC);
    622 
    623 		if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne)
    624 			encounter(-1);
    625 	}
    626 }
    627 
    628 void
    629 titlelist(void)
    630 {
    631 	FILE   *fp;		/* used for opening various files */
    632 	bool    councilfound = FALSE;	/* set if we find a member of the
    633 					 * council */
    634 	bool    kingfound = FALSE;	/* set if we find a king */
    635 	double  hiexp, nxtexp;	/* used for finding the two highest players */
    636 	double  hilvl, nxtlvl;	/* used for finding the two highest players */
    637 	char    hiname[21], nxtname[21];	/* used for finding the two
    638 						 * highest players */
    639 
    640 	nxtexp = 0;
    641 	mvaddstr(0, 14,
    642 	    "W e l c o m e   t o   P h a n t a s i a (vers. 3.3.2)!");
    643 
    644 	/* print message of the day */
    645 	if ((fp = fopen(_PATH_MOTD, "r")) != NULL
    646 	    && fgets(Databuf, SZ_DATABUF, fp) != NULL) {
    647 		mvaddstr(2, 40 - strlen(Databuf) / 2, Databuf);
    648 		fclose(fp);
    649 	}
    650 	/* search for king */
    651 	fseek(Playersfp, 0L, SEEK_SET);
    652 	while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
    653 		if (Other.p_specialtype == SC_KING &&
    654 		    Other.p_status != S_NOTUSED)
    655 			/* found the king */
    656 		{
    657 			snprintf(Databuf, SZ_DATABUF,
    658 			    "The present ruler is %s  Level:%.0f",
    659 			    Other.p_name, Other.p_level);
    660 			mvaddstr(4, 40 - strlen(Databuf) / 2, Databuf);
    661 			kingfound = TRUE;
    662 			break;
    663 		}
    664 	if (!kingfound)
    665 		mvaddstr(4, 24, "There is no ruler at this time.");
    666 
    667 	/* search for valar */
    668 	fseek(Playersfp, 0L, SEEK_SET);
    669 	while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
    670 		if (Other.p_specialtype == SC_VALAR && Other.p_status != S_NOTUSED)
    671 			/* found the valar */
    672 		{
    673 			snprintf(Databuf, SZ_DATABUF,
    674 				"The Valar is %s   Login:  %s",
    675 				Other.p_name, Other.p_login);
    676 			mvaddstr(6, 40 - strlen(Databuf) / 2, Databuf);
    677 			break;
    678 		}
    679 	/* search for council of the wise */
    680 	fseek(Playersfp, 0L, SEEK_SET);
    681 	Lines = 10;
    682 	while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
    683 		if (Other.p_specialtype == SC_COUNCIL && Other.p_status != S_NOTUSED)
    684 			/* found a member of the council */
    685 		{
    686 			if (!councilfound) {
    687 				mvaddstr(8, 30, "Council of the Wise:");
    688 				councilfound = TRUE;
    689 			}
    690 			/* This assumes a finite (<=5) number of C.O.W.: */
    691 			snprintf(Databuf, SZ_DATABUF,
    692 				"%s   Login:  %s", Other.p_name, Other.p_login);
    693 			mvaddstr(Lines++, 40 - strlen(Databuf) / 2, Databuf);
    694 		}
    695 	/* search for the two highest players */
    696 	nxtname[0] = hiname[0] = '\0';
    697 	hiexp = 0.0;
    698 	nxtlvl = hilvl = 0;
    699 
    700 	fseek(Playersfp, 0L, SEEK_SET);
    701 	while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
    702 		if (Other.p_experience > hiexp && Other.p_specialtype <= SC_KING && Other.p_status != S_NOTUSED)
    703 			/* highest found so far */
    704 		{
    705 			nxtexp = hiexp;
    706 			hiexp = Other.p_experience;
    707 			nxtlvl = hilvl;
    708 			hilvl = Other.p_level;
    709 			strcpy(nxtname, hiname);
    710 			strcpy(hiname, Other.p_name);
    711 		} else
    712 			if (Other.p_experience > nxtexp
    713 			    && Other.p_specialtype <= SC_KING
    714 			    && Other.p_status != S_NOTUSED)
    715 				/* next highest found so far */
    716 			{
    717 				nxtexp = Other.p_experience;
    718 				nxtlvl = Other.p_level;
    719 				strcpy(nxtname, Other.p_name);
    720 			}
    721 	mvaddstr(15, 28, "Highest characters are:");
    722 	snprintf(Databuf, SZ_DATABUF,
    723 	    "%s  Level:%.0f   and   %s  Level:%.0f",
    724 	    hiname, hilvl, nxtname, nxtlvl);
    725 	mvaddstr(17, 40 - strlen(Databuf) / 2, Databuf);
    726 
    727 	/* print last to die */
    728 	if ((fp = fopen(_PATH_LASTDEAD, "r")) != NULL
    729 	    && fgets(Databuf, SZ_DATABUF, fp) != NULL) {
    730 		mvaddstr(19, 25, "The last character to die was:");
    731 		mvaddstr(20, 40 - strlen(Databuf) / 2, Databuf);
    732 	}
    733 	if (fp)
    734 		fclose(fp);
    735 	refresh();
    736 }
    737 
    738 long
    739 recallplayer(void)
    740 {
    741 	long    loc = 0L;	/* location in player file */
    742 	int     loop;		/* loop counter */
    743 	int     ch;		/* input */
    744 
    745 	clear();
    746 	mvprintw(10, 0, "What was your character's name ? ");
    747 	getstring(Databuf, SZ_NAME);
    748 	truncstring(Databuf);
    749 
    750 	if ((loc = findname(Databuf, &Player)) >= 0L)
    751 		/* found character */
    752 	{
    753 		Echo = FALSE;
    754 
    755 		for (loop = 0; loop < 2; ++loop) {
    756 			/* prompt for password */
    757 			mvaddstr(11, 0, "Password ? ");
    758 			getstring(Databuf, SZ_PASSWORD);
    759 			if (strcmp(Databuf, Player.p_password) == 0)
    760 				/* password good */
    761 			{
    762 				Echo = TRUE;
    763 
    764 				if (Player.p_status != S_OFF)
    765 					/* player did not exit normally last
    766 					 * time */
    767 				{
    768 					clear();
    769 					addstr("Your character did not exit normally last time.\n");
    770 					addstr("If you think you have good cause to have your character saved,\n");
    771 					printw("you may quit and mail your reason to 'root'.\n");
    772 					addstr("Otherwise, continuing spells certain death.\n");
    773 					addstr("Do you want to quit ? ");
    774 					ch = getanswer("YN", FALSE);
    775 					if (ch == 'Y') {
    776 						Player.p_status = S_HUNGUP;
    777 						writerecord(&Player, loc);
    778 						cleanup(TRUE);
    779 						/* NOTREACHED */
    780 					}
    781 					death("Stupidity");
    782 					/* NOTREACHED */
    783 				}
    784 				return (loc);
    785 			} else
    786 				mvaddstr(12, 0, "No good.\n");
    787 		}
    788 
    789 		Echo = TRUE;
    790 	} else
    791 		mvaddstr(11, 0, "Not found.\n");
    792 
    793 	more(13);
    794 	return (-1L);
    795 }
    796 
    797 void
    798 neatstuff(void)
    799 {
    800 	double  temp;		/* for temporary calculations */
    801 	int     ch;		/* input */
    802 
    803 	switch ((int) ROLL(0.0, 100.0)) {
    804 	case 1:
    805 	case 2:
    806 		if (Player.p_poison > 0.0) {
    807 			mvaddstr(4, 0, "You've found a medic!  How much will you offer to be cured ? ");
    808 			temp = floor(infloat());
    809 			if (temp < 0.0 || temp > Player.p_gold)
    810 				/* negative gold, or more than available */
    811 			{
    812 				mvaddstr(6, 0, "He was not amused, and made you worse.\n");
    813 				Player.p_poison += 1.0;
    814 			} else
    815 				if (drandom() / 2.0 > (temp + 1.0) / MAX(Player.p_gold, 1))
    816 					/* medic wants 1/2 of available gold */
    817 					mvaddstr(5, 0, "Sorry, he wasn't interested.\n");
    818 				else {
    819 					mvaddstr(5, 0, "He accepted.");
    820 					Player.p_poison = MAX(0.0, Player.p_poison - 1.0);
    821 					Player.p_gold -= temp;
    822 				}
    823 		}
    824 		break;
    825 
    826 	case 3:
    827 		mvaddstr(4, 0, "You've been caught raping and pillaging!\n");
    828 		Player.p_experience += 4000.0;
    829 		Player.p_sin += 0.5;
    830 		break;
    831 
    832 	case 4:
    833 		temp = ROLL(10.0, 75.0);
    834 		mvprintw(4, 0, "You've found %.0f gold pieces, want them ? ", temp);
    835 		ch = getanswer("NY", FALSE);
    836 
    837 		if (ch == 'Y')
    838 			collecttaxes(temp, 0.0);
    839 		break;
    840 
    841 	case 5:
    842 		if (Player.p_sin > 1.0) {
    843 			mvaddstr(4, 0, "You've found a Holy Orb!\n");
    844 			Player.p_sin -= 0.25;
    845 		}
    846 		break;
    847 
    848 	case 6:
    849 		if (Player.p_poison < 1.0) {
    850 			mvaddstr(4, 0, "You've been hit with a plague!\n");
    851 			Player.p_poison += 1.0;
    852 		}
    853 		break;
    854 
    855 	case 7:
    856 		mvaddstr(4, 0, "You've found some holy water.\n");
    857 		++Player.p_holywater;
    858 		break;
    859 
    860 	case 8:
    861 		mvaddstr(4, 0, "You've met a Guru. . .");
    862 		if (drandom() * Player.p_sin > 1.0)
    863 			addstr("You disgusted him with your sins!\n");
    864 		else
    865 			if (Player.p_poison > 0.0) {
    866 				addstr("He looked kindly upon you, and cured you.\n");
    867 				Player.p_poison = 0.0;
    868 			} else {
    869 				addstr("He rewarded you for your virtue.\n");
    870 				Player.p_mana += 50.0;
    871 				Player.p_shield += 2.0;
    872 			}
    873 		break;
    874 
    875 	case 9:
    876 		mvaddstr(4, 0, "You've found an amulet.\n");
    877 		++Player.p_amulets;
    878 		break;
    879 
    880 	case 10:
    881 		if (Player.p_blindness) {
    882 			mvaddstr(4, 0, "You've regained your sight!\n");
    883 			Player.p_blindness = FALSE;
    884 		}
    885 		break;
    886 
    887 	default:		/* deal with poison */
    888 		if (Player.p_poison > 0.0) {
    889 			temp = Player.p_poison * Statptr->c_weakness
    890 			    * Player.p_maxenergy / 600.0;
    891 			if (Player.p_energy > Player.p_maxenergy / 10.0
    892 			    && temp + 5.0 < Player.p_energy)
    893 				Player.p_energy -= temp;
    894 		}
    895 		break;
    896 	}
    897 }
    898 
    899 void
    900 genchar(int type)
    901 {
    902 	int     subscript;	/* used for subscripting into Stattable */
    903 	const struct charstats *statptr; /* for pointing into Stattable */
    904 
    905 	subscript = type - '1';
    906 
    907 	if (subscript < C_MAGIC || subscript > C_EXPER)
    908 		if (subscript != C_SUPER || !Wizard)
    909 			/* fighter is default */
    910 			subscript = C_FIGHTER;
    911 
    912 	statptr = &Stattable[subscript];
    913 
    914 	Player.p_quickness =
    915 	    ROLL(statptr->c_quickness.base, statptr->c_quickness.interval);
    916 	Player.p_strength =
    917 	    ROLL(statptr->c_strength.base, statptr->c_strength.interval);
    918 	Player.p_mana =
    919 	    ROLL(statptr->c_mana.base, statptr->c_mana.interval);
    920 	Player.p_maxenergy =
    921 	    Player.p_energy =
    922 	    ROLL(statptr->c_energy.base, statptr->c_energy.interval);
    923 	Player.p_brains =
    924 	    ROLL(statptr->c_brains.base, statptr->c_brains.interval);
    925 	Player.p_magiclvl =
    926 	    ROLL(statptr->c_magiclvl.base, statptr->c_magiclvl.interval);
    927 
    928 	Player.p_type = subscript;
    929 
    930 	if (Player.p_type == C_HALFLING)
    931 		/* give halfling some experience */
    932 		Player.p_experience = ROLL(600.0, 200.0);
    933 }
    934 
    935 void
    936 playinit(void)
    937 {
    938 	/* catch/ingnore signals */
    939 
    940 #ifdef	BSD41
    941 	sigignore(SIGQUIT);
    942 	sigignore(SIGALRM);
    943 	sigignore(SIGTERM);
    944 	sigignore(SIGTSTP);
    945 	sigignore(SIGTTIN);
    946 	sigignore(SIGTTOU);
    947 	sighold(SIGINT);
    948 	sigset(SIGHUP, ill_sig);
    949 	sigset(SIGTRAP, ill_sig);
    950 	sigset(SIGIOT, ill_sig);
    951 	sigset(SIGEMT, ill_sig);
    952 	sigset(SIGFPE, ill_sig);
    953 	sigset(SIGBUS, ill_sig);
    954 	sigset(SIGSEGV, ill_sig);
    955 	sigset(SIGSYS, ill_sig);
    956 	sigset(SIGPIPE, ill_sig);
    957 #endif
    958 #ifdef	BSD42
    959 	signal(SIGQUIT, ill_sig);
    960 	signal(SIGALRM, SIG_IGN);
    961 	signal(SIGTERM, SIG_IGN);
    962 	signal(SIGTSTP, SIG_IGN);
    963 	signal(SIGTTIN, SIG_IGN);
    964 	signal(SIGTTOU, SIG_IGN);
    965 	signal(SIGINT, ill_sig);
    966 	signal(SIGHUP, SIG_DFL);
    967 	signal(SIGTRAP, ill_sig);
    968 	signal(SIGIOT, ill_sig);
    969 	signal(SIGEMT, ill_sig);
    970 	signal(SIGFPE, ill_sig);
    971 	signal(SIGBUS, ill_sig);
    972 	signal(SIGSEGV, ill_sig);
    973 	signal(SIGSYS, ill_sig);
    974 	signal(SIGPIPE, ill_sig);
    975 #endif
    976 #ifdef	SYS3
    977 	signal(SIGINT, SIG_IGN);
    978 	signal(SIGQUIT, SIG_IGN);
    979 	signal(SIGTERM, SIG_IGN);
    980 	signal(SIGALRM, SIG_IGN);
    981 	signal(SIGHUP, ill_sig);
    982 	signal(SIGTRAP, ill_sig);
    983 	signal(SIGIOT, ill_sig);
    984 	signal(SIGEMT, ill_sig);
    985 	signal(SIGFPE, ill_sig);
    986 	signal(SIGBUS, ill_sig);
    987 	signal(SIGSEGV, ill_sig);
    988 	signal(SIGSYS, ill_sig);
    989 	signal(SIGPIPE, ill_sig);
    990 #endif
    991 #ifdef	SYS5
    992 	signal(SIGINT, SIG_IGN);
    993 	signal(SIGQUIT, SIG_IGN);
    994 	signal(SIGTERM, SIG_IGN);
    995 	signal(SIGALRM, SIG_IGN);
    996 	signal(SIGHUP, ill_sig);
    997 	signal(SIGTRAP, ill_sig);
    998 	signal(SIGIOT, ill_sig);
    999 	signal(SIGEMT, ill_sig);
   1000 	signal(SIGFPE, ill_sig);
   1001 	signal(SIGBUS, ill_sig);
   1002 	signal(SIGSEGV, ill_sig);
   1003 	signal(SIGSYS, ill_sig);
   1004 	signal(SIGPIPE, ill_sig);
   1005 #endif
   1006 
   1007 	if (!initscr()) {	/* turn on curses */
   1008 		fprintf(stderr, "couldn't initialize screen\n");
   1009 		exit (0);
   1010 	}
   1011 	noecho();		/* do not echo input */
   1012 	cbreak();		/* do not process erase, kill */
   1013 	clear();
   1014 	refresh();
   1015 	Windows = TRUE;		/* mark the state */
   1016 }
   1017 
   1018 void
   1019 cleanup(int doexit)
   1020 {
   1021 	if (Windows) {
   1022 		move(LINES - 2, 0);
   1023 		refresh();
   1024 		nocbreak();
   1025 		endwin();
   1026 	}
   1027 	if (Playersfp)
   1028 		fclose(Playersfp);
   1029 	if (Monstfp)
   1030 		fclose(Monstfp);
   1031 	if (Messagefp)
   1032 		fclose(Messagefp);
   1033 	if (Energyvoidfp)
   1034 		fclose(Energyvoidfp);
   1035 
   1036 	if (doexit)
   1037 		exit(0);
   1038 	/* NOTREACHED */
   1039 }
   1040