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