Merge branch 'vendor/GCC44'
[dragonfly.git] / games / phantasia / gamesupport.c
1 /*
2  * gamesupport.c - auxiliary routines for support of Phantasia
3  *
4  * $FreeBSD: src/games/phantasia/gamesupport.c,v 1.6 1999/11/16 02:57:33 billf Exp $
5  * $DragonFly: src/games/phantasia/gamesupport.c,v 1.3 2005/05/31 00:06:26 swildner Exp $
6  */
7
8 #include <string.h>
9 #include "include.h"
10
11 /* functions which we need to know about */
12 /* interplayer.c */
13 extern  void    userlist(bool);
14 /* io.c */
15 extern  int     getanswer(const char *, bool);
16 extern  void    getstring(char *, int);
17 extern  double  infloat(void);
18 extern  void    more(int);
19 /* main.c */
20 extern  void    cleanup(bool);
21 /* misc.c */
22 extern  const char      *descrstatus(struct player *);
23 extern  const char      *descrtype(struct player *, bool);
24 extern  void    error(const char *);
25 extern  long    findname(char *, struct player *);
26 extern  void    freerecord(struct player *, long);
27 extern  void    truncstring(char *);
28 extern  void    writerecord(struct player *, long);
29
30 void    activelist(void);
31 void    changestats(bool);
32 void    enterscore(void);
33 void    monstlist(void);
34 void    purgeoldplayers(void);
35 void    scorelist(void);
36
37 /*
38  * FUNCTION: examine/change statistics for a player
39  *
40  * ARGUMENTS:
41  *      bool ingameflag - set if called while playing game (Wizard only)
42  *
43  * GLOBAL INPUTS: LINES, *Login, Other, Wizard, Player, *stdscr, Databuf[],
44  *      Fileloc
45  *
46  * GLOBAL OUTPUTS: Echo
47  *
48  * DESCRIPTION:
49  *      Prompt for player name to examine/change.
50  *      If the name is NULL, print a list of all players.
51  *      If we are called from within the game, check for the
52  *      desired name being the same as the current player's name.
53  *      Only the 'Wizard' may alter players.
54  *      Items are changed only if a non-zero value is specified.
55  *      To change an item to 0, use 0.1; it will be truncated later.
56  *
57  *      Players may alter their names and passwords, if the following
58  *      are true:
59  *          - current login matches the character's logins
60  *          - the password is known
61  *          - the player is not in the middle of the game (ingameflag == FALSE)
62  *
63  *      The last condition is imposed for two reasons:
64  *          - the game could possibly get a bit hectic if a player were
65  *            continually changing his/her name
66  *          - another player structure would be necessary to check for names
67  *            already in use
68  */
69
70 void
71 changestats(bool ingameflag)
72 {
73         static char flag[2] =   /* for printing values of bools */
74         { 'F', 'T' };
75         struct player *playerp; /* pointer to structure to alter */
76         const char *prompt;     /* pointer to prompt string */
77         int c;                  /* input */
78         int today;              /* day of year of today */
79         int temp;               /* temporary variable */
80         long loc;               /* location in player file */
81         time_t now;             /* time now */
82         double dtemp;           /* temporary variable */
83         bool *bptr;             /* pointer to bool item to change */
84         double *dptr;           /* pointer to double item to change */
85         short *sptr;            /* pointer to short item to change */
86
87         clear();
88
89         for (;;) {
90                 /* get name of player to examine/alter */
91                 mvaddstr(5, 0, "Which character do you want to look at ? ");
92                 getstring(Databuf, SZ_DATABUF);
93                 truncstring(Databuf);
94
95                 if (Databuf[0] == '\0')
96                         userlist(ingameflag);
97                 else
98                         break;
99         }
100
101         loc = -1L;
102
103         if (!ingameflag)
104                 /* use 'Player' structure */
105                 playerp = &Player;
106         else if (strcmp(Databuf, Player.p_name) == 0) {
107                 /* alter/examine current player */
108                 playerp = &Player;
109                 loc = Fileloc;
110         } else
111                 /* use 'Other' structure */
112                 playerp = &Other;
113
114         /* find player on file */
115         if (loc < 0L && (loc = findname(Databuf, playerp)) < 0L) {
116                 /* didn't find player */
117                 clear();
118                 mvaddstr(11, 0, "Not found.");
119                 return;
120         }
121
122         time(&now);
123         today = localtime(&now)->tm_yday;
124
125         clear();
126
127         for (;;) {
128                 /* print player structure, and prompt for action */
129                 mvprintw(0, 0, "A:Name         %s\n", playerp->p_name);
130
131                 if (Wizard)
132                         printw("B:Password     %s\n", playerp->p_password);
133                 else
134                         addstr("B:Password     XXXXXXXX\n");
135
136                 printw(" :Login        %s\n", playerp->p_login);
137
138                 printw("C:Experience   %.0f\n", playerp->p_experience);
139                 printw("D:Level        %.0f\n", playerp->p_level);
140                 printw("E:Strength     %.0f\n", playerp->p_strength);
141                 printw("F:Sword        %.0f\n", playerp->p_sword);
142                 printw(" :Might        %.0f\n", playerp->p_might);
143                 printw("G:Energy       %.0f\n", playerp->p_energy);
144                 printw("H:Max-Energy   %.0f\n", playerp->p_maxenergy);
145                 printw("I:Shield       %.0f\n", playerp->p_shield);
146                 printw("J:Quickness    %.0f\n", playerp->p_quickness);
147                 printw("K:Quicksilver  %.0f\n", playerp->p_quksilver);
148                 printw(" :Speed        %.0f\n", playerp->p_speed);
149                 printw("L:Magic Level  %.0f\n", playerp->p_magiclvl);
150                 printw("M:Mana         %.0f\n", playerp->p_mana);
151                 printw("N:Brains       %.0f\n", playerp->p_brains);
152
153                 if (Wizard || playerp->p_specialtype != SC_VALAR)
154                         mvaddstr(0, 40, descrstatus(playerp));
155
156                 mvprintw(1, 40, "O:Poison       %0.3f\n", playerp->p_poison);
157                 mvprintw(2, 40, "P:Gold         %.0f\n", playerp->p_gold);
158                 mvprintw(3, 40, "Q:Gem          %.0f\n", playerp->p_gems);
159                 mvprintw(4, 40, "R:Sin          %0.3f\n", playerp->p_sin);
160                 if (Wizard) {
161                         mvprintw(5, 40, "S:X-coord      %.0f\n", playerp->p_x);
162                         mvprintw(6, 40, "T:Y-coord      %.0f\n", playerp->p_y);
163                 } else {
164                         mvaddstr(5, 40, "S:X-coord      ?\n");
165                         mvaddstr(6, 40, "T:Y-coord      ?\n");
166                 }
167
168                 mvprintw(7, 40, "U:Age          %ld\n", playerp->p_age);
169                 mvprintw(8, 40, "V:Degenerated  %d\n", playerp->p_degenerated);
170
171                 mvprintw(9, 40, "W:Type         %d (%s)\n",
172                     playerp->p_type, descrtype(playerp, FALSE) + 1);
173                 mvprintw(10, 40, "X:Special Type %d\n", playerp->p_specialtype);
174                 mvprintw(11, 40, "Y:Lives        %d\n", playerp->p_lives);
175                 mvprintw(12, 40, "Z:Crowns       %d\n", playerp->p_crowns);
176                 mvprintw(13, 40, "0:Charms       %d\n", playerp->p_charms);
177                 mvprintw(14, 40, "1:Amulets      %d\n", playerp->p_amulets);
178                 mvprintw(15, 40, "2:Holy Water   %d\n", playerp->p_holywater);
179
180                 temp = today - playerp->p_lastused;
181                 if (temp < 0)
182                         /* last year */
183                         temp += 365;
184                 mvprintw(16, 40, "3:Lastused     %d  (%d)\n", playerp->p_lastused, temp);
185
186                 mvprintw(18, 8, "4:Palantir %c  5:Blessing %c  6:Virgin %c  7:Blind %c",
187                     flag[playerp->p_palantir],
188                     flag[playerp->p_blessing],
189                     flag[playerp->p_virgin],
190                     flag[playerp->p_blindness]);
191
192                 if (!Wizard)
193                         mvprintw(19, 8, "8:Ring    %c",
194                             flag[playerp->p_ring.ring_type != R_NONE]);
195                 else
196                         mvprintw(19, 8, "8:Ring    %d  9:Duration %d",
197                             playerp->p_ring.ring_type, playerp->p_ring.ring_duration);
198
199                 if (!Wizard
200                     && (ingameflag || strcmp(Login, playerp->p_login) != 0)) {
201                         /* in game or not examining own character */
202                         if (ingameflag) {
203                                 more(LINES - 1);
204                                 clear();
205                                 return;
206                         } else
207                                 cleanup(TRUE);
208                         /* NOTREACHED */
209                 }
210
211                 mvaddstr(20, 0, "!:Quit       ?:Delete");
212                 mvaddstr(21, 0, "What would you like to change ? ");
213
214                 if (Wizard)
215                         c = getanswer(" ", TRUE);
216                 else
217                         /* examining own player; allow to change name and password */
218                         c = getanswer("!BA", FALSE);
219
220                 switch (c) {
221                 case 'A':       /* change name */
222                 case 'B':       /* change password */
223                         if (!Wizard) {
224                                 /* prompt for password */
225                                 mvaddstr(23, 0, "Password ? ");
226                                 Echo = FALSE;
227                                 getstring(Databuf, 9);
228                                 Echo = TRUE;
229                                 if (strcmp(Databuf, playerp->p_password) != 0)
230                                         continue;
231                         }
232                         if (c == 'A') {
233                                 /* get new name */
234                                 mvaddstr(23, 0, "New name: ");
235                                 getstring(Databuf, SZ_NAME);
236                                 truncstring(Databuf);
237                                 if (Databuf[0] != '\0')
238                                         if (Wizard || findname(Databuf, &Other) < 0L)
239                                                 strcpy(playerp->p_name, Databuf);
240                         } else {
241                                 /* get new password */
242                                 if (!Wizard)
243                                         Echo = FALSE;
244
245                                 do {
246                                         /* get two copies of new password until they match */
247                                         /* get first copy */
248                                         mvaddstr(23, 0, "New password ? ");
249                                         getstring(Databuf, SZ_PASSWORD);
250                                         if (Databuf[0] == '\0')
251                                                 break;
252
253                                         /* get second copy */
254                                         mvaddstr(23, 0, "One more time ? ");
255                                         getstring(playerp->p_password, SZ_PASSWORD);
256                                 } while (strcmp(playerp->p_password, Databuf) != 0);
257
258                                 Echo = TRUE;
259                         }
260
261                         continue;
262
263                 case 'C':       /* change experience */
264                         prompt = "experience";
265                         dptr = &playerp->p_experience;
266                         goto DALTER;
267
268                 case 'D':       /* change level */
269                         prompt = "level";
270                         dptr = &playerp->p_level;
271                         goto DALTER;
272
273                 case 'E':       /* change strength */
274                         prompt = "strength";
275                         dptr = &playerp->p_strength;
276                         goto DALTER;
277
278                 case 'F':       /* change swords */
279                         prompt = "sword";
280                         dptr = &playerp->p_sword;
281                         goto DALTER;
282
283                 case 'G':       /* change energy */
284                         prompt = "energy";
285                         dptr = &playerp->p_energy;
286                         goto DALTER;
287
288                 case 'H':       /* change maximum energy */
289                         prompt = "max energy";
290                         dptr = &playerp->p_maxenergy;
291                         goto DALTER;
292
293                 case 'I':       /* change shields */
294                         prompt = "shield";
295                         dptr = &playerp->p_shield;
296                         goto DALTER;
297
298                 case 'J':       /* change quickness */
299                         prompt = "quickness";
300                         dptr = &playerp->p_quickness;
301                         goto DALTER;
302
303                 case 'K':       /* change quicksilver */
304                         prompt = "quicksilver";
305                         dptr = &playerp->p_quksilver;
306                         goto DALTER;
307
308                 case 'L':       /* change magic */
309                         prompt = "magic level";
310                         dptr = &playerp->p_magiclvl;
311                         goto DALTER;
312
313                 case 'M':       /* change mana */
314                         prompt = "mana";
315                         dptr = &playerp->p_mana;
316                         goto DALTER;
317
318                 case 'N':       /* change brains */
319                         prompt = "brains";
320                         dptr = &playerp->p_brains;
321                         goto DALTER;
322
323                 case 'O':       /* change poison */
324                         prompt = "poison";
325                         dptr = &playerp->p_poison;
326                         goto DALTER;
327
328                 case 'P':       /* change gold */
329                         prompt = "gold";
330                         dptr = &playerp->p_gold;
331                         goto DALTER;
332
333                 case 'Q':       /* change gems */
334                         prompt = "gems";
335                         dptr = &playerp->p_gems;
336                         goto DALTER;
337
338                 case 'R':       /* change sin */
339                         prompt = "sin";
340                         dptr = &playerp->p_sin;
341                         goto DALTER;
342
343                 case 'S':       /* change x coord */
344                         prompt = "x";
345                         dptr = &playerp->p_x;
346                         goto DALTER;
347
348                 case 'T':       /* change y coord */
349                         prompt = "y";
350                         dptr = &playerp->p_y;
351                         goto DALTER;
352
353                 case 'U':       /* change age */
354                         mvprintw(23, 0, "age = %ld; age = ", playerp->p_age);
355                         dtemp = infloat();
356                         if (dtemp != 0.0)
357                                 playerp->p_age = (long)dtemp;
358                         continue;
359
360                 case 'V':       /* change degen */
361                         mvprintw(23, 0, "degen = %d; degen = ", playerp->p_degenerated);
362                         dtemp = infloat();
363                         if (dtemp != 0.0)
364                                 playerp->p_degenerated = (int)dtemp;
365                         continue;
366
367                 case 'W':       /* change type */
368                         prompt = "type";
369                         sptr = &playerp->p_type;
370                         goto SALTER;
371
372                 case 'X':       /* change special type */
373                         prompt = "special type";
374                         sptr = &playerp->p_specialtype;
375                         goto SALTER;
376
377                 case 'Y':       /* change lives */
378                         prompt = "lives";
379                         sptr = &playerp->p_lives;
380                         goto SALTER;
381
382                 case 'Z':       /* change crowns */
383                         prompt = "crowns";
384                         sptr = &playerp->p_crowns;
385                         goto SALTER;
386
387                 case '0':       /* change charms */
388                         prompt = "charm";
389                         sptr = &playerp->p_charms;
390                         goto SALTER;
391
392                 case '1':       /* change amulet */
393                         prompt = "amulet";
394                         sptr = &playerp->p_amulets;
395                         goto SALTER;
396
397                 case '2':       /* change holy water */
398                         prompt = "holy water";
399                         sptr = &playerp->p_holywater;
400                         goto SALTER;
401
402                 case '3':       /* change last-used */
403                         prompt = "last-used";
404                         sptr = &playerp->p_lastused;
405                         goto SALTER;
406
407                 case '4':       /* change palantir */
408                         prompt = "palantir";
409                         bptr = &playerp->p_palantir;
410                         goto BALTER;
411
412                 case '5':       /* change blessing */
413                         prompt = "blessing";
414                         bptr = &playerp->p_blessing;
415                         goto BALTER;
416
417                 case '6':       /* change virgin */
418                         prompt = "virgin";
419                         bptr = &playerp->p_virgin;
420                         goto BALTER;
421
422                 case '7':       /* change blindness */
423                         prompt = "blindness";
424                         bptr = &playerp->p_blindness;
425                         goto BALTER;
426
427                 case '8':       /* change ring type */
428                         prompt = "ring-type";
429                         sptr = &playerp->p_ring.ring_type;
430                         goto SALTER;
431
432                 case '9':       /* change ring duration */
433                         prompt = "ring-duration";
434                         sptr = &playerp->p_ring.ring_duration;
435                         goto SALTER;
436
437                 case '!':       /* quit, update */
438                         if (Wizard &&
439                             (!ingameflag || playerp != &Player)) {
440                                 /* turn off status if not modifying self */
441                                 playerp->p_status = S_OFF;
442                                 playerp->p_tampered = T_OFF;
443                         }
444
445                         writerecord(playerp, loc);
446                         clear();
447                         return;
448
449                 case '?':       /* delete player */
450                         if (ingameflag && playerp == &Player)
451                                 /* cannot delete self */
452                                 continue;
453
454                         freerecord(playerp, loc);
455                         clear();
456                         return;
457
458                 default:
459                         continue;
460                 }
461 DALTER:
462                 mvprintw(23, 0, "%s = %f; %s = ", prompt, *dptr, prompt);
463                 dtemp = infloat();
464                 if (dtemp != 0.0)
465                         *dptr = dtemp;
466                 continue;
467
468 SALTER:
469                 mvprintw(23, 0, "%s = %d; %s = ", prompt, *sptr, prompt);
470                 dtemp = infloat();
471                 if (dtemp != 0.0)
472                         *sptr = (short)dtemp;
473                 continue;
474
475 BALTER:
476                 mvprintw(23, 0, "%s = %c; %s = ", prompt, flag[*bptr],
477                     prompt);
478                 c = getanswer("\nTF", TRUE);
479                 if (c == 'T')
480                         *bptr = TRUE;
481                 else if (c == 'F')
482                         *bptr = FALSE;
483                 continue;
484         }
485 }
486
487 /*
488  * FUNCTION: print a monster listing
489  *
490  * GLOBAL INPUTS: Curmonster, *Monstfp
491  *
492  * DESCRIPTION:
493  *      Read monster file, and print a monster listing on standard output.
494  */
495
496 void
497 monstlist(void)
498 {
499         int count = 0;  /* count in file */
500
501         puts(" #)  Name                 Str  Brain  Quick  Energy  Exper  Treas  Type  Flock%\n");
502         fseek(Monstfp, 0L, SEEK_SET);
503         while (fread((char *)&Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp) == 1)
504                 printf("%2d)  %-20.20s%4.0f   %4.0f     %2.0f   %5.0f  %5.0f     %2d    %2d     %3.0f\n", count++,
505                     Curmonster.m_name, Curmonster.m_strength, Curmonster.m_brains,
506                     Curmonster.m_speed, Curmonster.m_energy, Curmonster.m_experience,
507                     Curmonster.m_treasuretype, Curmonster.m_type, Curmonster.m_flock);
508 }
509
510 /*
511  * FUNCTION: print player score board
512  *
513  * DESCRIPTION:
514  *      Read the scoreboard file and print the contents.
515  */
516
517 void
518 scorelist(void)
519 {
520         struct scoreboard sbuf; /* for reading entries */
521         FILE *fp;               /* to open the file */
522
523         if ((fp = fopen(_PATH_SCORE, "r")) != NULL) {
524                 while (fread((char *)&sbuf, SZ_SCORESTRUCT, 1, fp) == 1)
525                         printf("%-20s   (%-9s)  Level: %6.0f  Type: %s\n",
526                             sbuf.sb_name, sbuf.sb_login, sbuf.sb_level, sbuf.sb_type);
527                 fclose(fp);
528         }
529 }
530
531 /*
532  * FUNCTION: print list of active players to standard output
533  *
534  * GLOBAL INPUTS: Other, *Playersfp
535  *
536  * DESCRIPTION:
537  *      Read player file, and print list of active records to standard output.
538  */
539
540 void
541 activelist(void)
542 {
543         fseek(Playersfp, 0L, SEEK_SET);
544         printf("Current characters on file are:\n\n");
545
546         while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
547                 if (Other.p_status != S_NOTUSED)
548                         printf("%-20s   (%-9s)  Level: %6.0f  %s  (%s)\n",
549                             Other.p_name, Other.p_login, Other.p_level,
550                             descrtype(&Other, FALSE), descrstatus(&Other));
551 }
552
553 /*
554  * FUNCTION: purge inactive players from player file
555  *
556  * GLOBAL INPUTS: Other, *Playersfp
557  *
558  * DESCRIPTION:
559  *      Delete characters which have not been used with the last
560  *      three weeks.
561  */
562
563 void
564 purgeoldplayers(void)
565 {
566         int today;      /* day of year for today */
567         int daysold;    /* how many days since the character has been used */
568         time_t ltime;   /* time in seconds */
569         long loc = 0L;  /* location in file */
570
571         time(&ltime);
572         today = localtime(&ltime)->tm_yday;
573
574         for (;;) {
575                 fseek(Playersfp, loc, SEEK_SET);
576                 if (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) != 1)
577                         break;
578
579                 daysold = today - Other.p_lastused;
580                 if (daysold < 0)
581                         daysold += 365;
582
583                 if (daysold > N_DAYSOLD)
584                         /* player hasn't been used in a while; delete */
585                         freerecord(&Other, loc);
586
587                 loc += SZ_PLAYERSTRUCT;
588         }
589 }
590
591 /*
592  * FUNCTION: enter player into scoreboard
593  *
594  * GLOBAL INPUTS: Player
595  *
596  * DESCRIPTION:
597  *      The scoreboard keeps track of the highest character on a
598  *      per-login basis.
599  *      Search the scoreboard for an entry for the current login,
600  *      if an entry is found, and it is lower than the current player,
601  *      replace it, otherwise create an entry.
602  */
603
604 void
605 enterscore(void)
606 {
607         struct scoreboard sbuf; /* buffer to read in scoreboard entries */
608         FILE *fp;               /* to open scoreboard file */
609         long loc = 0L;          /* location in scoreboard file */
610         bool found = FALSE;     /* set if we found an entry for this login */
611
612         if ((fp = fopen(_PATH_SCORE, "r+")) != NULL) {
613                 while (fread((char *)&sbuf, SZ_SCORESTRUCT, 1, fp) == 1)
614                         if (strcmp(Player.p_login, sbuf.sb_login) == 0) {
615                                 found = TRUE;
616                                 break;
617                         } else
618                                 loc += SZ_SCORESTRUCT;
619         } else {
620                 error(_PATH_SCORE);
621                 /* NOTREACHED */
622         }
623
624         /*
625          * At this point, 'loc' will either indicate a point beyond
626          * the end of file, or the place where the previous entry
627          * was found.
628          */
629
630         if ((!found) || Player.p_level > sbuf.sb_level) {
631                 /* put new entry in for this login */
632                 strcpy(sbuf.sb_login, Player.p_login);
633                 strcpy(sbuf.sb_name, Player.p_name);
634                 sbuf.sb_level = Player.p_level;
635                 strcpy(sbuf.sb_type, descrtype(&Player, TRUE));
636         }
637
638         /* update entry */
639         fseek(fp, loc, SEEK_SET);
640         fwrite((char *)&sbuf, SZ_SCORESTRUCT, 1, fp);
641         fclose(fp);
642 }