1/* $OpenBSD: misc.c,v 1.22 2023/10/10 09:43:52 tb Exp $ */ 2/* $NetBSD: misc.c,v 1.2 1995/03/24 03:59:03 cgd Exp $ */ 3 4/* 5 * misc.c Phantasia miscellaneous support routines 6 */ 7 8#include <curses.h> 9#include <err.h> 10#include <math.h> 11#include <stdint.h> 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <unistd.h> 16 17#include "macros.h" 18#include "pathnames.h" 19#include "phantdefs.h" 20#include "phantglobs.h" 21 22/************************************************************************ 23/ 24/ FUNCTION NAME: movelevel() 25/ 26/ FUNCTION: move player to new level 27/ 28/ AUTHOR: E. A. Estes, 12/4/85 29/ 30/ ARGUMENTS: none 31/ 32/ RETURN VALUE: none 33/ 34/ MODULES CALLED: death(), floor(), wmove(), drandom(), waddstr(), explevel() 35/ 36/ GLOBAL INPUTS: Player, *stdscr, *Statptr, Stattable[] 37/ 38/ GLOBAL OUTPUTS: Player, Changed 39/ 40/ DESCRIPTION: 41/ Use lookup table to increment important statistics when 42/ progressing to new experience level. 43/ Players are rested to maximum as a bonus for making a new 44/ level. 45/ Check for council of wise, and being too big to be king. 46/ 47*************************************************************************/ 48 49void 50movelevel(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 128char * 129descrlocation(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 225void 226tradingpost(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 496void 497displaystats(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 531void 532allstatslist(void) 533{ 534 static char *flags[] = /* to print value of some bools */ 535 { 536 "False", 537 " True" 538 }; 539 540 mvprintw(8, 0, "Type: %s\n", descrtype(&Player, FALSE)); 541 542 mvprintw(10, 0, "Experience: %9.0f", Player.p_experience); 543 mvprintw(11, 0, "Brains : %9.0f", Player.p_brains); 544 mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl); 545 mvprintw(13, 0, "Sin : %9.5f", Player.p_sin); 546 mvprintw(14, 0, "Poison : %9.5f", Player.p_poison); 547 mvprintw(15, 0, "Gems : %9.0f", Player.p_gems); 548 mvprintw(16, 0, "Age : %9ld", Player.p_age); 549 mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater); 550 mvprintw(11, 40, "Amulets : %9d", Player.p_amulets); 551 mvprintw(12, 40, "Charms : %9d", Player.p_charms); 552 mvprintw(13, 40, "Crowns : %9d", Player.p_crowns); 553 mvprintw(14, 40, "Shield : %9.0f", Player.p_shield); 554 mvprintw(15, 40, "Sword : %9.0f", Player.p_sword); 555 mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver); 556 557 mvprintw(18, 0, "Blessing: %s Ring: %s Virgin: %s Palantir: %s", 558 flags[(int)Player.p_blessing], 559 flags[Player.p_ring.ring_type != R_NONE], 560 flags[(int)Player.p_virgin], 561 flags[(int)Player.p_palantir]); 562} 563/**/ 564/************************************************************************ 565/ 566/ FUNCTION NAME: descrtype() 567/ 568/ FUNCTION: return a string specifying player type 569/ 570/ AUTHOR: E. A. Estes, 12/4/85 571/ 572/ ARGUMENTS: 573/ struct player playerp - pointer to structure for player 574/ bool shortflag - set if short form is desired 575/ 576/ RETURN VALUE: pointer to string describing player type 577/ 578/ MODULES CALLED: strlcpy() 579/ 580/ GLOBAL INPUTS: Databuf[] 581/ 582/ GLOBAL OUTPUTS: Databuf[] 583/ 584/ DESCRIPTION: 585/ Return a string describing the player type. 586/ King, council, valar, supersedes other types. 587/ The first character of the string is '*' if the player 588/ has a crown. 589/ If 'shortflag' is TRUE, return a 3 character string. 590/ 591*************************************************************************/ 592 593char * 594descrtype(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 681long 682findname(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 723long 724allocrecord(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 770void 771freerecord(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 802void 803leavegame(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 848void 849death(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 968void 969writerecord(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 1001double 1002explevel(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 1034void 1035truncstring(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 1071void 1072altercoordinates(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 1144void 1145readrecord(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 1174void 1175adjuststats(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 1289void 1290initplayer(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 1371void 1372readmessage(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 1406error(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 1444double 1445distance(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 1478char * 1479descrstatus(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 1537double 1538drandom(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 1571void 1572collecttaxes(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