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