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