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