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