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