1/*	$NetBSD: misc.c,v 1.20 2011/08/31 16:24:56 plunky Exp $	*/
2
3/*
4 * misc.c  Phantasia miscellaneous support routines
5 */
6
7#include <errno.h>
8#include <math.h>
9#include <setjmp.h>
10#include <signal.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16#include "macros.h"
17#include "phantdefs.h"
18#include "phantstruct.h"
19#include "phantglobs.h"
20#include "pathnames.h"
21
22#undef bool
23#include <curses.h>
24
25
26static double explevel(double);
27
28static void
29movelevel(void)
30{
31	const struct charstats *statptr; /* for pointing into Stattable */
32	double  new;		/* new level */
33	double  inc;		/* increment between new and old levels */
34
35	Changed = TRUE;
36
37	if (Player.p_type == C_EXPER)
38		/* roll a type to use for increment */
39		statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
40	else
41		statptr = Statptr;
42
43	new = explevel(Player.p_experience);
44	inc = new - Player.p_level;
45	Player.p_level = new;
46
47	/* add increments to statistics */
48	Player.p_strength += statptr->c_strength.increase * inc;
49	Player.p_mana += statptr->c_mana.increase * inc;
50	Player.p_brains += statptr->c_brains.increase * inc;
51	Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
52	Player.p_maxenergy += statptr->c_energy.increase * inc;
53
54	/* rest to maximum upon reaching new level */
55	Player.p_energy = Player.p_maxenergy + Player.p_shield;
56
57	if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
58		/* no longer able to be king -- turn crowns into cash */
59	{
60		Player.p_gold += ((double) Player.p_crowns) * 5000.0;
61		Player.p_crowns = 0;
62	}
63	if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
64		/* make a member of the council */
65	{
66		mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
67		addstr("Good Luck on your search for the Holy Grail.\n");
68
69		Player.p_specialtype = SC_COUNCIL;
70
71		/* no rings for council and above */
72		Player.p_ring.ring_type = R_NONE;
73		Player.p_ring.ring_duration = 0;
74
75		Player.p_lives = 3;	/* three extra lives */
76	}
77	if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
78		death("Old age");
79}
80
81const char   *
82descrlocation(struct player *playerp, phbool shortflag)
83{
84	double  circle;		/* corresponding circle for coordinates */
85	int     quadrant;	/* quandrant of grid */
86	const char   *label;	/* pointer to place name */
87	static const char *const nametable[4][4] =	/* names of places */
88	{
89		{"Anorien", "Ithilien", "Rohan", "Lorien"},
90		{"Gondor", "Mordor", "Dunland", "Rovanion"},
91		{"South Gondor", "Khand", "Eriador", "The Iron Hills"},
92		{"Far Harad", "Near Harad", "The Northern Waste", "Rhun"}
93	};
94
95	if (playerp->p_specialtype == SC_VALAR)
96		return (" is in Valhala");
97	else
98		if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0) {
99			if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
100				label = "The Point of No Return";
101			else
102				label = "The Ashen Mountains";
103		} else
104			if (circle >= 55)
105				label = "Morannon";
106			else
107				if (circle >= 35)
108					label = "Kennaquahair";
109				else
110					if (circle >= 20)
111						label = "The Dead Marshes";
112					else
113						if (circle >= 9)
114							label = "The Outer Waste";
115						else
116							if (circle >= 5)
117								label = "The Moors Adventurous";
118							else {
119								if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
120									label = "The Lord's Chamber";
121								else {
122									/* this
123									 *
124									 * expr
125									 * essi
126									 * on
127									 * is
128									 * spli
129									 * t
130									 * to
131									 * prev
132									 * ent
133									 * comp
134									 * iler
135									 *
136									 * loop
137									 *
138									 * with
139									 *
140									 * some
141									 *
142									 * comp
143									 * iler
144									 * s */
145									quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
146									quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
147									label = nametable[((int) circle) - 1][quadrant];
148								}
149							}
150
151	if (shortflag)
152		snprintf(Databuf, SZ_DATABUF, "%.29s", label);
153	else
154		snprintf(Databuf, SZ_DATABUF,
155			" is in %s  (%.0f,%.0f)",
156			label, playerp->p_x, playerp->p_y);
157
158	return (Databuf);
159}
160
161void
162tradingpost(void)
163{
164	double  numitems;	/* number of items to purchase */
165	double  cost;		/* cost of purchase */
166	double  blessingcost;	/* cost of blessing */
167	int     ch;		/* input */
168	int     size;		/* size of the trading post */
169	int     loop;		/* loop counter */
170	int     cheat = 0;	/* number of times player has tried to cheat */
171	bool    dishonest = FALSE;	/* set when merchant is dishonest */
172
173	Player.p_status = S_TRADING;
174	writerecord(&Player, Fileloc);
175
176	clear();
177	addstr("You are at a trading post. All purchases must be made with gold.");
178
179	size = sqrt(fabs(Player.p_x / 100)) + 1;
180	size = MIN(7, size);
181
182	/* set up cost of blessing */
183	blessingcost = 1000.0 * (Player.p_level + 5.0);
184
185	/* print Menu */
186	move(7, 0);
187	for (loop = 0; loop < size; ++loop)
188		/* print Menu */
189	{
190		if (loop == 6)
191			cost = blessingcost;
192		else
193			cost = Menu[loop].cost;
194		printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
195	}
196
197	mvprintw(5, 0, "L:Leave  P:Purchase  S:Sell Gems ? ");
198
199	for (;;) {
200		adjuststats();	/* truncate any bad values */
201
202		/* print some important statistics */
203		mvprintw(1, 0, "Gold:   %9.0f  Gems:  %9.0f  Level:   %6.0f  Charms: %6d\n",
204		    Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
205		printw("Shield: %9.0f  Sword: %9.0f  Quicksilver:%3.0f  Blessed: %s\n",
206		    Player.p_shield, Player.p_sword, Player.p_quksilver,
207		    (Player.p_blessing ? " True" : "False"));
208		printw("Brains: %9.0f  Mana:  %9.0f", Player.p_brains, Player.p_mana);
209
210		move(5, 36);
211		ch = getanswer("LPS", FALSE);
212		move(15, 0);
213		clrtobot();
214		switch (ch) {
215		case 'L':	/* leave */
216		case '\n':
217			altercoordinates(0.0, 0.0, A_NEAR);
218			return;
219
220		case 'P':	/* make purchase */
221			mvaddstr(15, 0, "What what would you like to buy ? ");
222			ch = getanswer(" 1234567", FALSE);
223			move(15, 0);
224			clrtoeol();
225
226			if (ch - '0' > size)
227				addstr("Sorry, this merchant doesn't have that.");
228			else
229				switch (ch) {
230				case '1':
231					printw("Mana is one per %.0f gold piece.  How many do you want (%.0f max) ? ",
232					    Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
233					cost = (numitems = floor(infloat())) * Menu[0].cost;
234
235					if (cost > Player.p_gold || numitems < 0)
236						++cheat;
237					else {
238						cheat = 0;
239						Player.p_gold -= cost;
240						if (drandom() < 0.02)
241							dishonest = TRUE;
242						else
243							Player.p_mana += numitems;
244					}
245					break;
246
247				case '2':
248					printw("Shields are %.0f per +1.  How many do you want (%.0f max) ? ",
249					    Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
250					cost = (numitems = floor(infloat())) * Menu[1].cost;
251
252					if (numitems == 0.0)
253						break;
254					else
255						if (cost > Player.p_gold || numitems < 0)
256							++cheat;
257						else
258							if (numitems < Player.p_shield)
259								NOBETTER();
260							else {
261								cheat = 0;
262								Player.p_gold -= cost;
263								if (drandom() < 0.02)
264									dishonest = TRUE;
265								else
266									Player.p_shield = numitems;
267							}
268					break;
269
270				case '3':
271					printw("A book costs %.0f gp.  How many do you want (%.0f max) ? ",
272					    Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
273					cost = (numitems = floor(infloat())) * Menu[2].cost;
274
275					if (cost > Player.p_gold || numitems < 0)
276						++cheat;
277					else {
278						cheat = 0;
279						Player.p_gold -= cost;
280						if (drandom() < 0.02)
281							dishonest = TRUE;
282						else
283							if (drandom() * numitems > Player.p_level / 10.0
284							    && numitems != 1) {
285								printw("\nYou blew your mind!\n");
286								Player.p_brains /= 5;
287							} else {
288								Player.p_brains += floor(numitems) * ROLL(20, 8);
289							}
290					}
291					break;
292
293				case '4':
294					printw("Swords are %.0f gp per +1.  How many + do you want (%.0f max) ? ",
295					    Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
296					cost = (numitems = floor(infloat())) * Menu[3].cost;
297
298					if (numitems == 0.0)
299						break;
300					else
301						if (cost > Player.p_gold || numitems < 0)
302							++cheat;
303						else
304							if (numitems < Player.p_sword)
305								NOBETTER();
306							else {
307								cheat = 0;
308								Player.p_gold -= cost;
309								if (drandom() < 0.02)
310									dishonest = TRUE;
311								else
312									Player.p_sword = numitems;
313							}
314					break;
315
316				case '5':
317					printw("A charm costs %.0f gp.  How many do you want (%.0f max) ? ",
318					    Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
319					cost = (numitems = floor(infloat())) * Menu[4].cost;
320
321					if (cost > Player.p_gold || numitems < 0)
322						++cheat;
323					else {
324						cheat = 0;
325						Player.p_gold -= cost;
326						if (drandom() < 0.02)
327							dishonest = TRUE;
328						else
329							Player.p_charms += numitems;
330					}
331					break;
332
333				case '6':
334					printw("Quicksilver is %.0f gp per +1.  How many + do you want (%.0f max) ? ",
335					    Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
336					cost = (numitems = floor(infloat())) * Menu[5].cost;
337
338					if (numitems == 0.0)
339						break;
340					else
341						if (cost > Player.p_gold || numitems < 0)
342							++cheat;
343						else
344							if (numitems < Player.p_quksilver)
345								NOBETTER();
346							else {
347								cheat = 0;
348								Player.p_gold -= cost;
349								if (drandom() < 0.02)
350									dishonest = TRUE;
351								else
352									Player.p_quksilver = numitems;
353							}
354					break;
355
356				case '7':
357					if (Player.p_blessing) {
358						addstr("You already have a blessing.");
359						break;
360					}
361					printw("A blessing requires a %.0f gp donation.  Still want one ? ", blessingcost);
362					ch = getanswer("NY", FALSE);
363
364					if (ch == 'Y') {
365						if (Player.p_gold < blessingcost)
366							++cheat;
367						else {
368							cheat = 0;
369							Player.p_gold -= blessingcost;
370							if (drandom() < 0.02)
371								dishonest = TRUE;
372							else
373								Player.p_blessing = TRUE;
374						}
375					}
376					break;
377				}
378			break;
379
380		case 'S':	/* sell gems */
381			mvprintw(15, 0, "A gem is worth %.0f gp.  How many do you want to sell (%.0f max) ? ",
382			    (double) N_GEMVALUE, Player.p_gems);
383			numitems = floor(infloat());
384
385			if (numitems > Player.p_gems || numitems < 0)
386				++cheat;
387			else {
388				cheat = 0;
389				Player.p_gems -= numitems;
390				Player.p_gold += numitems * N_GEMVALUE;
391			}
392		}
393
394		if (cheat == 1)
395			mvaddstr(17, 0, "Come on, merchants aren't stupid.  Stop cheating.\n");
396		else
397			if (cheat == 2) {
398				mvaddstr(17, 0, "You had your chance.  This merchant happens to be\n");
399				printw("a %.0f level magic user, and you made %s mad!\n",
400				    ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
401				altercoordinates(0.0, 0.0, A_FAR);
402				Player.p_energy /= 2.0;
403				++Player.p_sin;
404				more(23);
405				return;
406			} else
407				if (dishonest) {
408					mvaddstr(17, 0, "The merchant stole your money!");
409					refresh();
410					altercoordinates(Player.p_x - Player.p_x / 10.0,
411					    Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
412					sleep(2);
413					return;
414				}
415	}
416}
417
418void
419displaystats(void)
420{
421	mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
422	mvprintw(1, 0, "Level :%7.0f   Energy  :%9.0f(%9.0f)  Mana :%9.0f  Users:%3d\n",
423	    Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
424	    Player.p_mana, Users);
425	mvprintw(2, 0, "Quick :%3.0f(%3.0f)  Strength:%9.0f(%9.0f)  Gold :%9.0f  %s\n",
426	    Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
427	    Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
428}
429
430void
431allstatslist(void)
432{
433	static const char *const flags[] = /* to print value of some bools */
434	{
435		"False",
436		" True"
437	};
438
439	mvprintw(8, 0, "Type: %s\n", descrtype(&Player, FALSE));
440
441	mvprintw(10, 0, "Experience: %9.0f", Player.p_experience);
442	mvprintw(11, 0, "Brains    : %9.0f", Player.p_brains);
443	mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl);
444	mvprintw(13, 0, "Sin       : %9.5f", Player.p_sin);
445	mvprintw(14, 0, "Poison    : %9.5f", Player.p_poison);
446	mvprintw(15, 0, "Gems      : %9.0f", Player.p_gems);
447	mvprintw(16, 0, "Age       : %9ld", Player.p_age);
448	mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
449	mvprintw(11, 40, "Amulets   : %9d", Player.p_amulets);
450	mvprintw(12, 40, "Charms    : %9d", Player.p_charms);
451	mvprintw(13, 40, "Crowns    : %9d", Player.p_crowns);
452	mvprintw(14, 40, "Shield    : %9.0f", Player.p_shield);
453	mvprintw(15, 40, "Sword     : %9.0f", Player.p_sword);
454	mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
455
456	mvprintw(18, 0, "Blessing: %s   Ring: %s   Virgin: %s   Palantir: %s",
457	    flags[(int)Player.p_blessing],
458	    flags[Player.p_ring.ring_type != R_NONE],
459	    flags[(int)Player.p_virgin],
460	    flags[(int)Player.p_palantir]);
461}
462
463const char   *
464descrtype(struct player *playerp, phbool shortflag)
465{
466	int     type;		/* for caluculating result subscript */
467	static const char *const results[] =/* description table */
468	{
469		" Magic User", " MU",
470		" Fighter", " F ",
471		" Elf", " E ",
472		" Dwarf", " D ",
473		" Halfling", " H ",
474		" Experimento", " EX",
475		" Super", " S ",
476		" King", " K ",
477		" Council of Wise", " CW",
478		" Ex-Valar", " EV",
479		" Valar", " V ",
480		" ? ", " ? "
481	};
482
483	type = playerp->p_type;
484
485	switch (playerp->p_specialtype) {
486	case SC_NONE:
487		type = playerp->p_type;
488		break;
489
490	case SC_KING:
491		type = 7;
492		break;
493
494	case SC_COUNCIL:
495		type = 8;
496		break;
497
498	case SC_EXVALAR:
499		type = 9;
500		break;
501
502	case SC_VALAR:
503		type = 10;
504		break;
505	}
506
507	type *= 2;		/* calculate offset */
508
509	if (type > 20)
510		/* error */
511		type = 22;
512
513	if (shortflag)
514		/* use short descriptions */
515		++type;
516
517	if (playerp->p_crowns > 0) {
518		strcpy(Databuf, results[type]);
519		Databuf[0] = '*';
520		return (Databuf);
521	} else
522		return (results[type]);
523}
524
525long
526findname(const char *name, struct player *playerp)
527{
528	long    loc = 0;	/* location in the file */
529
530	fseek(Playersfp, 0L, SEEK_SET);
531	while (fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
532		if (strcmp(playerp->p_name, name) == 0) {
533			if (playerp->p_status != S_NOTUSED || Wizard)
534				/* found it */
535				return (loc);
536		}
537		loc += SZ_PLAYERSTRUCT;
538	}
539
540	return (-1);
541}
542
543long
544allocrecord(void)
545{
546	long    loc = 0L;	/* location in file */
547
548	fseek(Playersfp, 0L, SEEK_SET);
549	while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
550		if (Other.p_status == S_NOTUSED)
551			/* found an empty record */
552			return (loc);
553		else
554			loc += SZ_PLAYERSTRUCT;
555	}
556
557	/* make a new record */
558	initplayer(&Other);
559	Player.p_status = S_OFF;
560	writerecord(&Other, loc);
561
562	return (loc);
563}
564
565void
566freerecord(struct player *playerp, long loc)
567{
568	playerp->p_name[0] = CH_MARKDELETE;
569	playerp->p_status = S_NOTUSED;
570	writerecord(playerp, loc);
571}
572
573void
574leavegame(void)
575{
576
577	if (Player.p_level < 1.0)
578		/* delete character */
579		freerecord(&Player, Fileloc);
580	else {
581		Player.p_status = S_OFF;
582		writerecord(&Player, Fileloc);
583	}
584
585	cleanup(TRUE);
586	/* NOTREACHED */
587}
588
589void
590death(const char *how)
591{
592	FILE   *fp;		/* for updating various files */
593	int     ch;		/* input */
594	static const char *const deathmesg[] =
595	/* add more messages here, if desired */
596	{
597		"You have been wounded beyond repair.  ",
598		"You have been disemboweled.  ",
599		"You've been mashed, mauled, and spit upon.  (You're dead.)\n",
600		"You died!  ",
601		"You're a complete failure -- you've died!!\n",
602		"You have been dealt a fatal blow!  "
603	};
604
605	clear();
606
607	if (strcmp(how, "Stupidity") != 0) {
608		if (Player.p_level > 9999.0)
609			/* old age */
610			addstr("Characters must be retired upon reaching level 10000.  Sorry.");
611		else
612			if (Player.p_lives > 0)
613				/* extra lives */
614			{
615				addstr("You should be more cautious.  You've been killed.\n");
616				printw("You only have %d more chance(s).\n", --Player.p_lives);
617				more(3);
618				Player.p_energy = Player.p_maxenergy;
619				return;
620			} else
621				if (Player.p_specialtype == SC_VALAR) {
622					addstr("You had your chances, but Valar aren't totally\n");
623					addstr("immortal.  You are now left to wither and die . . .\n");
624					more(3);
625					Player.p_brains = Player.p_level / 25.0;
626					Player.p_energy = Player.p_maxenergy /= 5.0;
627					Player.p_quksilver = Player.p_sword = 0.0;
628					Player.p_specialtype = SC_COUNCIL;
629					return;
630				} else
631					if (Player.p_ring.ring_inuse &&
632					    (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
633						/* good ring in use - saved
634						 * from death */
635					{
636						mvaddstr(4, 0, "Your ring saved you from death!\n");
637						refresh();
638						Player.p_ring.ring_type = R_NONE;
639						Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
640						if (Player.p_crowns > 0)
641							--Player.p_crowns;
642						return;
643					} else
644						if (Player.p_ring.ring_type == R_BAD
645						    || Player.p_ring.ring_type == R_SPOILED)
646							/* bad ring in
647							 * possession; name
648							 * idiot after player */
649						{
650							mvaddstr(4, 0,
651							    "Your ring has taken control of you and turned you into a monster!\n");
652							fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
653							fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
654							strcpy(Curmonster.m_name, Player.p_name);
655							fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
656							fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
657							fflush(Monstfp);
658						}
659	}
660	enterscore();		/* update score board */
661
662	/* put info in last dead file */
663	fp = fopen(_PATH_LASTDEAD, "w");
664	fprintf(fp, "%s (%s, run by %s, level %.0f, killed by %s)",
665	    Player.p_name, descrtype(&Player, TRUE),
666	    Player.p_login, Player.p_level, how);
667	fclose(fp);
668
669	/* let other players know */
670	fp = fopen(_PATH_MESS, "w");
671	fprintf(fp, "%s was killed by %s.", Player.p_name, how);
672	fclose(fp);
673
674	freerecord(&Player, Fileloc);
675
676	clear();
677	move(10, 0);
678	addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
679	addstr("Care to give it another try ? ");
680	ch = getanswer("NY", FALSE);
681
682	if (ch == 'Y') {
683		cleanup(FALSE);
684		execl(_PATH_GAMEPROG, "phantasia", "-s",
685		    (Wizard ? "-S" : (char *) NULL), (char *) NULL);
686		exit(0);
687		/* NOTREACHED */
688	}
689	cleanup(TRUE);
690	/* NOTREACHED */
691}
692
693void
694writerecord(struct player *playerp, long place)
695{
696	fseek(Playersfp, place, SEEK_SET);
697	fwrite((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
698	fflush(Playersfp);
699}
700
701static double
702explevel(double experience)
703{
704	if (experience < 1.1e7)
705		return (floor(pow((experience / 1000.0), 0.4875)));
706	else
707		return (floor(pow((experience / 1250.0), 0.4865)));
708}
709
710void
711truncstring(char *string)
712{
713	int     length;		/* length of string */
714
715	length = strlen(string);
716	while (string[--length] == ' ')
717		string[length] = '\0';
718}
719
720void
721altercoordinates(double xnew, double ynew, int operation)
722{
723	switch (operation) {
724	case A_FORCED:		/* move with no checks */
725		break;
726
727	case A_NEAR:		/* pick random coordinates near */
728		xnew = Player.p_x + ROLL(1.0, 5.0);
729		ynew = Player.p_y - ROLL(1.0, 5.0);
730		/* fall through for check */
731
732	case A_SPECIFIC:	/* just move player */
733		if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
734			/*
735			 * cannot move back from point of no return
736			 * pick the largest coordinate to remain unchanged
737			 */
738		{
739			if (fabs(xnew) > fabs(ynew))
740				xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
741			else
742				ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
743		}
744		break;
745
746	case A_FAR:		/* pick random coordinates far */
747		xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
748		ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
749		break;
750	}
751
752	/* now set location flags and adjust coordinates */
753	Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
754
755	/* set up flags based upon location */
756	Throne = Marsh = Beyond = FALSE;
757
758	if (Player.p_x == 0.0 && Player.p_y == 0.0)
759		Throne = TRUE;
760	else
761		if (Circle < 35 && Circle >= 20)
762			Marsh = TRUE;
763		else
764			if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
765				Beyond = TRUE;
766
767	Changed = TRUE;
768}
769
770void
771readrecord(struct player *playerp, long loc)
772{
773	fseek(Playersfp, loc, SEEK_SET);
774	fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
775}
776
777void
778adjuststats(void)
779{
780	double  dtemp;		/* for temporary calculations */
781
782	if (explevel(Player.p_experience) > Player.p_level)
783		/* move one or more levels */
784	{
785		movelevel();
786		if (Player.p_level > 5.0)
787			Timeout = TRUE;
788	}
789	if (Player.p_specialtype == SC_VALAR)
790		/* valar */
791		Circle = Player.p_level / 5.0;
792
793	/* calculate effective quickness */
794	dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
795	    - Player.p_level;
796	dtemp = MAX(0.0, dtemp);/* gold slows player down */
797	Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
798
799	/* calculate effective strength */
800	if (Player.p_poison > 0.0)
801		/* poison makes player weaker */
802	{
803		dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
804		dtemp = MAX(0.1, dtemp);
805	} else
806		dtemp = 1.0;
807	Player.p_might = dtemp * Player.p_strength + Player.p_sword;
808
809	/* insure that important things are within limits */
810	Player.p_quksilver = MIN(99.0, Player.p_quksilver);
811	Player.p_mana = MIN(Player.p_mana,
812	    Player.p_level * Statptr->c_maxmana + 1000.0);
813	Player.p_brains = MIN(Player.p_brains,
814	    Player.p_level * Statptr->c_maxbrains + 200.0);
815	Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
816
817	/*
818         * some implementations have problems with floating point compare
819         * we work around it with this stuff
820         */
821	Player.p_gold = floor(Player.p_gold) + 0.1;
822	Player.p_gems = floor(Player.p_gems) + 0.1;
823	Player.p_mana = floor(Player.p_mana) + 0.1;
824
825	if (Player.p_ring.ring_type != R_NONE)
826		/* do ring things */
827	{
828		/* rest to max */
829		Player.p_energy = Player.p_maxenergy + Player.p_shield;
830
831		if (Player.p_ring.ring_duration <= 0)
832			/* clean up expired rings */
833			switch (Player.p_ring.ring_type) {
834			case R_BAD:	/* ring drives player crazy */
835				Player.p_ring.ring_type = R_SPOILED;
836				Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
837				break;
838
839			case R_NAZREG:	/* ring disappears */
840				Player.p_ring.ring_type = R_NONE;
841				break;
842
843			case R_SPOILED:	/* ring kills player */
844				death("A cursed ring");
845				break;
846
847			case R_DLREG:	/* this ring doesn't expire */
848				Player.p_ring.ring_duration = 0;
849				break;
850			}
851	}
852	if (Player.p_age / N_AGE > Player.p_degenerated)
853		/* age player slightly */
854	{
855		++Player.p_degenerated;
856		if (Player.p_quickness > 23.0)
857			Player.p_quickness *= 0.99;
858		Player.p_strength *= 0.97;
859		Player.p_brains *= 0.95;
860		Player.p_magiclvl *= 0.97;
861		Player.p_maxenergy *= 0.95;
862		Player.p_quksilver *= 0.95;
863		Player.p_sword *= 0.93;
864		Player.p_shield *= 0.93;
865	}
866}
867
868void
869initplayer(struct player *playerp)
870{
871	playerp->p_experience =
872	    playerp->p_level =
873	    playerp->p_strength =
874	    playerp->p_sword =
875	    playerp->p_might =
876	    playerp->p_energy =
877	    playerp->p_maxenergy =
878	    playerp->p_shield =
879	    playerp->p_quickness =
880	    playerp->p_quksilver =
881	    playerp->p_speed =
882	    playerp->p_magiclvl =
883	    playerp->p_mana =
884	    playerp->p_brains =
885	    playerp->p_poison =
886	    playerp->p_gems =
887	    playerp->p_sin =
888	    playerp->p_1scratch =
889	    playerp->p_2scratch = 0.0;
890
891	playerp->p_gold = ROLL(50.0, 75.0) + 0.1;	/* give some gold */
892
893	playerp->p_x = ROLL(-125.0, 251.0);
894	playerp->p_y = ROLL(-125.0, 251.0);	/* give random x, y */
895
896	/* clear ring */
897	playerp->p_ring.ring_type = R_NONE;
898	playerp->p_ring.ring_duration = 0;
899	playerp->p_ring.ring_inuse = FALSE;
900
901	playerp->p_age = 0L;
902
903	playerp->p_degenerated = 1;	/* don't degenerate initially */
904
905	playerp->p_type = C_FIGHTER;	/* default */
906	playerp->p_specialtype = SC_NONE;
907	playerp->p_lives =
908	    playerp->p_crowns =
909	    playerp->p_charms =
910	    playerp->p_amulets =
911	    playerp->p_holywater =
912	    playerp->p_lastused = 0;
913	playerp->p_status = S_NOTUSED;
914	playerp->p_tampered = T_OFF;
915	playerp->p_istat = I_OFF;
916
917	playerp->p_palantir =
918	    playerp->p_blessing =
919	    playerp->p_virgin =
920	    playerp->p_blindness = FALSE;
921
922	playerp->p_name[0] =
923	    playerp->p_password[0] =
924	    playerp->p_login[0] = '\0';
925}
926
927void
928readmessage(void)
929{
930	move(3, 0);
931	clrtoeol();
932	fseek(Messagefp, 0L, SEEK_SET);
933	if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
934		addstr(Databuf);
935}
936
937void
938error(const char *whichfile)
939{
940	int     (*funcp)(const char *,...);
941
942	if (Windows) {
943		funcp = printw;
944		clear();
945	} else
946		funcp = printf;
947
948	(*funcp) ("An unrecoverable error has occurred reading %s.  (%s)\n", whichfile, strerror(errno));
949	(*funcp) ("Please run 'setup' to determine the problem.\n");
950	cleanup(TRUE);
951	/* NOTREACHED */
952}
953
954double
955distance(double x_1, double x_2, double y_1, double y_2)
956{
957	double  deltax, deltay;
958
959	deltax = x_1 - x_2;
960	deltay = y_1 - y_2;
961	return (sqrt(deltax * deltax + deltay * deltay));
962}
963
964void
965ill_sig(int whichsig)
966{
967	clear();
968	if (!(whichsig == SIGINT || whichsig == SIGQUIT))
969		printw("Error: caught signal # %d.\n", whichsig);
970	cleanup(TRUE);
971	/* NOTREACHED */
972}
973
974const char *
975descrstatus(struct player *playerp)
976{
977	switch (playerp->p_status) {
978	case S_PLAYING:
979		if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
980			return ("Low Energy");
981		else
982			if (playerp->p_blindness)
983				return ("Blind");
984			else
985				return ("In game");
986
987	case S_CLOAKED:
988		return ("Cloaked");
989
990	case S_INBATTLE:
991		return ("In Battle");
992
993	case S_MONSTER:
994		return ("Encounter");
995
996	case S_TRADING:
997		return ("Trading");
998
999	case S_OFF:
1000		return ("Off");
1001
1002	case S_HUNGUP:
1003		return ("Hung up");
1004
1005	default:
1006		return ("");
1007	}
1008}
1009
1010double
1011drandom(void)
1012{
1013	if (sizeof(int) != 2)
1014		/* use only low bits */
1015		return ((double) (random() & 0x7fff) / 32768.0);
1016	else
1017		return ((double) random() / 32768.0);
1018}
1019
1020void
1021collecttaxes(double gold, double gems)
1022{
1023	FILE   *fp;		/* to update Goldfile */
1024	double  dtemp;		/* for temporary calculations */
1025	double  taxes;		/* tax liability */
1026
1027	/* add to cache */
1028	Player.p_gold += gold;
1029	Player.p_gems += gems;
1030
1031	/* calculate tax liability */
1032	taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
1033
1034	if (Player.p_gold < taxes)
1035		/* not enough gold to pay taxes, must convert some gems to
1036		 * gold */
1037	{
1038		dtemp = floor(taxes / N_GEMVALUE + 1.0);	/* number of gems to
1039								 * convert */
1040
1041		if (Player.p_gems >= dtemp)
1042			/* player has enough to convert */
1043		{
1044			Player.p_gems -= dtemp;
1045			Player.p_gold += dtemp * N_GEMVALUE;
1046		} else
1047			/* take everything; this should never happen */
1048		{
1049			Player.p_gold += Player.p_gems * N_GEMVALUE;
1050			Player.p_gems = 0.0;
1051			taxes = Player.p_gold;
1052		}
1053	}
1054	Player.p_gold -= taxes;
1055
1056	if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
1057		/* update taxes */
1058	{
1059		dtemp = 0.0;
1060		fread((char *) &dtemp, sizeof(double), 1, fp);
1061		dtemp += floor(taxes);
1062		fseek(fp, 0L, SEEK_SET);
1063		fwrite((char *) &dtemp, sizeof(double), 1, fp);
1064		fclose(fp);
1065	}
1066}
1067