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