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