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