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