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