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