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