Home | History | Annotate | Line # | Download | only in phantasia
      1 /*	$NetBSD: misc.c,v 1.24 2022/05/24 06:27:59 andvar Exp $	*/
      2 
      3 /*
      4  * misc.c  Phantasia miscellaneous support routines
      5  */
      6 
      7 #include <errno.h>
      8 #include <math.h>
      9 #include <setjmp.h>
     10 #include <signal.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <unistd.h>
     15 
     16 #include "macros.h"
     17 #include "phantdefs.h"
     18 #include "phantstruct.h"
     19 #include "phantglobs.h"
     20 #include "pathnames.h"
     21 
     22 #undef bool
     23 #include <curses.h>
     24 
     25 
     26 static double explevel(double);
     27 
     28 static void
     29 movelevel(void)
     30 {
     31 	const struct charstats *statptr; /* for pointing into Stattable */
     32 	double  new;		/* new level */
     33 	double  inc;		/* increment between new and old levels */
     34 
     35 	Changed = TRUE;
     36 
     37 	if (Player.p_type == C_EXPER)
     38 		/* roll a type to use for increment */
     39 		statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
     40 	else
     41 		statptr = Statptr;
     42 
     43 	new = explevel(Player.p_experience);
     44 	inc = new - Player.p_level;
     45 	Player.p_level = new;
     46 
     47 	/* add increments to statistics */
     48 	Player.p_strength += statptr->c_strength.increase * inc;
     49 	Player.p_mana += statptr->c_mana.increase * inc;
     50 	Player.p_brains += statptr->c_brains.increase * inc;
     51 	Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
     52 	Player.p_maxenergy += statptr->c_energy.increase * inc;
     53 
     54 	/* rest to maximum upon reaching new level */
     55 	Player.p_energy = Player.p_maxenergy + Player.p_shield;
     56 
     57 	if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
     58 		/* no longer able to be king -- turn crowns into cash */
     59 	{
     60 		Player.p_gold += ((double) Player.p_crowns) * 5000.0;
     61 		Player.p_crowns = 0;
     62 	}
     63 	if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
     64 		/* make a member of the council */
     65 	{
     66 		mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
     67 		addstr("Good Luck on your search for the Holy Grail.\n");
     68 
     69 		Player.p_specialtype = SC_COUNCIL;
     70 
     71 		/* no rings for council and above */
     72 		Player.p_ring.ring_type = R_NONE;
     73 		Player.p_ring.ring_duration = 0;
     74 
     75 		Player.p_lives = 3;	/* three extra lives */
     76 	}
     77 	if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
     78 		death("Old age");
     79 }
     80 
     81 const char   *
     82 descrlocation(struct player *playerp, phbool shortflag)
     83 {
     84 	double  circle;		/* corresponding circle for coordinates */
     85 	int     quadrant;	/* quandrant of grid */
     86 	const char   *label;	/* pointer to place name */
     87 	static const char *const nametable[4][4] =	/* names of places */
     88 	{
     89 		{"Anorien", "Ithilien", "Rohan", "Lorien"},
     90 		{"Gondor", "Mordor", "Dunland", "Rovanion"},
     91 		{"South Gondor", "Khand", "Eriador", "The Iron Hills"},
     92 		{"Far Harad", "Near Harad", "The Northern Waste", "Rhun"}
     93 	};
     94 
     95 	if (playerp->p_specialtype == SC_VALAR)
     96 		return (" is in Valhala");
     97 	else
     98 		if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0) {
     99 			if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
    100 				label = "The Point of No Return";
    101 			else
    102 				label = "The Ashen Mountains";
    103 		} else
    104 			if (circle >= 55)
    105 				label = "Morannon";
    106 			else
    107 				if (circle >= 35)
    108 					label = "Kennaquahair";
    109 				else
    110 					if (circle >= 20)
    111 						label = "The Dead Marshes";
    112 					else
    113 						if (circle >= 9)
    114 							label = "The Outer Waste";
    115 						else
    116 							if (circle >= 5)
    117 								label = "The Moors Adventurous";
    118 							else {
    119 								if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
    120 									label = "The Lord's Chamber";
    121 								else {
    122 									/* this
    123 									 *
    124 									 * expr
    125 									 * essi
    126 									 * on
    127 									 * is
    128 									 * spli
    129 									 * t
    130 									 * to
    131 									 * prev
    132 									 * ent
    133 									 * comp
    134 									 * iler
    135 									 *
    136 									 * loop
    137 									 *
    138 									 * with
    139 									 *
    140 									 * some
    141 									 *
    142 									 * comp
    143 									 * iler
    144 									 * s */
    145 									quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
    146 									quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
    147 									label = nametable[((int) circle) - 1][quadrant];
    148 								}
    149 							}
    150 
    151 	if (shortflag)
    152 		snprintf(Databuf, SZ_DATABUF, "%.29s", label);
    153 	else
    154 		snprintf(Databuf, SZ_DATABUF,
    155 			" is in %s  (%.0f,%.0f)",
    156 			label, playerp->p_x, playerp->p_y);
    157 
    158 	return (Databuf);
    159 }
    160 
    161 void
    162 tradingpost(void)
    163 {
    164 	double  numitems;	/* number of items to purchase */
    165 	double  cost;		/* cost of purchase */
    166 	double  blessingcost;	/* cost of blessing */
    167 	int     ch;		/* input */
    168 	int     size;		/* size of the trading post */
    169 	int     loop;		/* loop counter */
    170 	int     cheat = 0;	/* number of times player has tried to cheat */
    171 	bool    dishonest = FALSE;	/* set when merchant is dishonest */
    172 
    173 	Player.p_status = S_TRADING;
    174 	writerecord(&Player, Fileloc);
    175 
    176 	clear();
    177 	addstr("You are at a trading post. All purchases must be made with gold.");
    178 
    179 	size = sqrt(fabs(Player.p_x / 100)) + 1;
    180 	size = MIN(7, size);
    181 
    182 	/* set up cost of blessing */
    183 	blessingcost = 1000.0 * (Player.p_level + 5.0);
    184 
    185 	/* print Menu */
    186 	move(7, 0);
    187 	for (loop = 0; loop < size; ++loop)
    188 		/* print Menu */
    189 	{
    190 		if (loop == 6)
    191 			cost = blessingcost;
    192 		else
    193 			cost = Menu[loop].cost;
    194 		printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
    195 	}
    196 
    197 	mvprintw(5, 0, "L:Leave  P:Purchase  S:Sell Gems ? ");
    198 
    199 	for (;;) {
    200 		adjuststats();	/* truncate any bad values */
    201 
    202 		/* print some important statistics */
    203 		mvprintw(1, 0, "Gold:   %9.0f  Gems:  %9.0f  Level:   %6.0f  Charms: %6d\n",
    204 		    Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
    205 		printw("Shield: %9.0f  Sword: %9.0f  Quicksilver:%3.0f  Blessed: %s\n",
    206 		    Player.p_shield, Player.p_sword, Player.p_quksilver,
    207 		    (Player.p_blessing ? " True" : "False"));
    208 		printw("Brains: %9.0f  Mana:  %9.0f", Player.p_brains, Player.p_mana);
    209 
    210 		move(5, 36);
    211 		ch = getanswer("LPS", FALSE);
    212 		move(15, 0);
    213 		clrtobot();
    214 		switch (ch) {
    215 		case 'L':	/* leave */
    216 		case '\n':
    217 			altercoordinates(0.0, 0.0, A_NEAR);
    218 			return;
    219 
    220 		case 'P':	/* make purchase */
    221 			mvaddstr(15, 0, "What what would you like to buy ? ");
    222 			ch = getanswer(" 1234567", FALSE);
    223 			move(15, 0);
    224 			clrtoeol();
    225 
    226 			if (ch - '0' > size)
    227 				addstr("Sorry, this merchant doesn't have that.");
    228 			else
    229 				switch (ch) {
    230 				case '1':
    231 					printw("Mana is one per %.0f gold piece.  How many do you want (%.0f max) ? ",
    232 					    Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
    233 					cost = (numitems = floor(infloat())) * Menu[0].cost;
    234 
    235 					if (cost > Player.p_gold || numitems < 0)
    236 						++cheat;
    237 					else {
    238 						cheat = 0;
    239 						Player.p_gold -= cost;
    240 						if (drandom() < 0.02)
    241 							dishonest = TRUE;
    242 						else
    243 							Player.p_mana += numitems;
    244 					}
    245 					break;
    246 
    247 				case '2':
    248 					printw("Shields are %.0f per +1.  How many do you want (%.0f max) ? ",
    249 					    Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
    250 					cost = (numitems = floor(infloat())) * Menu[1].cost;
    251 
    252 					if (numitems == 0.0)
    253 						break;
    254 					else
    255 						if (cost > Player.p_gold || numitems < 0)
    256 							++cheat;
    257 						else
    258 							if (numitems < Player.p_shield)
    259 								NOBETTER();
    260 							else {
    261 								cheat = 0;
    262 								Player.p_gold -= cost;
    263 								if (drandom() < 0.02)
    264 									dishonest = TRUE;
    265 								else
    266 									Player.p_shield = numitems;
    267 							}
    268 					break;
    269 
    270 				case '3':
    271 					printw("A book costs %.0f gp.  How many do you want (%.0f max) ? ",
    272 					    Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
    273 					cost = (numitems = floor(infloat())) * Menu[2].cost;
    274 
    275 					if (cost > Player.p_gold || numitems < 0)
    276 						++cheat;
    277 					else {
    278 						cheat = 0;
    279 						Player.p_gold -= cost;
    280 						if (drandom() < 0.02)
    281 							dishonest = TRUE;
    282 						else
    283 							if (drandom() * numitems > Player.p_level / 10.0
    284 							    && numitems != 1) {
    285 								printw("\nYou blew your mind!\n");
    286 								Player.p_brains /= 5;
    287 							} else {
    288 								Player.p_brains += floor(numitems) * ROLL(20, 8);
    289 							}
    290 					}
    291 					break;
    292 
    293 				case '4':
    294 					printw("Swords are %.0f gp per +1.  How many + do you want (%.0f max) ? ",
    295 					    Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
    296 					cost = (numitems = floor(infloat())) * Menu[3].cost;
    297 
    298 					if (numitems == 0.0)
    299 						break;
    300 					else
    301 						if (cost > Player.p_gold || numitems < 0)
    302 							++cheat;
    303 						else
    304 							if (numitems < Player.p_sword)
    305 								NOBETTER();
    306 							else {
    307 								cheat = 0;
    308 								Player.p_gold -= cost;
    309 								if (drandom() < 0.02)
    310 									dishonest = TRUE;
    311 								else
    312 									Player.p_sword = numitems;
    313 							}
    314 					break;
    315 
    316 				case '5':
    317 					printw("A charm costs %.0f gp.  How many do you want (%.0f max) ? ",
    318 					    Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
    319 					cost = (numitems = floor(infloat())) * Menu[4].cost;
    320 
    321 					if (cost > Player.p_gold || numitems < 0)
    322 						++cheat;
    323 					else {
    324 						cheat = 0;
    325 						Player.p_gold -= cost;
    326 						if (drandom() < 0.02)
    327 							dishonest = TRUE;
    328 						else
    329 							Player.p_charms += numitems;
    330 					}
    331 					break;
    332 
    333 				case '6':
    334 					printw("Quicksilver is %.0f gp per +1.  How many + do you want (%.0f max) ? ",
    335 					    Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
    336 					cost = (numitems = floor(infloat())) * Menu[5].cost;
    337 
    338 					if (numitems == 0.0)
    339 						break;
    340 					else
    341 						if (cost > Player.p_gold || numitems < 0)
    342 							++cheat;
    343 						else
    344 							if (numitems < Player.p_quksilver)
    345 								NOBETTER();
    346 							else {
    347 								cheat = 0;
    348 								Player.p_gold -= cost;
    349 								if (drandom() < 0.02)
    350 									dishonest = TRUE;
    351 								else
    352 									Player.p_quksilver = numitems;
    353 							}
    354 					break;
    355 
    356 				case '7':
    357 					if (Player.p_blessing) {
    358 						addstr("You already have a blessing.");
    359 						break;
    360 					}
    361 					printw("A blessing requires a %.0f gp donation.  Still want one ? ", blessingcost);
    362 					ch = getanswer("NY", FALSE);
    363 
    364 					if (ch == 'Y') {
    365 						if (Player.p_gold < blessingcost)
    366 							++cheat;
    367 						else {
    368 							cheat = 0;
    369 							Player.p_gold -= blessingcost;
    370 							if (drandom() < 0.02)
    371 								dishonest = TRUE;
    372 							else
    373 								Player.p_blessing = TRUE;
    374 						}
    375 					}
    376 					break;
    377 				}
    378 			break;
    379 
    380 		case 'S':	/* sell gems */
    381 			mvprintw(15, 0, "A gem is worth %.0f gp.  How many do you want to sell (%.0f max) ? ",
    382 			    (double) N_GEMVALUE, Player.p_gems);
    383 			numitems = floor(infloat());
    384 
    385 			if (numitems > Player.p_gems || numitems < 0)
    386 				++cheat;
    387 			else {
    388 				cheat = 0;
    389 				Player.p_gems -= numitems;
    390 				Player.p_gold += numitems * N_GEMVALUE;
    391 			}
    392 		}
    393 
    394 		if (cheat == 1)
    395 			mvaddstr(17, 0, "Come on, merchants aren't stupid.  Stop cheating.\n");
    396 		else
    397 			if (cheat == 2) {
    398 				mvaddstr(17, 0, "You had your chance.  This merchant happens to be\n");
    399 				printw("a %.0f level magic user, and you made %s mad!\n",
    400 				    ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
    401 				altercoordinates(0.0, 0.0, A_FAR);
    402 				Player.p_energy /= 2.0;
    403 				++Player.p_sin;
    404 				more(23);
    405 				return;
    406 			} else
    407 				if (dishonest) {
    408 					mvaddstr(17, 0, "The merchant stole your money!");
    409 					refresh();
    410 					altercoordinates(Player.p_x - Player.p_x / 10.0,
    411 					    Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
    412 					sleep(2);
    413 					return;
    414 				}
    415 	}
    416 }
    417 
    418 void
    419 displaystats(void)
    420 {
    421 	mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
    422 	mvprintw(1, 0, "Level :%7.0f   Energy  :%9.0f(%9.0f)  Mana :%9.0f  Users:%3d\n",
    423 	    Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
    424 	    Player.p_mana, Users);
    425 	mvprintw(2, 0, "Quick :%3.0f(%3.0f)  Strength:%9.0f(%9.0f)  Gold :%9.0f  %s\n",
    426 	    Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
    427 	    Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
    428 }
    429 
    430 void
    431 allstatslist(void)
    432 {
    433 	static const char *const flags[] = /* to print value of some bools */
    434 	{
    435 		"False",
    436 		" True"
    437 	};
    438 
    439 	mvprintw(8, 0, "Type: %s\n", descrtype(&Player, FALSE));
    440 
    441 	mvprintw(10, 0, "Experience: %9.0f", Player.p_experience);
    442 	mvprintw(11, 0, "Brains    : %9.0f", Player.p_brains);
    443 	mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl);
    444 	mvprintw(13, 0, "Sin       : %9.5f", Player.p_sin);
    445 	mvprintw(14, 0, "Poison    : %9.5f", Player.p_poison);
    446 	mvprintw(15, 0, "Gems      : %9.0f", Player.p_gems);
    447 	mvprintw(16, 0, "Age       : %9ld", Player.p_age);
    448 	mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
    449 	mvprintw(11, 40, "Amulets   : %9d", Player.p_amulets);
    450 	mvprintw(12, 40, "Charms    : %9d", Player.p_charms);
    451 	mvprintw(13, 40, "Crowns    : %9d", Player.p_crowns);
    452 	mvprintw(14, 40, "Shield    : %9.0f", Player.p_shield);
    453 	mvprintw(15, 40, "Sword     : %9.0f", Player.p_sword);
    454 	mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
    455 
    456 	mvprintw(18, 0, "Blessing: %s   Ring: %s   Virgin: %s   Palantir: %s",
    457 	    flags[(int)Player.p_blessing],
    458 	    flags[Player.p_ring.ring_type != R_NONE],
    459 	    flags[(int)Player.p_virgin],
    460 	    flags[(int)Player.p_palantir]);
    461 }
    462 
    463 const char   *
    464 descrtype(struct player *playerp, phbool shortflag)
    465 {
    466 	int     type;		/* for calculating result subscript */
    467 	static const char *const results[] =/* description table */
    468 	{
    469 		" Magic User", " MU",
    470 		" Fighter", " F ",
    471 		" Elf", " E ",
    472 		" Dwarf", " D ",
    473 		" Halfling", " H ",
    474 		" Experimento", " EX",
    475 		" Super", " S ",
    476 		" King", " K ",
    477 		" Council of Wise", " CW",
    478 		" Ex-Valar", " EV",
    479 		" Valar", " V ",
    480 		" ? ", " ? "
    481 	};
    482 
    483 	type = playerp->p_type;
    484 
    485 	switch (playerp->p_specialtype) {
    486 	case SC_NONE:
    487 		type = playerp->p_type;
    488 		break;
    489 
    490 	case SC_KING:
    491 		type = 7;
    492 		break;
    493 
    494 	case SC_COUNCIL:
    495 		type = 8;
    496 		break;
    497 
    498 	case SC_EXVALAR:
    499 		type = 9;
    500 		break;
    501 
    502 	case SC_VALAR:
    503 		type = 10;
    504 		break;
    505 	}
    506 
    507 	type *= 2;		/* calculate offset */
    508 
    509 	if (type > 20)
    510 		/* error */
    511 		type = 22;
    512 
    513 	if (shortflag)
    514 		/* use short descriptions */
    515 		++type;
    516 
    517 	if (playerp->p_crowns > 0) {
    518 		strcpy(Databuf, results[type]);
    519 		Databuf[0] = '*';
    520 		return (Databuf);
    521 	} else
    522 		return (results[type]);
    523 }
    524 
    525 long
    526 findname(const char *name, struct player *playerp)
    527 {
    528 	long    loc = 0;	/* location in the file */
    529 
    530 	fseek(Playersfp, 0L, SEEK_SET);
    531 	while (fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
    532 		if (strcmp(playerp->p_name, name) == 0) {
    533 			if (playerp->p_status != S_NOTUSED || Wizard)
    534 				/* found it */
    535 				return (loc);
    536 		}
    537 		loc += SZ_PLAYERSTRUCT;
    538 	}
    539 
    540 	return (-1);
    541 }
    542 
    543 long
    544 allocrecord(void)
    545 {
    546 	long    loc = 0L;	/* location in file */
    547 
    548 	fseek(Playersfp, 0L, SEEK_SET);
    549 	while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
    550 		if (Other.p_status == S_NOTUSED)
    551 			/* found an empty record */
    552 			return (loc);
    553 		else
    554 			loc += SZ_PLAYERSTRUCT;
    555 	}
    556 
    557 	/* make a new record */
    558 	initplayer(&Other);
    559 	Player.p_status = S_OFF;
    560 	writerecord(&Other, loc);
    561 
    562 	return (loc);
    563 }
    564 
    565 void
    566 freerecord(struct player *playerp, long loc)
    567 {
    568 	playerp->p_name[0] = CH_MARKDELETE;
    569 	playerp->p_status = S_NOTUSED;
    570 	writerecord(playerp, loc);
    571 }
    572 
    573 void
    574 leavegame(void)
    575 {
    576 
    577 	if (Player.p_level < 1.0)
    578 		/* delete character */
    579 		freerecord(&Player, Fileloc);
    580 	else {
    581 		Player.p_status = S_OFF;
    582 		writerecord(&Player, Fileloc);
    583 	}
    584 
    585 	cleanup(TRUE);
    586 	/* NOTREACHED */
    587 }
    588 
    589 void
    590 death(const char *how)
    591 {
    592 	FILE   *fp;		/* for updating various files */
    593 	int     ch;		/* input */
    594 	static const char *const deathmesg[] =
    595 	/* add more messages here, if desired */
    596 	{
    597 		"You have been wounded beyond repair.  ",
    598 		"You have been disemboweled.  ",
    599 		"You've been mashed, mauled, and spit upon.  (You're dead.)\n",
    600 		"You died!  ",
    601 		"You're a complete failure -- you've died!!\n",
    602 		"You have been dealt a fatal blow!  "
    603 	};
    604 
    605 	clear();
    606 
    607 	if (strcmp(how, "Stupidity") != 0) {
    608 		if (Player.p_level > 9999.0)
    609 			/* old age */
    610 			addstr("Characters must be retired upon reaching level 10000.  Sorry.");
    611 		else
    612 			if (Player.p_lives > 0)
    613 				/* extra lives */
    614 			{
    615 				addstr("You should be more cautious.  You've been killed.\n");
    616 				printw("You only have %d more chance(s).\n", --Player.p_lives);
    617 				more(3);
    618 				Player.p_energy = Player.p_maxenergy;
    619 				return;
    620 			} else
    621 				if (Player.p_specialtype == SC_VALAR) {
    622 					addstr("You had your chances, but Valar aren't totally\n");
    623 					addstr("immortal.  You are now left to wither and die . . .\n");
    624 					more(3);
    625 					Player.p_brains = Player.p_level / 25.0;
    626 					Player.p_energy = Player.p_maxenergy /= 5.0;
    627 					Player.p_quksilver = Player.p_sword = 0.0;
    628 					Player.p_specialtype = SC_COUNCIL;
    629 					return;
    630 				} else
    631 					if (Player.p_ring.ring_inuse &&
    632 					    (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
    633 						/* good ring in use - saved
    634 						 * from death */
    635 					{
    636 						mvaddstr(4, 0, "Your ring saved you from death!\n");
    637 						refresh();
    638 						Player.p_ring.ring_type = R_NONE;
    639 						Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
    640 						if (Player.p_crowns > 0)
    641 							--Player.p_crowns;
    642 						return;
    643 					} else
    644 						if (Player.p_ring.ring_type == R_BAD
    645 						    || Player.p_ring.ring_type == R_SPOILED)
    646 							/* bad ring in
    647 							 * possession; name
    648 							 * idiot after player */
    649 						{
    650 							mvaddstr(4, 0,
    651 							    "Your ring has taken control of you and turned you into a monster!\n");
    652 							fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
    653 							fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
    654 							strcpy(Curmonster.m_name, Player.p_name);
    655 							fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
    656 							fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
    657 							fflush(Monstfp);
    658 						}
    659 	}
    660 	enterscore();		/* update score board */
    661 
    662 	/* put info in last dead file */
    663 	fp = fopen(_PATH_LASTDEAD, "w");
    664 	fprintf(fp, "%s (%s, run by %s, level %.0f, killed by %s)",
    665 	    Player.p_name, descrtype(&Player, TRUE),
    666 	    Player.p_login, Player.p_level, how);
    667 	fclose(fp);
    668 
    669 	/* let other players know */
    670 	fp = fopen(_PATH_MESS, "w");
    671 	fprintf(fp, "%s was killed by %s.", Player.p_name, how);
    672 	fclose(fp);
    673 
    674 	freerecord(&Player, Fileloc);
    675 
    676 	clear();
    677 	move(10, 0);
    678 	addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
    679 	addstr("Care to give it another try ? ");
    680 	ch = getanswer("NY", FALSE);
    681 
    682 	if (ch == 'Y') {
    683 		cleanup(FALSE);
    684 		execl(_PATH_GAMEPROG, "phantasia", "-s",
    685 		    (Wizard ? "-S" : (char *) NULL), (char *) NULL);
    686 		exit(0);
    687 		/* NOTREACHED */
    688 	}
    689 	cleanup(TRUE);
    690 	/* NOTREACHED */
    691 }
    692 
    693 void
    694 writerecord(struct player *playerp, long place)
    695 {
    696 	fseek(Playersfp, place, SEEK_SET);
    697 	fwrite((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
    698 	fflush(Playersfp);
    699 }
    700 
    701 static double
    702 explevel(double experience)
    703 {
    704 	if (experience < 1.1e7)
    705 		return (floor(pow((experience / 1000.0), 0.4875)));
    706 	else
    707 		return (floor(pow((experience / 1250.0), 0.4865)));
    708 }
    709 
    710 void
    711 truncstring(char *string)
    712 {
    713 	int     length;		/* length of string */
    714 
    715 	length = strlen(string);
    716 	while (string[--length] == ' ')
    717 		string[length] = '\0';
    718 }
    719 
    720 void
    721 altercoordinates(double xnew, double ynew, int operation)
    722 {
    723 	switch (operation) {
    724 	case A_FORCED:		/* move with no checks */
    725 		break;
    726 
    727 	case A_NEAR:		/* pick random coordinates near */
    728 		xnew = Player.p_x + ROLL(1.0, 5.0);
    729 		ynew = Player.p_y - ROLL(1.0, 5.0);
    730 		/* fall through for check */
    731 
    732 		/* FALLTHROUGH */
    733 	case A_SPECIFIC:	/* just move player */
    734 		if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
    735 			/*
    736 			 * cannot move back from point of no return
    737 			 * pick the largest coordinate to remain unchanged
    738 			 */
    739 		{
    740 			if (fabs(xnew) > fabs(ynew))
    741 				xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
    742 			else
    743 				ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
    744 		}
    745 		break;
    746 
    747 	case A_FAR:		/* pick random coordinates far */
    748 		xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
    749 		ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
    750 		break;
    751 	}
    752 
    753 	/* now set location flags and adjust coordinates */
    754 	Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
    755 
    756 	/* set up flags based upon location */
    757 	Throne = Marsh = Beyond = FALSE;
    758 
    759 	if (Player.p_x == 0.0 && Player.p_y == 0.0)
    760 		Throne = TRUE;
    761 	else
    762 		if (Circle < 35 && Circle >= 20)
    763 			Marsh = TRUE;
    764 		else
    765 			if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
    766 				Beyond = TRUE;
    767 
    768 	Changed = TRUE;
    769 }
    770 
    771 void
    772 readrecord(struct player *playerp, long loc)
    773 {
    774 	fseek(Playersfp, loc, SEEK_SET);
    775 	fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
    776 }
    777 
    778 void
    779 adjuststats(void)
    780 {
    781 	double  dtemp;		/* for temporary calculations */
    782 
    783 	if (explevel(Player.p_experience) > Player.p_level)
    784 		/* move one or more levels */
    785 	{
    786 		movelevel();
    787 		if (Player.p_level > 5.0)
    788 			Timeout = TRUE;
    789 	}
    790 	if (Player.p_specialtype == SC_VALAR)
    791 		/* valar */
    792 		Circle = Player.p_level / 5.0;
    793 
    794 	/* calculate effective quickness */
    795 	dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
    796 	    - Player.p_level;
    797 	dtemp = MAX(0.0, dtemp);/* gold slows player down */
    798 	Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
    799 
    800 	/* calculate effective strength */
    801 	if (Player.p_poison > 0.0)
    802 		/* poison makes player weaker */
    803 	{
    804 		dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
    805 		dtemp = MAX(0.1, dtemp);
    806 	} else
    807 		dtemp = 1.0;
    808 	Player.p_might = dtemp * Player.p_strength + Player.p_sword;
    809 
    810 	/* insure that important things are within limits */
    811 	Player.p_quksilver = MIN(99.0, Player.p_quksilver);
    812 	Player.p_mana = MIN(Player.p_mana,
    813 	    Player.p_level * Statptr->c_maxmana + 1000.0);
    814 	Player.p_brains = MIN(Player.p_brains,
    815 	    Player.p_level * Statptr->c_maxbrains + 200.0);
    816 	Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
    817 
    818 	/*
    819          * some implementations have problems with floating point compare
    820          * we work around it with this stuff
    821          */
    822 	Player.p_gold = floor(Player.p_gold) + 0.1;
    823 	Player.p_gems = floor(Player.p_gems) + 0.1;
    824 	Player.p_mana = floor(Player.p_mana) + 0.1;
    825 
    826 	if (Player.p_ring.ring_type != R_NONE)
    827 		/* do ring things */
    828 	{
    829 		/* rest to max */
    830 		Player.p_energy = Player.p_maxenergy + Player.p_shield;
    831 
    832 		if (Player.p_ring.ring_duration <= 0)
    833 			/* clean up expired rings */
    834 			switch (Player.p_ring.ring_type) {
    835 			case R_BAD:	/* ring drives player crazy */
    836 				Player.p_ring.ring_type = R_SPOILED;
    837 				Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
    838 				break;
    839 
    840 			case R_NAZREG:	/* ring disappears */
    841 				Player.p_ring.ring_type = R_NONE;
    842 				break;
    843 
    844 			case R_SPOILED:	/* ring kills player */
    845 				death("A cursed ring");
    846 				break;
    847 
    848 			case R_DLREG:	/* this ring doesn't expire */
    849 				Player.p_ring.ring_duration = 0;
    850 				break;
    851 			}
    852 	}
    853 	if (Player.p_age / N_AGE > Player.p_degenerated)
    854 		/* age player slightly */
    855 	{
    856 		++Player.p_degenerated;
    857 		if (Player.p_quickness > 23.0)
    858 			Player.p_quickness *= 0.99;
    859 		Player.p_strength *= 0.97;
    860 		Player.p_brains *= 0.95;
    861 		Player.p_magiclvl *= 0.97;
    862 		Player.p_maxenergy *= 0.95;
    863 		Player.p_quksilver *= 0.95;
    864 		Player.p_sword *= 0.93;
    865 		Player.p_shield *= 0.93;
    866 	}
    867 }
    868 
    869 void
    870 initplayer(struct player *playerp)
    871 {
    872 	playerp->p_experience =
    873 	    playerp->p_level =
    874 	    playerp->p_strength =
    875 	    playerp->p_sword =
    876 	    playerp->p_might =
    877 	    playerp->p_energy =
    878 	    playerp->p_maxenergy =
    879 	    playerp->p_shield =
    880 	    playerp->p_quickness =
    881 	    playerp->p_quksilver =
    882 	    playerp->p_speed =
    883 	    playerp->p_magiclvl =
    884 	    playerp->p_mana =
    885 	    playerp->p_brains =
    886 	    playerp->p_poison =
    887 	    playerp->p_gems =
    888 	    playerp->p_sin =
    889 	    playerp->p_1scratch =
    890 	    playerp->p_2scratch = 0.0;
    891 
    892 	playerp->p_gold = ROLL(50.0, 75.0) + 0.1;	/* give some gold */
    893 
    894 	playerp->p_x = ROLL(-125.0, 251.0);
    895 	playerp->p_y = ROLL(-125.0, 251.0);	/* give random x, y */
    896 
    897 	/* clear ring */
    898 	playerp->p_ring.ring_type = R_NONE;
    899 	playerp->p_ring.ring_duration = 0;
    900 	playerp->p_ring.ring_inuse = FALSE;
    901 
    902 	playerp->p_age = 0L;
    903 
    904 	playerp->p_degenerated = 1;	/* don't degenerate initially */
    905 
    906 	playerp->p_type = C_FIGHTER;	/* default */
    907 	playerp->p_specialtype = SC_NONE;
    908 	playerp->p_lives =
    909 	    playerp->p_crowns =
    910 	    playerp->p_charms =
    911 	    playerp->p_amulets =
    912 	    playerp->p_holywater =
    913 	    playerp->p_lastused = 0;
    914 	playerp->p_status = S_NOTUSED;
    915 	playerp->p_tampered = T_OFF;
    916 	playerp->p_istat = I_OFF;
    917 
    918 	playerp->p_palantir =
    919 	    playerp->p_blessing =
    920 	    playerp->p_virgin =
    921 	    playerp->p_blindness = FALSE;
    922 
    923 	playerp->p_name[0] =
    924 	    playerp->p_password[0] =
    925 	    playerp->p_login[0] = '\0';
    926 }
    927 
    928 void
    929 readmessage(void)
    930 {
    931 	move(3, 0);
    932 	clrtoeol();
    933 	fseek(Messagefp, 0L, SEEK_SET);
    934 	if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
    935 		addstr(Databuf);
    936 }
    937 
    938 void
    939 error(const char *whichfile)
    940 {
    941 	int     (*funcp)(const char *,...);
    942 
    943 	if (Windows) {
    944 		funcp = printw;
    945 		clear();
    946 	} else
    947 		funcp = printf;
    948 
    949 	(*funcp) ("An unrecoverable error has occurred reading %s.  (%s)\n", whichfile, strerror(errno));
    950 	(*funcp) ("Please run 'setup' to determine the problem.\n");
    951 	cleanup(TRUE);
    952 	/* NOTREACHED */
    953 }
    954 
    955 double
    956 distance(double x_1, double x_2, double y_1, double y_2)
    957 {
    958 	double  deltax, deltay;
    959 
    960 	deltax = x_1 - x_2;
    961 	deltay = y_1 - y_2;
    962 	return (sqrt(deltax * deltax + deltay * deltay));
    963 }
    964 
    965 void
    966 ill_sig(int whichsig)
    967 {
    968 	clear();
    969 	if (!(whichsig == SIGINT || whichsig == SIGQUIT))
    970 		printw("Error: caught signal # %d.\n", whichsig);
    971 	cleanup(TRUE);
    972 	/* NOTREACHED */
    973 }
    974 
    975 const char *
    976 descrstatus(struct player *playerp)
    977 {
    978 	switch (playerp->p_status) {
    979 	case S_PLAYING:
    980 		if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
    981 			return ("Low Energy");
    982 		else
    983 			if (playerp->p_blindness)
    984 				return ("Blind");
    985 			else
    986 				return ("In game");
    987 
    988 	case S_CLOAKED:
    989 		return ("Cloaked");
    990 
    991 	case S_INBATTLE:
    992 		return ("In Battle");
    993 
    994 	case S_MONSTER:
    995 		return ("Encounter");
    996 
    997 	case S_TRADING:
    998 		return ("Trading");
    999 
   1000 	case S_OFF:
   1001 		return ("Off");
   1002 
   1003 	case S_HUNGUP:
   1004 		return ("Hung up");
   1005 
   1006 	default:
   1007 		return ("");
   1008 	}
   1009 }
   1010 
   1011 double
   1012 drandom(void)
   1013 {
   1014 	if (sizeof(int) != 2)
   1015 		/* use only low bits */
   1016 		return ((double) (random() & 0x7fff) / 32768.0);
   1017 	else
   1018 		return ((double) random() / 32768.0);
   1019 }
   1020 
   1021 void
   1022 collecttaxes(double gold, double gems)
   1023 {
   1024 	FILE   *fp;		/* to update Goldfile */
   1025 	double  dtemp;		/* for temporary calculations */
   1026 	double  taxes;		/* tax liability */
   1027 
   1028 	/* add to cache */
   1029 	Player.p_gold += gold;
   1030 	Player.p_gems += gems;
   1031 
   1032 	/* calculate tax liability */
   1033 	taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
   1034 
   1035 	if (Player.p_gold < taxes)
   1036 		/* not enough gold to pay taxes, must convert some gems to
   1037 		 * gold */
   1038 	{
   1039 		dtemp = floor(taxes / N_GEMVALUE + 1.0);	/* number of gems to
   1040 								 * convert */
   1041 
   1042 		if (Player.p_gems >= dtemp)
   1043 			/* player has enough to convert */
   1044 		{
   1045 			Player.p_gems -= dtemp;
   1046 			Player.p_gold += dtemp * N_GEMVALUE;
   1047 		} else
   1048 			/* take everything; this should never happen */
   1049 		{
   1050 			Player.p_gold += Player.p_gems * N_GEMVALUE;
   1051 			Player.p_gems = 0.0;
   1052 			taxes = Player.p_gold;
   1053 		}
   1054 	}
   1055 	Player.p_gold -= taxes;
   1056 
   1057 	if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
   1058 		/* update taxes */
   1059 	{
   1060 		dtemp = 0.0;
   1061 		fread((char *) &dtemp, sizeof(double), 1, fp);
   1062 		dtemp += floor(taxes);
   1063 		fseek(fp, 0L, SEEK_SET);
   1064 		fwrite((char *) &dtemp, sizeof(double), 1, fp);
   1065 		fclose(fp);
   1066 	}
   1067 }
   1068