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