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