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
63void
64changestats(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		}
466DALTER:
467		mvprintw(23, 0, "%s = %f; %s = ", prompt, *dptr, prompt);
468		dtemp = infloat();
469		if (dtemp != 0.0)
470			*dptr = dtemp;
471		continue;
472
473SALTER:
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
480BALTER:
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
516void
517monstlist(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
553void
554scorelist(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
590void
591activelist(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
628void
629purgeoldplayers(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(&ltime);
638	today = localtime(&ltime)->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
685void
686enterscore(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