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