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