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