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