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