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