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