1/* $NetBSD: misc.c,v 1.20 2011/08/31 16:24:56 plunky 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 26static double explevel(double); 27 28static void 29movelevel(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 81const char * 82descrlocation(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 161void 162tradingpost(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 418void 419displaystats(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 430void 431allstatslist(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 463const char * 464descrtype(struct player *playerp, phbool shortflag) 465{ 466 int type; /* for caluculating 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 525long 526findname(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 543long 544allocrecord(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 565void 566freerecord(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 573void 574leavegame(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 589void 590death(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 693void 694writerecord(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 701static double 702explevel(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 710void 711truncstring(char *string) 712{ 713 int length; /* length of string */ 714 715 length = strlen(string); 716 while (string[--length] == ' ') 717 string[length] = '\0'; 718} 719 720void 721altercoordinates(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 case A_SPECIFIC: /* just move player */ 733 if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND) 734 /* 735 * cannot move back from point of no return 736 * pick the largest coordinate to remain unchanged 737 */ 738 { 739 if (fabs(xnew) > fabs(ynew)) 740 xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND); 741 else 742 ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND); 743 } 744 break; 745 746 case A_FAR: /* pick random coordinates far */ 747 xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle); 748 ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle); 749 break; 750 } 751 752 /* now set location flags and adjust coordinates */ 753 Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew)); 754 755 /* set up flags based upon location */ 756 Throne = Marsh = Beyond = FALSE; 757 758 if (Player.p_x == 0.0 && Player.p_y == 0.0) 759 Throne = TRUE; 760 else 761 if (Circle < 35 && Circle >= 20) 762 Marsh = TRUE; 763 else 764 if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND) 765 Beyond = TRUE; 766 767 Changed = TRUE; 768} 769 770void 771readrecord(struct player *playerp, long loc) 772{ 773 fseek(Playersfp, loc, SEEK_SET); 774 fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp); 775} 776 777void 778adjuststats(void) 779{ 780 double dtemp; /* for temporary calculations */ 781 782 if (explevel(Player.p_experience) > Player.p_level) 783 /* move one or more levels */ 784 { 785 movelevel(); 786 if (Player.p_level > 5.0) 787 Timeout = TRUE; 788 } 789 if (Player.p_specialtype == SC_VALAR) 790 /* valar */ 791 Circle = Player.p_level / 5.0; 792 793 /* calculate effective quickness */ 794 dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote 795 - Player.p_level; 796 dtemp = MAX(0.0, dtemp);/* gold slows player down */ 797 Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp; 798 799 /* calculate effective strength */ 800 if (Player.p_poison > 0.0) 801 /* poison makes player weaker */ 802 { 803 dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0; 804 dtemp = MAX(0.1, dtemp); 805 } else 806 dtemp = 1.0; 807 Player.p_might = dtemp * Player.p_strength + Player.p_sword; 808 809 /* insure that important things are within limits */ 810 Player.p_quksilver = MIN(99.0, Player.p_quksilver); 811 Player.p_mana = MIN(Player.p_mana, 812 Player.p_level * Statptr->c_maxmana + 1000.0); 813 Player.p_brains = MIN(Player.p_brains, 814 Player.p_level * Statptr->c_maxbrains + 200.0); 815 Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0); 816 817 /* 818 * some implementations have problems with floating point compare 819 * we work around it with this stuff 820 */ 821 Player.p_gold = floor(Player.p_gold) + 0.1; 822 Player.p_gems = floor(Player.p_gems) + 0.1; 823 Player.p_mana = floor(Player.p_mana) + 0.1; 824 825 if (Player.p_ring.ring_type != R_NONE) 826 /* do ring things */ 827 { 828 /* rest to max */ 829 Player.p_energy = Player.p_maxenergy + Player.p_shield; 830 831 if (Player.p_ring.ring_duration <= 0) 832 /* clean up expired rings */ 833 switch (Player.p_ring.ring_type) { 834 case R_BAD: /* ring drives player crazy */ 835 Player.p_ring.ring_type = R_SPOILED; 836 Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0); 837 break; 838 839 case R_NAZREG: /* ring disappears */ 840 Player.p_ring.ring_type = R_NONE; 841 break; 842 843 case R_SPOILED: /* ring kills player */ 844 death("A cursed ring"); 845 break; 846 847 case R_DLREG: /* this ring doesn't expire */ 848 Player.p_ring.ring_duration = 0; 849 break; 850 } 851 } 852 if (Player.p_age / N_AGE > Player.p_degenerated) 853 /* age player slightly */ 854 { 855 ++Player.p_degenerated; 856 if (Player.p_quickness > 23.0) 857 Player.p_quickness *= 0.99; 858 Player.p_strength *= 0.97; 859 Player.p_brains *= 0.95; 860 Player.p_magiclvl *= 0.97; 861 Player.p_maxenergy *= 0.95; 862 Player.p_quksilver *= 0.95; 863 Player.p_sword *= 0.93; 864 Player.p_shield *= 0.93; 865 } 866} 867 868void 869initplayer(struct player *playerp) 870{ 871 playerp->p_experience = 872 playerp->p_level = 873 playerp->p_strength = 874 playerp->p_sword = 875 playerp->p_might = 876 playerp->p_energy = 877 playerp->p_maxenergy = 878 playerp->p_shield = 879 playerp->p_quickness = 880 playerp->p_quksilver = 881 playerp->p_speed = 882 playerp->p_magiclvl = 883 playerp->p_mana = 884 playerp->p_brains = 885 playerp->p_poison = 886 playerp->p_gems = 887 playerp->p_sin = 888 playerp->p_1scratch = 889 playerp->p_2scratch = 0.0; 890 891 playerp->p_gold = ROLL(50.0, 75.0) + 0.1; /* give some gold */ 892 893 playerp->p_x = ROLL(-125.0, 251.0); 894 playerp->p_y = ROLL(-125.0, 251.0); /* give random x, y */ 895 896 /* clear ring */ 897 playerp->p_ring.ring_type = R_NONE; 898 playerp->p_ring.ring_duration = 0; 899 playerp->p_ring.ring_inuse = FALSE; 900 901 playerp->p_age = 0L; 902 903 playerp->p_degenerated = 1; /* don't degenerate initially */ 904 905 playerp->p_type = C_FIGHTER; /* default */ 906 playerp->p_specialtype = SC_NONE; 907 playerp->p_lives = 908 playerp->p_crowns = 909 playerp->p_charms = 910 playerp->p_amulets = 911 playerp->p_holywater = 912 playerp->p_lastused = 0; 913 playerp->p_status = S_NOTUSED; 914 playerp->p_tampered = T_OFF; 915 playerp->p_istat = I_OFF; 916 917 playerp->p_palantir = 918 playerp->p_blessing = 919 playerp->p_virgin = 920 playerp->p_blindness = FALSE; 921 922 playerp->p_name[0] = 923 playerp->p_password[0] = 924 playerp->p_login[0] = '\0'; 925} 926 927void 928readmessage(void) 929{ 930 move(3, 0); 931 clrtoeol(); 932 fseek(Messagefp, 0L, SEEK_SET); 933 if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL) 934 addstr(Databuf); 935} 936 937void 938error(const char *whichfile) 939{ 940 int (*funcp)(const char *,...); 941 942 if (Windows) { 943 funcp = printw; 944 clear(); 945 } else 946 funcp = printf; 947 948 (*funcp) ("An unrecoverable error has occurred reading %s. (%s)\n", whichfile, strerror(errno)); 949 (*funcp) ("Please run 'setup' to determine the problem.\n"); 950 cleanup(TRUE); 951 /* NOTREACHED */ 952} 953 954double 955distance(double x_1, double x_2, double y_1, double y_2) 956{ 957 double deltax, deltay; 958 959 deltax = x_1 - x_2; 960 deltay = y_1 - y_2; 961 return (sqrt(deltax * deltax + deltay * deltay)); 962} 963 964void 965ill_sig(int whichsig) 966{ 967 clear(); 968 if (!(whichsig == SIGINT || whichsig == SIGQUIT)) 969 printw("Error: caught signal # %d.\n", whichsig); 970 cleanup(TRUE); 971 /* NOTREACHED */ 972} 973 974const char * 975descrstatus(struct player *playerp) 976{ 977 switch (playerp->p_status) { 978 case S_PLAYING: 979 if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield)) 980 return ("Low Energy"); 981 else 982 if (playerp->p_blindness) 983 return ("Blind"); 984 else 985 return ("In game"); 986 987 case S_CLOAKED: 988 return ("Cloaked"); 989 990 case S_INBATTLE: 991 return ("In Battle"); 992 993 case S_MONSTER: 994 return ("Encounter"); 995 996 case S_TRADING: 997 return ("Trading"); 998 999 case S_OFF: 1000 return ("Off"); 1001 1002 case S_HUNGUP: 1003 return ("Hung up"); 1004 1005 default: 1006 return (""); 1007 } 1008} 1009 1010double 1011drandom(void) 1012{ 1013 if (sizeof(int) != 2) 1014 /* use only low bits */ 1015 return ((double) (random() & 0x7fff) / 32768.0); 1016 else 1017 return ((double) random() / 32768.0); 1018} 1019 1020void 1021collecttaxes(double gold, double gems) 1022{ 1023 FILE *fp; /* to update Goldfile */ 1024 double dtemp; /* for temporary calculations */ 1025 double taxes; /* tax liability */ 1026 1027 /* add to cache */ 1028 Player.p_gold += gold; 1029 Player.p_gems += gems; 1030 1031 /* calculate tax liability */ 1032 taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold); 1033 1034 if (Player.p_gold < taxes) 1035 /* not enough gold to pay taxes, must convert some gems to 1036 * gold */ 1037 { 1038 dtemp = floor(taxes / N_GEMVALUE + 1.0); /* number of gems to 1039 * convert */ 1040 1041 if (Player.p_gems >= dtemp) 1042 /* player has enough to convert */ 1043 { 1044 Player.p_gems -= dtemp; 1045 Player.p_gold += dtemp * N_GEMVALUE; 1046 } else 1047 /* take everything; this should never happen */ 1048 { 1049 Player.p_gold += Player.p_gems * N_GEMVALUE; 1050 Player.p_gems = 0.0; 1051 taxes = Player.p_gold; 1052 } 1053 } 1054 Player.p_gold -= taxes; 1055 1056 if ((fp = fopen(_PATH_GOLD, "r+")) != NULL) 1057 /* update taxes */ 1058 { 1059 dtemp = 0.0; 1060 fread((char *) &dtemp, sizeof(double), 1, fp); 1061 dtemp += floor(taxes); 1062 fseek(fp, 0L, SEEK_SET); 1063 fwrite((char *) &dtemp, sizeof(double), 1, fp); 1064 fclose(fp); 1065 } 1066} 1067