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