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