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