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