xref: /netbsd-src/games/phantasia/misc.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*	$NetBSD: misc.c,v 1.2 1995/03/24 03:59:03 cgd Exp $	*/
2 
3 /*
4  * misc.c  Phantasia miscellaneous support routines
5  */
6 
7 #include "include.h"
8 
9 
10 /************************************************************************
11 /
12 / FUNCTION NAME: movelevel()
13 /
14 / FUNCTION: move player to new level
15 /
16 / AUTHOR: E. A. Estes, 12/4/85
17 /
18 / ARGUMENTS: none
19 /
20 / RETURN VALUE: none
21 /
22 / MODULES CALLED: death(), floor(), wmove(), drandom(), waddstr(), explevel()
23 /
24 / GLOBAL INPUTS: Player, *stdscr, *Statptr, Stattable[]
25 /
26 / GLOBAL OUTPUTS: Player, Changed
27 /
28 / DESCRIPTION:
29 /	Use lookup table to increment important statistics when
30 /	progressing to new experience level.
31 /	Players are rested to maximum as a bonus for making a new
32 /	level.
33 /	Check for council of wise, and being too big to be king.
34 /
35 /************************************************************************/
36 
37 movelevel()
38 {
39 register struct charstats	*statptr;	/* for pointing into Stattable */
40 double	new;			/* new level */
41 double	inc;			/* increment between new and old levels */
42 
43     Changed = TRUE;
44 
45     if (Player.p_type == C_EXPER)
46 	/* roll a type to use for increment */
47 	statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
48     else
49 	statptr = Statptr;
50 
51     new = explevel(Player.p_experience);
52     inc = new - Player.p_level;
53     Player.p_level = new;
54 
55     /* add increments to statistics */
56     Player.p_strength += statptr->c_strength.increase * inc;
57     Player.p_mana += statptr->c_mana.increase * inc;
58     Player.p_brains += statptr->c_brains.increase * inc;
59     Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
60     Player.p_maxenergy += statptr->c_energy.increase * inc;
61 
62     /* rest to maximum upon reaching new level */
63     Player.p_energy = Player.p_maxenergy + Player.p_shield;
64 
65     if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
66 	/* no longer able to be king -- turn crowns into cash */
67 	{
68 	Player.p_gold += ((double) Player.p_crowns) * 5000.0;
69 	Player.p_crowns = 0;
70 	}
71 
72     if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
73 	/* make a member of the council */
74 	{
75 	mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
76 	addstr("Good Luck on your search for the Holy Grail.\n");
77 
78 	Player.p_specialtype = SC_COUNCIL;
79 
80 	/* no rings for council and above */
81 	Player.p_ring.ring_type = R_NONE;
82 	Player.p_ring.ring_duration = 0;
83 
84 	Player.p_lives = 3;		/* three extra lives */
85 	}
86 
87     if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
88 	death("Old age");
89 }
90 /**/
91 /************************************************************************
92 /
93 / FUNCTION NAME: descrlocation()
94 /
95 / FUNCTION: return a formatted description of location
96 /
97 / AUTHOR: E. A. Estes, 12/4/85
98 /
99 / ARGUMENTS:
100 /	struct player playerp - pointer to player structure
101 /	bool shortflag - set if short form is desired
102 /
103 / RETURN VALUE: pointer to string containing result
104 /
105 / MODULES CALLED: fabs(), floor(), sprintf(), distance()
106 /
107 / GLOBAL INPUTS: Databuf[]
108 /
109 / GLOBAL OUTPUTS: none
110 /
111 / DESCRIPTION:
112 /	Look at coordinates and return an appropriately formatted
113 /	string.
114 /
115 /************************************************************************/
116 
117 char	*
118 descrlocation(playerp, shortflag)
119 struct player	*playerp;
120 bool	shortflag;
121 {
122 double	circle;			/* corresponding circle for coordinates */
123 register int	quadrant;	/* quandrant of grid */
124 register char	*label;		/* pointer to place name */
125 static char	*nametable[4][4] =   /* names of places */
126 	{
127 	"Anorien",	"Ithilien",	"Rohan",	"Lorien",
128 	"Gondor",	"Mordor",	"Dunland",	"Rovanion",
129 	"South Gondor", "Khand",	"Eriador",	"The Iron Hills",
130 	"Far Harad",	"Near Harad",	"The Northern Waste", "Rhun"
131 	};
132 
133     if (playerp->p_specialtype == SC_VALAR)
134 	return(" is in Valhala");
135     else if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0)
136 	{
137 	if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
138 	    label = "The Point of No Return";
139 	else
140 	    label = "The Ashen Mountains";
141 	}
142     else if (circle >= 55)
143 	label = "Morannon";
144     else if (circle >= 35)
145 	label = "Kennaquahair";
146     else if (circle >= 20)
147 	label = "The Dead Marshes";
148     else if (circle >= 9)
149 	label = "The Outer Waste";
150     else if (circle >= 5)
151 	label = "The Moors Adventurous";
152     else
153 	{
154 	if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
155 	    label = "The Lord's Chamber";
156 	else
157 	    {
158 	    /* this expression is split to prevent compiler loop with some compilers */
159 	    quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
160 	    quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
161 	    label = nametable[((int) circle) - 1][quadrant];
162 	    }
163 	}
164 
165     if (shortflag)
166 	sprintf(Databuf, "%.29s", label);
167     else
168 	sprintf(Databuf, " is in %s  (%.0f,%.0f)", label, playerp->p_x, playerp->p_y);
169 
170     return(Databuf);
171 }
172 /**/
173 /************************************************************************
174 /
175 / FUNCTION NAME: tradingpost()
176 /
177 / FUNCTION: do trading post stuff
178 /
179 / AUTHOR: E. A. Estes, 12/4/85
180 /
181 / ARGUMENTS: none
182 /
183 / RETURN VALUE: none
184 /
185 / MODULES CALLED: writerecord(), adjuststats(), fabs(), more(), sqrt(),
186 /	sleep(), floor(), wmove(), drandom(), wclear(), printw(),
187 /	altercoordinates(), infloat(), waddstr(), wrefresh(), mvprintw(), getanswer(),
188 /	wclrtoeol(), wclrtobot()
189 /
190 / GLOBAL INPUTS: Menu[], Circle, Player, *stdscr, Fileloc, Nobetter[]
191 /
192 / GLOBAL OUTPUTS: Player
193 /
194 / DESCRIPTION:
195 /	Different trading posts have different items.
196 /	Merchants cannot be cheated, but they can be dishonest
197 /	themselves.
198 /
199 /	Shields, swords, and quicksilver are not cumulative.  This is
200 /	one major area of complaint, but there are two reasons for this:
201 /		1) It becomes MUCH too easy to make very large versions
202 /		   of these items.
203 /		2) In the real world, one cannot simply weld two swords
204 /		   together to make a bigger one.
205 /
206 /	At one time, it was possible to sell old weapons at half the purchase
207 /	price.  This resulted in huge amounts of gold floating around,
208 /	and the game lost much of its challenge.
209 /
210 /	Also, purchasing gems defeats the whole purpose of gold.  Gold
211 /	is small change for lower level players.  They really shouldn't
212 /	be able to accumulate more than enough gold for a small sword or
213 /	a few books.  Higher level players shouldn't even bother to pick
214 /	up gold, except maybe to buy mana once in a while.
215 /
216 /************************************************************************/
217 
218 tradingpost()
219 {
220 double	numitems;	/* number of items to purchase */
221 double	cost;		/* cost of purchase */
222 double	blessingcost;	/* cost of blessing */
223 int	ch;		/* input */
224 register int	size;	/* size of the trading post */
225 register int	loop;	/* loop counter */
226 int	cheat = 0;	/* number of times player has tried to cheat */
227 bool	dishonest = FALSE;/* set when merchant is dishonest */
228 
229     Player.p_status = S_TRADING;
230     writerecord(&Player, Fileloc);
231 
232     clear();
233     addstr("You are at a trading post. All purchases must be made with gold.");
234 
235     size = sqrt(fabs(Player.p_x / 100)) + 1;
236     size = MIN(7, size);
237 
238     /* set up cost of blessing */
239     blessingcost = 1000.0 * (Player.p_level + 5.0);
240 
241     /* print Menu */
242     move(7, 0);
243     for (loop = 0; loop < size; ++loop)
244 	/* print Menu */
245 	{
246 	if (loop == 6)
247 	    cost = blessingcost;
248 	else
249 	    cost = Menu[loop].cost;
250 	printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
251 	}
252 
253     mvprintw(5, 0, "L:Leave  P:Purchase  S:Sell Gems ? ");
254 
255     for (;;)
256 	{
257 	adjuststats();	/* truncate any bad values */
258 
259 	/* print some important statistics */
260 	mvprintw(1, 0, "Gold:   %9.0f  Gems:  %9.0f  Level:   %6.0f  Charms: %6d\n",
261 	    Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
262 	printw("Shield: %9.0f  Sword: %9.0f  Quicksilver:%3.0f  Blessed: %s\n",
263 	    Player.p_shield, Player.p_sword, Player.p_quksilver,
264 	    (Player.p_blessing ? " True" : "False"));
265 	printw("Brains: %9.0f  Mana:  %9.0f", Player.p_brains, Player.p_mana);
266 
267 	move(5, 36);
268 	ch = getanswer("LPS", FALSE);
269 	move(15, 0);
270 	clrtobot();
271 	switch(ch)
272 	    {
273 	    case 'L':		/* leave */
274 	    case '\n':
275 		altercoordinates(0.0, 0.0, A_NEAR);
276 		return;
277 
278 	    case 'P':		/* make purchase */
279 		mvaddstr(15, 0, "What what would you like to buy ? ");
280 		ch = getanswer(" 1234567", FALSE);
281 		move(15, 0);
282 		clrtoeol();
283 
284 		if (ch - '0' > size)
285 		    addstr("Sorry, this merchant doesn't have that.");
286 		else
287 		    switch (ch)
288 			{
289 			case '1':
290 			    printw("Mana is one per %.0f gold piece.  How many do you want (%.0f max) ? ",
291 				Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
292 			    cost = (numitems = floor(infloat())) * Menu[0].cost;
293 
294 			    if (cost > Player.p_gold || numitems < 0)
295 				++cheat;
296 			    else
297 				{
298 				cheat = 0;
299 				Player.p_gold -= cost;
300 				if (drandom() < 0.02)
301 				    dishonest = TRUE;
302 				else
303 				    Player.p_mana += numitems;
304 				}
305 			    break;
306 
307 			case '2':
308 			    printw("Shields are %.0f per +1.  How many do you want (%.0f max) ? ",
309 				Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
310 			    cost = (numitems = floor(infloat())) * Menu[1].cost;
311 
312 			    if (numitems == 0.0)
313 				break;
314 			    else if (cost > Player.p_gold || numitems < 0)
315 				++cheat;
316 			    else if (numitems < Player.p_shield)
317 				NOBETTER();
318 			    else
319 				{
320 				cheat = 0;
321 				Player.p_gold -= cost;
322 				if (drandom() < 0.02)
323 				    dishonest = TRUE;
324 				else
325 				    Player.p_shield = numitems;
326 				}
327 			    break;
328 
329 			case '3':
330 			    printw("A book costs %.0f gp.  How many do you want (%.0f max) ? ",
331 				Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
332 			    cost = (numitems = floor(infloat())) * Menu[2].cost;
333 
334 			    if (cost > Player.p_gold || numitems < 0)
335 				++cheat;
336 			    else
337 				{
338 				cheat = 0;
339 				Player.p_gold -= cost;
340 				if (drandom() < 0.02)
341 				    dishonest = TRUE;
342 				else if (drandom() * numitems > Player.p_level / 10.0
343 				    && numitems != 1)
344 				    {
345 				    printw("\nYou blew your mind!\n");
346 				    Player.p_brains /= 5;
347 				    }
348 				else
349 				    {
350 				    Player.p_brains += floor(numitems) * ROLL(20, 8);
351 				    }
352 				}
353 			    break;
354 
355 			case '4':
356 			    printw("Swords are %.0f gp per +1.  How many + do you want (%.0f max) ? ",
357 				Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
358 			    cost = (numitems = floor(infloat())) * Menu[3].cost;
359 
360 			    if (numitems == 0.0)
361 				break;
362 			    else if (cost > Player.p_gold || numitems < 0)
363 				++cheat;
364 			    else if (numitems < Player.p_sword)
365 				NOBETTER();
366 			    else
367 				{
368 				cheat = 0;
369 				Player.p_gold -= cost;
370 				if (drandom() < 0.02)
371 				    dishonest = TRUE;
372 				else
373 				    Player.p_sword = numitems;
374 				}
375 			    break;
376 
377 			case '5':
378 			    printw("A charm costs %.0f gp.  How many do you want (%.0f max) ? ",
379 				Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
380 			    cost = (numitems = floor(infloat())) * Menu[4].cost;
381 
382 			    if (cost > Player.p_gold || numitems < 0)
383 				++cheat;
384 			    else
385 				{
386 				cheat = 0;
387 				Player.p_gold -= cost;
388 				if (drandom() < 0.02)
389 				    dishonest = TRUE;
390 				else
391 				    Player.p_charms += numitems;
392 				}
393 			    break;
394 
395 			case '6':
396 			    printw("Quicksilver is %.0f gp per +1.  How many + do you want (%.0f max) ? ",
397 				Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
398 			    cost = (numitems = floor(infloat())) * Menu[5].cost;
399 
400 			    if (numitems == 0.0)
401 				break;
402 			    else if (cost > Player.p_gold || numitems < 0)
403 				++cheat;
404 			    else if (numitems < Player.p_quksilver)
405 				NOBETTER();
406 			    else
407 				{
408 				cheat = 0;
409 				Player.p_gold -= cost;
410 				if (drandom() < 0.02)
411 				    dishonest = TRUE;
412 				else
413 				    Player.p_quksilver = numitems;
414 				}
415 			    break;
416 
417 			case '7':
418 			    if (Player.p_blessing)
419 				{
420 				addstr("You already have a blessing.");
421 				break;
422 				}
423 
424 			    printw("A blessing requires a %.0f gp donation.  Still want one ? ", blessingcost);
425 			    ch = getanswer("NY", FALSE);
426 
427 			    if (ch == 'Y')
428 				if (Player.p_gold < blessingcost)
429 				    ++cheat;
430 				else
431 				    {
432 				    cheat = 0;
433 				    Player.p_gold -= blessingcost;
434 				    if (drandom() < 0.02)
435 					dishonest = TRUE;
436 				    else
437 					Player.p_blessing = TRUE;
438 				    }
439 			    break;
440 			}
441 	    break;
442 
443 	    case 'S':		/* sell gems */
444 		mvprintw(15, 0, "A gem is worth %.0f gp.  How many do you want to sell (%.0f max) ? ",
445 		    (double) N_GEMVALUE, Player.p_gems);
446 		numitems = floor(infloat());
447 
448 		if (numitems > Player.p_gems || numitems < 0)
449 		    ++cheat;
450 		else
451 		    {
452 		    cheat = 0;
453 		    Player.p_gems -= numitems;
454 		    Player.p_gold += numitems * N_GEMVALUE;
455 		    }
456 	    }
457 
458 	if (cheat == 1)
459 	    mvaddstr(17, 0, "Come on, merchants aren't stupid.  Stop cheating.\n");
460 	else if (cheat == 2)
461 	    {
462 	    mvaddstr(17, 0, "You had your chance.  This merchant happens to be\n");
463 	    printw("a %.0f level magic user, and you made %s mad!\n",
464 		ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
465 	    altercoordinates(0.0, 0.0, A_FAR);
466 	    Player.p_energy /= 2.0;
467 	    ++Player.p_sin;
468 	    more(23);
469 	    return;
470 	    }
471 	else if (dishonest)
472 	    {
473 	    mvaddstr(17, 0, "The merchant stole your money!");
474 	    refresh();
475 	    altercoordinates(Player.p_x - Player.p_x / 10.0,
476 		Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
477 	    sleep(2);
478 	    return;
479 	    }
480 	}
481 }
482 /**/
483 /************************************************************************
484 /
485 / FUNCTION NAME: displaystats()
486 /
487 / FUNCTION: print out important player statistics
488 /
489 / AUTHOR: E. A. Estes, 12/4/85
490 /
491 / ARGUMENTS: none
492 /
493 / RETURN VALUE: none
494 /
495 / MODULES CALLED: descrstatus(), descrlocation(), mvprintw()
496 /
497 / GLOBAL INPUTS: Users, Player
498 /
499 / GLOBAL OUTPUTS: none
500 /
501 / DESCRIPTION:
502 /	Important player statistics are printed on the screen.
503 /
504 /************************************************************************/
505 
506 displaystats()
507 {
508     mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
509     mvprintw(1, 0, "Level :%7.0f   Energy  :%9.0f(%9.0f)  Mana :%9.0f  Users:%3d\n",
510 	Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
511 	Player.p_mana, Users);
512     mvprintw(2, 0, "Quick :%3.0f(%3.0f)  Strength:%9.0f(%9.0f)  Gold :%9.0f  %s\n",
513 	Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
514 	Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
515 }
516 /**/
517 /************************************************************************
518 /
519 / FUNCTION NAME: allstatslist()
520 /
521 / FUNCTION: show player items
522 /
523 / AUTHOR: E. A. Estes, 12/4/85
524 /
525 / ARGUMENTS: none
526 /
527 / RETURN VALUE: none
528 /
529 / MODULES CALLED: mvprintw(), descrtype()
530 /
531 / GLOBAL INPUTS: Player
532 /
533 / GLOBAL OUTPUTS: none
534 /
535 / DESCRIPTION:
536 /	Print out some player statistics of lesser importance.
537 /
538 /************************************************************************/
539 
540 allstatslist()
541 {
542 static	char	*flags[] =	/* to print value of some bools */
543 	    {
544 	    "False",
545 	    " True"
546 	    };
547 
548     mvprintw( 8,  0, "Type: %s\n",  descrtype(&Player,  FALSE));
549 
550     mvprintw(10,  0, "Experience: %9.0f", Player.p_experience);
551     mvprintw(11,  0, "Brains    : %9.0f", Player.p_brains);
552     mvprintw(12,  0, "Magic Lvl : %9.0f", Player.p_magiclvl);
553     mvprintw(13,  0, "Sin       : %9.5f", Player.p_sin);
554     mvprintw(14,  0, "Poison    : %9.5f", Player.p_poison);
555     mvprintw(15,  0, "Gems      : %9.0f", Player.p_gems);
556     mvprintw(16,  0, "Age       : %9d", Player.p_age);
557     mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
558     mvprintw(11, 40, "Amulets   : %9d", Player.p_amulets);
559     mvprintw(12, 40, "Charms    : %9d", Player.p_charms);
560     mvprintw(13, 40, "Crowns    : %9d", Player.p_crowns);
561     mvprintw(14, 40, "Shield    : %9.0f", Player.p_shield);
562     mvprintw(15, 40, "Sword     : %9.0f", Player.p_sword);
563     mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
564 
565     mvprintw(18,  0, "Blessing: %s   Ring: %s   Virgin: %s   Palantir: %s",
566 	flags[Player.p_blessing], flags[Player.p_ring.ring_type != R_NONE],
567 	flags[Player.p_virgin], flags[Player.p_palantir]);
568 }
569 /**/
570 /************************************************************************
571 /
572 / FUNCTION NAME: descrtype()
573 /
574 / FUNCTION: return a string specifying player type
575 /
576 / AUTHOR: E. A. Estes, 12/4/85
577 /
578 / ARGUMENTS:
579 /	struct player playerp - pointer to structure for player
580 /	bool shortflag - set if short form is desired
581 /
582 / RETURN VALUE: pointer to string describing player type
583 /
584 / MODULES CALLED: strcpy()
585 /
586 / GLOBAL INPUTS: Databuf[]
587 /
588 / GLOBAL OUTPUTS: Databuf[]
589 /
590 / DESCRIPTION:
591 /	Return a string describing the player type.
592 /	King, council, valar, supercedes other types.
593 /	The first character of the string is '*' if the player
594 /	has a crown.
595 /	If 'shortflag' is TRUE, return a 3 character string.
596 /
597 /************************************************************************/
598 
599 char	*
600 descrtype(playerp, shortflag)
601 struct player *playerp;
602 bool	shortflag;
603 {
604 register int type;	/* for caluculating result subscript */
605 static char	*results[] =	/* description table */
606 			{
607 			" Magic User", " MU",
608 			" Fighter", " F ",
609 			" Elf", " E ",
610 			" Dwarf", " D ",
611 			" Halfling", " H ",
612 			" Experimento", " EX",
613 			" Super", " S ",
614 			" King", " K ",
615 			" Council of Wise", " CW",
616 			" Ex-Valar", " EV",
617 			" Valar", " V ",
618 			" ? ", " ? "
619 			};
620 
621     type = playerp->p_type;
622 
623     switch (playerp->p_specialtype)
624 	{
625 	case SC_NONE:
626 	    type = playerp->p_type;
627 	    break;
628 
629 	case SC_KING:
630 	    type = 7;
631 	    break;
632 
633 	case SC_COUNCIL:
634 	    type = 8;
635 	    break;
636 
637 	case SC_EXVALAR:
638 	    type = 9;
639 	    break;
640 
641 	case SC_VALAR:
642 	    type = 10;
643 	    break;
644 	}
645 
646     type *= 2;		/* calculate offset */
647 
648     if (type > 20)
649 	/* error */
650 	type = 22;
651 
652     if (shortflag)
653 	/* use short descriptions */
654 	++type;
655 
656     if (playerp->p_crowns > 0)
657 	{
658 	strcpy(Databuf, results[type]);
659 	Databuf[0] = '*';
660 	return(Databuf);
661 	}
662     else
663 	return(results[type]);
664 }
665 /**/
666 /************************************************************************
667 /
668 / FUNCTION NAME: findname()
669 /
670 / FUNCTION: find location in player file of given name
671 /
672 / AUTHOR: E. A. Estes, 12/4/85
673 /
674 / ARGUMENTS:
675 /	char *name - name of character to look for
676 /	struct player *playerp - pointer of structure to fill
677 /
678 / RETURN VALUE: location of player if found, -1 otherwise
679 /
680 / MODULES CALLED: fread(), fseek(), strcmp()
681 /
682 / GLOBAL INPUTS: Wizard, *Playersfp
683 /
684 / GLOBAL OUTPUTS: none
685 /
686 / DESCRIPTION:
687 /	Search the player file for the player of the given name.
688 /	If player is found, fill structure with player data.
689 /
690 /************************************************************************/
691 
692 long
693 findname(name, playerp)
694 register char	*name;
695 register struct player *playerp;
696 {
697 long	loc = 0;			/* location in the file */
698 
699     fseek(Playersfp, 0L, 0);
700     while (fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
701 	{
702 	if (strcmp(playerp->p_name, name) == 0)
703 	    {
704 	    if (playerp->p_status != S_NOTUSED || Wizard)
705 		/* found it */
706 		return(loc);
707 	    }
708 	loc += SZ_PLAYERSTRUCT;
709 	}
710 
711     return(-1);
712 }
713 /**/
714 /************************************************************************
715 /
716 / FUNCTION NAME: allocrecord()
717 /
718 / FUNCTION: find space in the player file for a new character
719 /
720 / AUTHOR: E. A. Estes, 12/4/85
721 /
722 / ARGUMENTS: none
723 /
724 / RETURN VALUE: location of free space in file
725 /
726 / MODULES CALLED: initplayer(), writerecord(), fread(), fseek()
727 /
728 / GLOBAL INPUTS: Other, *Playersfp
729 /
730 / GLOBAL OUTPUTS: Player
731 /
732 / DESCRIPTION:
733 /	Search the player file for an unused entry.  If none are found,
734 /	make one at the end of the file.
735 /
736 /************************************************************************/
737 
738 long
739 allocrecord()
740 {
741 long	loc = 0L;		/* location in file */
742 
743     fseek(Playersfp, 0L, 0);
744     while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
745 	{
746 	if (Other.p_status == S_NOTUSED)
747 	    /* found an empty record */
748 	    return(loc);
749 	else
750 	    loc += SZ_PLAYERSTRUCT;
751 	}
752 
753     /* make a new record */
754     initplayer(&Other);
755     Player.p_status = S_OFF;
756     writerecord(&Other, loc);
757 
758     return(loc);
759 }
760 /**/
761 /************************************************************************
762 /
763 / FUNCTION NAME: freerecord()
764 /
765 / FUNCTION: free up a record on the player file
766 /
767 / AUTHOR: E. A. Estes, 2/7/86
768 /
769 / ARGUMENTS:
770 /	struct player playerp - pointer to structure to free
771 /	long loc - location in file to free
772 /
773 / RETURN VALUE: none
774 /
775 / MODULES CALLED: writerecord()
776 /
777 / GLOBAL INPUTS: none
778 /
779 / GLOBAL OUTPUTS: none
780 /
781 / DESCRIPTION:
782 /	Mark structure as not used, and update player file.
783 /
784 /************************************************************************/
785 
786 freerecord(playerp, loc)
787 struct player	*playerp;
788 long	loc;
789 {
790     playerp->p_name[0] = CH_MARKDELETE;
791     playerp->p_status = S_NOTUSED;
792     writerecord(playerp, loc);
793 }
794 /**/
795 /************************************************************************
796 /
797 / FUNCTION NAME: leavegame()
798 /
799 / FUNCTION: leave game
800 /
801 / AUTHOR: E. A. Estes, 12/4/85
802 /
803 / ARGUMENTS: none
804 /
805 / RETURN VALUE: none
806 /
807 / MODULES CALLED: freerecord(), writerecord(), cleanup()
808 /
809 / GLOBAL INPUTS: Player, Fileloc
810 /
811 / GLOBAL OUTPUTS: Player
812 /
813 / DESCRIPTION:
814 /	Mark player as inactive, and cleanup.
815 /	Do not save players below level 1.
816 /
817 /************************************************************************/
818 
819 leavegame()
820 {
821 
822     if (Player.p_level < 1.0)
823 	/* delete character */
824 	freerecord(&Player, Fileloc);
825     else
826 	{
827 	Player.p_status = S_OFF;
828 	writerecord(&Player, Fileloc);
829 	}
830 
831     cleanup(TRUE);
832     /*NOTREACHED*/
833 }
834 /**/
835 /************************************************************************
836 /
837 / FUNCTION NAME: death()
838 /
839 / FUNCTION: death routine
840 /
841 / AUTHOR: E. A. Estes, 12/4/85
842 /
843 / ARGUMENTS:
844 /	char *how - pointer to string describing cause of death
845 /
846 / RETURN VALUE: none
847 /
848 / MODULES CALLED: freerecord(), enterscore(), more(), exit(), fread(),
849 /	fseek(), execl(), fopen(), floor(), wmove(), drandom(), wclear(), strcmp(),
850 /	fwrite(), fflush(), printw(), strcpy(), fclose(), waddstr(), cleanup(),
851 /	fprintf(), wrefresh(), getanswer(), descrtype()
852 /
853 / GLOBAL INPUTS: Curmonster, Wizard, Player, *stdscr, Fileloc, *Monstfp
854 /
855 / GLOBAL OUTPUTS: Player
856 /
857 / DESCRIPTION:
858 /	Kill off current player.
859 /	Handle rings, and multiple lives.
860 /	Print an appropriate message.
861 /	Update scoreboard, lastdead, and let other players know about
862 /	the demise of their comrade.
863 /
864 /************************************************************************/
865 
866 death(how)
867 char	*how;
868 {
869 FILE	*fp;		/* for updating various files */
870 int	ch;		/* input */
871 static	char	*deathmesg[] =
872 	/* add more messages here, if desired */
873 	{
874 	"You have been wounded beyond repair.  ",
875 	"You have been disemboweled.  ",
876 	"You've been mashed, mauled, and spit upon.  (You're dead.)\n",
877 	"You died!  ",
878 	"You're a complete failure -- you've died!!\n",
879 	"You have been dealt a fatal blow!  "
880 	};
881 
882     clear();
883 
884     if (strcmp(how, "Stupidity") != 0)
885 	{
886 	if (Player.p_level > 9999.0)
887 	    /* old age */
888 	    addstr("Characters must be retired upon reaching level 10000.  Sorry.");
889 	else if (Player.p_lives > 0)
890 	    /* extra lives */
891 	    {
892 	    addstr("You should be more cautious.  You've been killed.\n");
893 	    printw("You only have %d more chance(s).\n", --Player.p_lives);
894 	    more(3);
895 	    Player.p_energy = Player.p_maxenergy;
896 	    return;
897 	    }
898 	else if (Player.p_specialtype == SC_VALAR)
899 	    {
900 	    addstr("You had your chances, but Valar aren't totally\n");
901 	    addstr("immortal.  You are now left to wither and die . . .\n");
902 	    more(3);
903 	    Player.p_brains = Player.p_level / 25.0;
904 	    Player.p_energy = Player.p_maxenergy /= 5.0;
905 	    Player.p_quksilver = Player.p_sword = 0.0;
906 	    Player.p_specialtype = SC_COUNCIL;
907 	    return;
908 	    }
909 	else if (Player.p_ring.ring_inuse &&
910 	    (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
911 	    /* good ring in use - saved from death */
912 	    {
913 	    mvaddstr(4, 0, "Your ring saved you from death!\n");
914 	    refresh();
915 	    Player.p_ring.ring_type = R_NONE;
916 	    Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
917 	    if (Player.p_crowns > 0)
918 		--Player.p_crowns;
919 	    return;
920 	    }
921 	else if (Player.p_ring.ring_type == R_BAD
922 	    || Player.p_ring.ring_type == R_SPOILED)
923 	    /* bad ring in possession; name idiot after player */
924 	    {
925 	    mvaddstr(4, 0,
926 		"Your ring has taken control of you and turned you into a monster!\n");
927 	    fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, 0);
928 	    fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
929 	    strcpy(Curmonster.m_name, Player.p_name);
930 	    fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, 0);
931 	    fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
932 	    fflush(Monstfp);
933 	    }
934 	}
935 
936     enterscore();		/* update score board */
937 
938     /* put info in last dead file */
939     fp = fopen(_PATH_LASTDEAD, "w");
940     fprintf(fp,"%s (%s, run by %s, level %.0f, killed by %s)",
941 	Player.p_name, descrtype(&Player, TRUE),
942 	Player.p_login, Player.p_level, how);
943     fclose(fp);
944 
945     /* let other players know */
946     fp = fopen(_PATH_MESS, "w");
947     fprintf(fp, "%s was killed by %s.", Player.p_name, how);
948     fclose(fp);
949 
950     freerecord(&Player, Fileloc);
951 
952     clear();
953     move(10, 0);
954     addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
955     addstr("Care to give it another try ? ");
956     ch = getanswer("NY", FALSE);
957 
958     if (ch == 'Y')
959 	{
960 	cleanup(FALSE);
961 	execl(_PATH_GAMEPROG, "phantasia", "-s",
962 	    (Wizard ? "-S": (char *) NULL), 0);
963 	exit(0);
964 	/*NOTREACHED*/
965 	}
966 
967     cleanup(TRUE);
968     /*NOTREACHED*/
969 }
970 /**/
971 /************************************************************************
972 /
973 / FUNCTION NAME: writerecord()
974 /
975 / FUNCTION: update structure in player file
976 /
977 / AUTHOR: E. A. Estes, 12/4/85
978 /
979 / ARGUMENTS:
980 /	struct player *playerp - pointer to structure to write out
981 /	long place - location in file to updata
982 /
983 / RETURN VALUE: none
984 /
985 / MODULES CALLED: fseek(), fwrite(), fflush()
986 /
987 / GLOBAL INPUTS: *Playersfp
988 /
989 / GLOBAL OUTPUTS: none
990 /
991 / DESCRIPTION:
992 /	Update location in player file with given structure.
993 /
994 /************************************************************************/
995 
996 writerecord(playerp, place)
997 register struct player	*playerp;
998 long	place;
999 {
1000     fseek(Playersfp, place, 0);
1001     fwrite((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
1002     fflush(Playersfp);
1003 }
1004 /**/
1005 /************************************************************************
1006 /
1007 / FUNCTION NAME: explevel()
1008 /
1009 / FUNCTION: calculate level based upon experience
1010 /
1011 / AUTHOR: E. A. Estes, 12/4/85
1012 /
1013 / ARGUMENTS:
1014 /	double experience - experience to calculate experience level from
1015 /
1016 / RETURN VALUE: experience level
1017 /
1018 / MODULES CALLED: pow(), floor()
1019 /
1020 / GLOBAL INPUTS: none
1021 /
1022 / GLOBAL OUTPUTS: none
1023 /
1024 / DESCRIPTION:
1025 /	Experience level is a geometric progression.  This has been finely
1026 /	tuned over the years, and probably should not be changed.
1027 /
1028 /************************************************************************/
1029 
1030 double
1031 explevel(experience)
1032 double	experience;
1033 {
1034     if (experience < 1.1e7)
1035 	return(floor(pow((experience / 1000.0), 0.4875)));
1036     else
1037 	return(floor(pow((experience / 1250.0), 0.4865)));
1038 }
1039 /**/
1040 /************************************************************************
1041 /
1042 / FUNCTION NAME: truncstring()
1043 /
1044 / FUNCTION: truncate trailing blanks off a string
1045 /
1046 / AUTHOR: E. A. Estes, 12/4/85
1047 /
1048 / ARGUMENTS:
1049 /	char *string - pointer to null terminated string
1050 /
1051 / RETURN VALUE: none
1052 /
1053 / MODULES CALLED: strlen()
1054 /
1055 / GLOBAL INPUTS: none
1056 /
1057 / GLOBAL OUTPUTS: none
1058 /
1059 / DESCRIPTION:
1060 /	Put nul characters in place of spaces at the end of the string.
1061 /
1062 /************************************************************************/
1063 
1064 truncstring(string)
1065 register char	*string;
1066 {
1067 register int	length;		/* length of string */
1068 
1069     length = strlen(string);
1070     while (string[--length] == ' ')
1071 	string[length] = '\0';
1072 }
1073 /**/
1074 /************************************************************************
1075 /
1076 / FUNCTION NAME: altercoordinates()
1077 /
1078 / FUNCTION: Alter x, y coordinates and set/check location flags
1079 /
1080 / AUTHOR: E. A. Estes, 12/16/85
1081 /
1082 / ARGUMENTS:
1083 /	double xnew, ynew - new x, y coordinates
1084 /	int operation - operation to perform with coordinates
1085 /
1086 / RETURN VALUE: none
1087 /
1088 / MODULES CALLED: fabs(), floor(), drandom(), distance()
1089 /
1090 / GLOBAL INPUTS: Circle, Beyond, Player
1091 /
1092 / GLOBAL OUTPUTS: Marsh, Circle, Beyond, Throne, Player, Changed
1093 /
1094 / DESCRIPTION:
1095 /	This module is called whenever the player's coordinates are altered.
1096 /	If the player is beyond the point of no return, he/she is forced
1097 /	to stay there.
1098 /
1099 /************************************************************************/
1100 
1101 altercoordinates(xnew, ynew, operation)
1102 double	xnew;
1103 double	ynew;
1104 int	operation;
1105 {
1106     switch (operation)
1107 	{
1108 	case A_FORCED:	/* move with no checks */
1109 	    break;
1110 
1111 	case A_NEAR:	/* pick random coordinates near */
1112 	    xnew = Player.p_x + ROLL(1.0, 5.0);
1113 	    ynew = Player.p_y - ROLL(1.0, 5.0);
1114 	    /* fall through for check */
1115 
1116 	case A_SPECIFIC:	/* just move player */
1117 	    if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
1118 		/*
1119 		 * cannot move back from point of no return
1120 		 * pick the largest coordinate to remain unchanged
1121 		 */
1122 		{
1123 		if (fabs(xnew) > fabs(ynew))
1124 		    xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
1125 		else
1126 		    ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
1127 		}
1128 	    break;
1129 
1130 	case A_FAR:	/* pick random coordinates far */
1131 	    xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
1132 	    ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
1133 	    break;
1134 	}
1135 
1136     /* now set location flags and adjust coordinates */
1137     Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
1138 
1139     /* set up flags based upon location */
1140     Throne = Marsh = Beyond = FALSE;
1141 
1142     if (Player.p_x == 0.0 && Player.p_y == 0.0)
1143 	Throne = TRUE;
1144     else if (Circle < 35 && Circle >= 20)
1145 	Marsh = TRUE;
1146     else if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
1147 	Beyond = TRUE;
1148 
1149     Changed = TRUE;
1150 }
1151 /**/
1152 /************************************************************************
1153 /
1154 / FUNCTION NAME: readrecord()
1155 /
1156 / FUNCTION: read a player structure from file
1157 /
1158 / AUTHOR: E. A. Estes, 12/4/85
1159 /
1160 / ARGUMENTS:
1161 /	struct player *playerp - pointer to structure to fill
1162 /	int loc - location of record to read
1163 /
1164 / RETURN VALUE: none
1165 /
1166 / MODULES CALLED: fread(), fseek()
1167 /
1168 / GLOBAL INPUTS: *Playersfp
1169 /
1170 / GLOBAL OUTPUTS: none
1171 /
1172 / DESCRIPTION:
1173 /	Read structure information from player file.
1174 /
1175 /************************************************************************/
1176 
1177 readrecord(playerp, loc)
1178 register struct player	*playerp;
1179 long	loc;
1180 {
1181     fseek(Playersfp, loc, 0);
1182     fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
1183 }
1184 /**/
1185 /************************************************************************
1186 /
1187 / FUNCTION NAME: adjuststats()
1188 /
1189 / FUNCTION: adjust player statistics
1190 /
1191 / AUTHOR: E. A. Estes, 12/4/85
1192 /
1193 / ARGUMENTS: none
1194 /
1195 / RETURN VALUE: none
1196 /
1197 / MODULES CALLED: death(), floor(), drandom(), explevel(), movelevel()
1198 /
1199 / GLOBAL INPUTS: Player, *Statptr
1200 /
1201 / GLOBAL OUTPUTS: Circle, Player, Timeout
1202 /
1203 / DESCRIPTION:
1204 /	Handle adjustment and maximums on various player characteristics.
1205 /
1206 /************************************************************************/
1207 
1208 adjuststats()
1209 {
1210 double	dtemp;				/* for temporary calculations */
1211 
1212     if (explevel(Player.p_experience) > Player.p_level)
1213 	/* move one or more levels */
1214 	{
1215 	movelevel();
1216 	if (Player.p_level > 5.0)
1217 	    Timeout = TRUE;
1218 	}
1219 
1220     if (Player.p_specialtype == SC_VALAR)
1221 	/* valar */
1222 	Circle = Player.p_level / 5.0;
1223 
1224     /* calculate effective quickness */
1225     dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
1226 	- Player.p_level;;
1227     dtemp = MAX(0.0, dtemp);		/* gold slows player down */
1228     Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
1229 
1230     /* calculate effective strength */
1231     if (Player.p_poison > 0.0)
1232 	/* poison makes player weaker */
1233 	{
1234 	dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
1235 	dtemp = MAX(0.1, dtemp);
1236 	}
1237     else
1238 	dtemp = 1.0;
1239     Player.p_might = dtemp *  Player.p_strength + Player.p_sword;
1240 
1241     /* insure that important things are within limits */
1242     Player.p_quksilver = MIN(99.0, Player.p_quksilver);
1243     Player.p_mana = MIN(Player.p_mana,
1244 	Player.p_level * Statptr->c_maxmana + 1000.0);
1245     Player.p_brains = MIN(Player.p_brains,
1246 	Player.p_level * Statptr->c_maxbrains + 200.0);
1247     Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
1248 
1249     /*
1250      * some implementations have problems with floating point compare
1251      * we work around it with this stuff
1252      */
1253     Player.p_gold = floor(Player.p_gold) + 0.1;
1254     Player.p_gems = floor(Player.p_gems) + 0.1;
1255     Player.p_mana = floor(Player.p_mana) + 0.1;
1256 
1257     if (Player.p_ring.ring_type != R_NONE)
1258 	/* do ring things */
1259 	{
1260 	/* rest to max */
1261 	Player.p_energy = Player.p_maxenergy + Player.p_shield;
1262 
1263         if (Player.p_ring.ring_duration <= 0)
1264 	    /* clean up expired rings */
1265 	    switch (Player.p_ring.ring_type)
1266 		{
1267 		case R_BAD:	/* ring drives player crazy */
1268 		    Player.p_ring.ring_type = R_SPOILED;
1269 		    Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
1270 		    break;
1271 
1272 		case R_NAZREG:	/* ring disappears */
1273 		    Player.p_ring.ring_type = R_NONE;
1274 		    break;
1275 
1276 		case R_SPOILED:	/* ring kills player */
1277 		    death("A cursed ring");
1278 		    break;
1279 
1280 		case R_DLREG:	/* this ring doesn't expire */
1281 		    Player.p_ring.ring_duration = 0;
1282 		    break;
1283 		}
1284 	}
1285 
1286     if (Player.p_age / N_AGE > Player.p_degenerated)
1287 	/* age player slightly */
1288 	{
1289 	++Player.p_degenerated;
1290 	if (Player.p_quickness > 23.0)
1291 	    Player.p_quickness *= 0.99;
1292 	Player.p_strength *= 0.97;
1293 	Player.p_brains *= 0.95;
1294 	Player.p_magiclvl *= 0.97;
1295 	Player.p_maxenergy *= 0.95;
1296 	Player.p_quksilver *= 0.95;
1297 	Player.p_sword *= 0.93;
1298 	Player.p_shield *= 0.93;
1299 	}
1300 }
1301 /**/
1302 /************************************************************************
1303 /
1304 / FUNCTION NAME: initplayer()
1305 /
1306 / FUNCTION: initialize a character
1307 /
1308 / AUTHOR: E. A. Estes, 12/4/85
1309 /
1310 / ARGUMENTS:
1311 /	struct player *playerp - pointer to structure to init
1312 /
1313 / RETURN VALUE: none
1314 /
1315 / MODULES CALLED: floor(), drandom()
1316 /
1317 / GLOBAL INPUTS: none
1318 /
1319 / GLOBAL OUTPUTS: none
1320 /
1321 / DESCRIPTION:
1322 /	Put a bunch of default values in the given structure.
1323 /
1324 /************************************************************************/
1325 
1326 initplayer(playerp)
1327 register struct  player   *playerp;
1328 {
1329     playerp->p_experience =
1330     playerp->p_level =
1331     playerp->p_strength =
1332     playerp->p_sword =
1333     playerp->p_might =
1334     playerp->p_energy =
1335     playerp->p_maxenergy =
1336     playerp->p_shield =
1337     playerp->p_quickness =
1338     playerp->p_quksilver =
1339     playerp->p_speed =
1340     playerp->p_magiclvl =
1341     playerp->p_mana =
1342     playerp->p_brains =
1343     playerp->p_poison =
1344     playerp->p_gems =
1345     playerp->p_sin =
1346     playerp->p_1scratch =
1347     playerp->p_2scratch = 0.0;
1348 
1349     playerp->p_gold = ROLL(50.0, 75.0) + 0.1;	/* give some gold */
1350 
1351     playerp->p_x = ROLL(-125.0, 251.0);
1352     playerp->p_y = ROLL(-125.0, 251.0);		/* give random x, y */
1353 
1354     /* clear ring */
1355     playerp->p_ring.ring_type = R_NONE;
1356     playerp->p_ring.ring_duration = 0;
1357     playerp->p_ring.ring_inuse = FALSE;
1358 
1359     playerp->p_age = 0L;
1360 
1361     playerp->p_degenerated = 1;			/* don't degenerate initially */
1362 
1363     playerp->p_type = C_FIGHTER;		/* default */
1364     playerp->p_specialtype = SC_NONE;
1365     playerp->p_lives =
1366     playerp->p_crowns =
1367     playerp->p_charms =
1368     playerp->p_amulets =
1369     playerp->p_holywater =
1370     playerp->p_lastused = 0;
1371     playerp->p_status = S_NOTUSED;
1372     playerp->p_tampered = T_OFF;
1373     playerp->p_istat = I_OFF;
1374 
1375     playerp->p_palantir =
1376     playerp->p_blessing =
1377     playerp->p_virgin =
1378     playerp->p_blindness = FALSE;
1379 
1380     playerp->p_name[0] =
1381     playerp->p_password[0] =
1382     playerp->p_login[0] = '\0';
1383 }
1384 /**/
1385 /************************************************************************
1386 /
1387 / FUNCTION NAME: readmessage()
1388 /
1389 / FUNCTION: read message from other players
1390 /
1391 / AUTHOR: E. A. Estes, 12/4/85
1392 /
1393 / ARGUMENTS: none
1394 /
1395 / RETURN VALUE: none
1396 /
1397 / MODULES CALLED: fseek(), fgets(), wmove(), waddstr(), wclrtoeol()
1398 /
1399 / GLOBAL INPUTS: *stdscr, Databuf[], *Messagefp
1400 /
1401 / GLOBAL OUTPUTS: none
1402 /
1403 / DESCRIPTION:
1404 /	If there is a message from other players, print it.
1405 /
1406 /************************************************************************/
1407 
1408 readmessage()
1409 {
1410     move(3, 0);
1411     clrtoeol();
1412     fseek(Messagefp, 0L, 0);
1413     if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
1414 	addstr(Databuf);
1415 }
1416 /**/
1417 /************************************************************************
1418 /
1419 / FUNCTION NAME: error()
1420 /
1421 / FUNCTION: process evironment error
1422 /
1423 / AUTHOR: E. A. Estes, 12/4/85
1424 /
1425 / ARGUMENTS:
1426 /	char *whichfile - pointer to name of file which caused error
1427 /
1428 / RETURN VALUE: none
1429 /
1430 / MODULES CALLED: wclear(), cleanup()
1431 /
1432 / GLOBAL INPUTS: errno, *stdscr, printw(), printf(), Windows
1433 /
1434 / GLOBAL OUTPUTS: none
1435 /
1436 / DESCRIPTION:
1437 /	Print message about offending file, and exit.
1438 /
1439 /************************************************************************/
1440 
1441 error(whichfile)
1442 	char	*whichfile;
1443 {
1444 	int	(*funcp) __P((const char *, ...));
1445 
1446     if (Windows)
1447 	{
1448 	funcp = printw;
1449 	clear();
1450 	}
1451     else
1452 	funcp = printf;
1453 
1454     (*funcp)("An unrecoverable error has occurred reading %s.  (errno = %d)\n", whichfile, errno);
1455     (*funcp)("Please run 'setup' to determine the problem.\n");
1456     cleanup(TRUE);
1457     /*NOTREACHED*/
1458 }
1459 /**/
1460 /************************************************************************
1461 /
1462 / FUNCTION NAME: distance()
1463 /
1464 / FUNCTION: calculate distance between two points
1465 /
1466 / AUTHOR: E. A. Estes, 12/4/85
1467 /
1468 / ARGUMENTS:
1469 /	double x1, y1 - x, y coordinates of first point
1470 /	double x2, y2 - x, y coordinates of second point
1471 /
1472 / RETURN VALUE: distance between the two points
1473 /
1474 / MODULES CALLED: sqrt()
1475 /
1476 / GLOBAL INPUTS: none
1477 /
1478 / GLOBAL OUTPUTS: none
1479 /
1480 / DESCRIPTION:
1481 /	This function is provided because someone's hypot() library function
1482 /	fails if x1 == x2 && y1 == y2.
1483 /
1484 /************************************************************************/
1485 
1486 double
1487 distance(x1, x2, y1, y2)
1488 double	x1, x2, y1, y2;
1489 {
1490 double	deltax, deltay;
1491 
1492     deltax = x1 - x2;
1493     deltay = y1 - y2;
1494     return(sqrt(deltax * deltax + deltay * deltay));
1495 }
1496 
1497 /**/
1498 /************************************************************************
1499 /
1500 / FUNCTION NAME: ill_sig()
1501 /
1502 / FUNCTION: exit upon trapping an illegal signal
1503 /
1504 / AUTHOR: E. A. Estes, 12/4/85
1505 /
1506 / ARGUMENTS:
1507 /	int whichsig - signal which occured to cause jump to here
1508 /
1509 / RETURN VALUE: none
1510 /
1511 / MODULES CALLED: wclear(), printw(), cleanup()
1512 /
1513 / GLOBAL INPUTS: *stdscr
1514 /
1515 / GLOBAL OUTPUTS: none
1516 /
1517 / DESCRIPTION:
1518 /	When an illegal signal is caught, print a message, and cleanup.
1519 /
1520 /************************************************************************/
1521 
1522 ill_sig(whichsig)
1523 int whichsig;
1524 {
1525     clear();
1526     if (!(whichsig == SIGINT || whichsig == SIGQUIT))
1527 	printw("Error: caught signal # %d.\n", whichsig);
1528     cleanup(TRUE);
1529     /*NOTREACHED*/
1530 }
1531 /**/
1532 /************************************************************************
1533 /
1534 / FUNCTION NAME: descrstatus()
1535 /
1536 / FUNCTION: return a string describing the player status
1537 /
1538 / AUTHOR: E. A. Estes, 3/3/86
1539 /
1540 / ARGUMENTS:
1541 /	struct player playerp - pointer to player structure to describe
1542 /
1543 / RETURN VALUE: string describing player's status
1544 /
1545 / MODULES CALLED: none
1546 /
1547 / GLOBAL INPUTS: none
1548 /
1549 / GLOBAL OUTPUTS: none
1550 /
1551 / DESCRIPTION:
1552 /	Return verbal description of player status.
1553 /	If player status is S_PLAYING, check for low energy and blindness.
1554 /
1555 /************************************************************************/
1556 
1557 char	*
1558 descrstatus(playerp)
1559 register struct player	*playerp;
1560 {
1561     switch (playerp->p_status)
1562 	{
1563 	case S_PLAYING:
1564 	    if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
1565 		return("Low Energy");
1566 	    else if (playerp->p_blindness)
1567 		return("Blind");
1568 	    else
1569 		return("In game");
1570 
1571 	case S_CLOAKED:
1572 	    return("Cloaked");
1573 
1574 	case S_INBATTLE:
1575 	    return("In Battle");
1576 
1577 	case S_MONSTER:
1578 	    return("Encounter");
1579 
1580 	case S_TRADING:
1581 	    return("Trading");
1582 
1583 	case S_OFF:
1584 	    return("Off");
1585 
1586 	case S_HUNGUP:
1587 	    return("Hung up");
1588 
1589 	default:
1590 	    return("");
1591 	}
1592 }
1593 /**/
1594 /************************************************************************
1595 /
1596 / FUNCTION NAME: drandom()
1597 /
1598 / FUNCTION: return a random floating point number from 0.0 < 1.0
1599 /
1600 / AUTHOR: E. A. Estes, 2/7/86
1601 /
1602 / ARGUMENTS: none
1603 /
1604 / RETURN VALUE: none
1605 /
1606 / MODULES CALLED: random()
1607 /
1608 / GLOBAL INPUTS: none
1609 /
1610 / GLOBAL OUTPUTS: none
1611 /
1612 / DESCRIPTION:
1613 /	Convert random integer from library routine into a floating
1614 /	point number, and divide by the largest possible random number.
1615 /	We mask large integers with 32767 to handle sites that return
1616 /	31 bit random integers.
1617 /
1618 /************************************************************************/
1619 
1620 double
1621 drandom()
1622 {
1623     if (sizeof(int) != 2)
1624 	/* use only low bits */
1625 	return((double) (random() & 0x7fff) / 32768.0);
1626     else
1627 	return((double) random() / 32768.0);
1628 }
1629 /**/
1630 /************************************************************************
1631 /
1632 / FUNCTION NAME: collecttaxes()
1633 /
1634 / FUNCTION: collect taxes from current player
1635 /
1636 / AUTHOR: E. A. Estes, 2/7/86
1637 /
1638 / ARGUMENTS:
1639 /	double gold - amount of gold to tax
1640 /	double gems - amount of gems to tax
1641 /
1642 / RETURN VALUE: none
1643 /
1644 / MODULES CALLED: fread(), fseek(), fopen(), floor(), fwrite(), fclose()
1645 /
1646 / GLOBAL INPUTS: Player
1647 /
1648 / GLOBAL OUTPUTS: Player
1649 /
1650 / DESCRIPTION:
1651 /	Pay taxes on gold and gems.  If the player does not have enough
1652 /	gold to pay taxes on the added gems, convert some gems to gold.
1653 /	Add taxes to tax data base; add remaining gold and gems to
1654 /	player's cache.
1655 /
1656 /************************************************************************/
1657 
1658 collecttaxes(gold, gems)
1659 double	gold;
1660 double	gems;
1661 {
1662 FILE	*fp;		/* to update Goldfile */
1663 double	dtemp;		/* for temporary calculations */
1664 double	taxes;		/* tax liability */
1665 
1666     /* add to cache */
1667     Player.p_gold += gold;
1668     Player.p_gems += gems;
1669 
1670     /* calculate tax liability */
1671     taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
1672 
1673     if (Player.p_gold < taxes)
1674 	/* not enough gold to pay taxes, must convert some gems to gold */
1675 	{
1676 	dtemp = floor(taxes / N_GEMVALUE + 1.0); /* number of gems to convert */
1677 
1678 	if (Player.p_gems >= dtemp)
1679 	    /* player has enough to convert */
1680 	    {
1681 	    Player.p_gems -= dtemp;
1682 	    Player.p_gold += dtemp * N_GEMVALUE;
1683 	    }
1684 	else
1685 	    /* take everything; this should never happen */
1686 	    {
1687 	    Player.p_gold += Player.p_gems * N_GEMVALUE;
1688 	    Player.p_gems = 0.0;
1689 	    taxes = Player.p_gold;
1690 	    }
1691 	}
1692 
1693     Player.p_gold -= taxes;
1694 
1695     if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
1696 	/* update taxes */
1697 	{
1698 	dtemp = 0.0;
1699 	fread((char *) &dtemp, sizeof(double), 1, fp);
1700 	dtemp += floor(taxes);
1701 	fseek(fp, 0L, 0);
1702 	fwrite((char *) &dtemp, sizeof(double), 1, fp);
1703 	fclose(fp);
1704 	}
1705 }
1706