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