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