1/* $NetBSD: fight.c,v 1.12 2009/08/12 08:21:41 dholland Exp $ */ 2 3/* 4 * fight.c Phantasia monster fighting routines 5 */ 6 7#include <math.h> 8#include <setjmp.h> 9#include <stdio.h> 10#include <string.h> 11 12#include "macros.h" 13#include "phantdefs.h" 14#include "phantstruct.h" 15#include "phantglobs.h" 16 17#undef bool 18#include <curses.h> 19 20static void awardtreasure(void); 21static void callmonster(int); 22static void cancelmonster(void); 23static void cursedtreasure(void); 24static void hitmonster(double); 25static void monsthits(void); 26static int pickmonster(void); 27static void playerhits(void); 28static void scramblestats(void); 29static void throwspell(void); 30 31void 32encounter(int particular) 33{ 34 volatile bool firsthit = Player.p_blessing; /* set if player gets 35 * the first hit */ 36 volatile int flockcnt = 1; /* how many time flocked */ 37 38 /* let others know what we are doing */ 39 Player.p_status = S_MONSTER; 40 writerecord(&Player, Fileloc); 41 42#ifdef SYS5 43 flushinp(); 44#endif 45 46 Shield = 0.0; /* no shield up yet */ 47 48 if (particular >= 0) 49 /* monster is specified */ 50 Whichmonster = particular; 51 else 52 /* pick random monster */ 53 Whichmonster = pickmonster(); 54 55 setjmp(Fightenv); /* this is to enable changing fight state */ 56 57 move(6, 0); 58 clrtobot(); /* clear bottom area of screen */ 59 60 Lines = 9; 61 callmonster(Whichmonster); /* set up monster to fight */ 62 63 Luckout = FALSE; /* haven't tried to luckout yet */ 64 65 if (Curmonster.m_type == SM_MORGOTH) 66 mvprintw(4, 0, "You've encountered %s, Bane of the Council and Valar.\n", 67 Enemyname); 68 69 if (Curmonster.m_type == SM_UNICORN) { 70 if (Player.p_virgin) { 71 printw("You just subdued %s, thanks to the virgin.\n", Enemyname); 72 Player.p_virgin = FALSE; 73 } else { 74 printw("You just saw %s running away!\n", Enemyname); 75 Curmonster.m_experience = 0.0; 76 Curmonster.m_treasuretype = 0; 77 } 78 } else 79 /* not a special monster */ 80 for (;;) 81 /* print header, and arbitrate between player and 82 * monster */ 83 { 84 mvprintw(6, 0, "You are being attacked by %s, EXP: %.0f (Size: %.0f)\n", 85 Enemyname, Curmonster.m_experience, Circle); 86 87 displaystats(); 88 mvprintw(1, 26, "%20.0f", Player.p_energy + Shield); /* overprint energy */ 89 readmessage(); 90 91 if (Curmonster.m_type == SM_DARKLORD 92 && Player.p_blessing 93 && Player.p_charms > 0) 94 /* overpower Dark Lord with blessing and charm */ 95 { 96 mvprintw(7, 0, "You just overpowered %s!", Enemyname); 97 Lines = 8; 98 Player.p_blessing = FALSE; 99 --Player.p_charms; 100 break; 101 } 102 /* allow paralyzed monster to wake up */ 103 Curmonster.m_speed = MIN(Curmonster.m_speed + 1.0, Curmonster.m_maxspeed); 104 105 if (drandom() * Curmonster.m_speed > drandom() * Player.p_speed 106 /* monster is faster */ 107 && Curmonster.m_type != SM_DARKLORD 108 /* not D. L. */ 109 && Curmonster.m_type != SM_SHRIEKER 110 /* not mimic */ 111 && !firsthit) 112 /* monster gets a hit */ 113 monsthits(); 114 else 115 /* player gets a hit */ 116 { 117 firsthit = FALSE; 118 playerhits(); 119 } 120 121 refresh(); 122 123 if (Lines > LINES - 2) 124 /* near bottom of screen - pause */ 125 { 126 more(Lines); 127 move(Lines = 8, 0); 128 clrtobot(); 129 } 130 if (Player.p_energy <= 0.0) 131 /* player died */ 132 { 133 more(Lines); 134 death(Enemyname); 135 cancelmonster(); 136 break; /* fight ends if the player is saved 137 * from death */ 138 } 139 if (Curmonster.m_energy <= 0.0) 140 /* monster died */ 141 break; 142 } 143 144 /* give player credit for killing monster */ 145 Player.p_experience += Curmonster.m_experience; 146 147 if (drandom() < Curmonster.m_flock / 100.0) 148 /* monster flocks */ 149 { 150 more(Lines); 151 ++flockcnt; 152 longjmp(Fightenv, 0); 153 /* NOTREACHED */ 154 } else 155 if (Circle > 1.0 156 && Curmonster.m_treasuretype > 0 157 && drandom() > 0.2 + pow(0.4, (double) (flockcnt / 3 + Circle / 3.0))) 158 /* monster has treasure; this takes # of flocks and 159 * size into account */ 160 { 161 more(Lines); 162 awardtreasure(); 163 } 164 /* pause before returning */ 165 getyx(stdscr, Lines, flockcnt); 166 more(Lines + 1); 167 168 Player.p_ring.ring_inuse = FALSE; /* not using ring */ 169 170 /* clean up the screen */ 171 move(4, 0); 172 clrtobot(); 173} 174 175static int 176pickmonster(void) 177{ 178 if (Player.p_specialtype == SC_VALAR) 179 /* even chance of any monster */ 180 return ((int) ROLL(0.0, 100.0)); 181 182 if (Marsh) 183 /* water monsters */ 184 return ((int) ROLL(0.0, 15.0)); 185 186 else 187 if (Circle > 24) 188 /* even chance of all non-water monsters */ 189 return ((int) ROLL(14.0, 86.0)); 190 191 else 192 if (Circle > 15) 193 /* chance of all non-water monsters, weighted 194 * toward middle */ 195 return ((int) (ROLL(0.0, 50.0) + ROLL(14.0, 37.0))); 196 197 else 198 if (Circle > 8) 199 /* not all non-water monsters, 200 * weighted toward middle */ 201 return ((int) (ROLL(0.0, 50.0) + ROLL(14.0, 26.0))); 202 203 else 204 if (Circle > 3) 205 /* even chance of some tamer 206 * non-water monsters */ 207 return ((int) ROLL(14.0, 50.0)); 208 209 else 210 /* even chance of some of the 211 * tamest non-water monsters */ 212 return ((int) ROLL(14.0, 25.0)); 213} 214 215static void 216playerhits(void) 217{ 218 double inflict; /* damage inflicted */ 219 int ch; /* input */ 220 221 mvaddstr(7, 0, "1:Melee 2:Skirmish 3:Evade 4:Spell 5:Nick "); 222 223 if (!Luckout) { 224 /* haven't tried to luckout yet */ 225 if (Curmonster.m_type == SM_MORGOTH) 226 /* cannot luckout against Morgoth */ 227 addstr("6:Ally "); 228 else 229 addstr("6:Luckout "); 230 } 231 232 if (Player.p_ring.ring_type != R_NONE) 233 /* player has a ring */ 234 addstr("7:Use Ring "); 235 else 236 clrtoeol(); 237 238 ch = inputoption(); 239 240 move(8, 0); 241 clrtobot(); /* clear any messages from before */ 242 Lines = 9; 243 mvaddstr(4, 0, "\n\n"); /* clear status area */ 244 245 switch (ch) { 246 case 'T': /* timeout; lose turn */ 247 break; 248 249 case ' ': 250 case '1': /* melee */ 251 /* melee affects monster's energy and strength */ 252 inflict = ROLL(Player.p_might / 2.0 + 5.0, 1.3 * Player.p_might) 253 + (Player.p_ring.ring_inuse ? Player.p_might : 0.0); 254 255 Curmonster.m_melee += inflict; 256 Curmonster.m_strength = Curmonster.m_o_strength 257 - Curmonster.m_melee / Curmonster.m_o_energy 258 * Curmonster.m_o_strength / 4.0; 259 hitmonster(inflict); 260 break; 261 262 case '2': /* skirmish */ 263 /* skirmish affects monter's energy and speed */ 264 inflict = ROLL(Player.p_might / 3.0 + 3.0, 1.1 * Player.p_might) 265 + (Player.p_ring.ring_inuse ? Player.p_might : 0.0); 266 267 Curmonster.m_skirmish += inflict; 268 Curmonster.m_maxspeed = Curmonster.m_o_speed 269 - Curmonster.m_skirmish / Curmonster.m_o_energy 270 * Curmonster.m_o_speed / 4.0; 271 hitmonster(inflict); 272 break; 273 274 case '3': /* evade */ 275 /* use brains and speed to try to evade */ 276 if ((Curmonster.m_type == SM_DARKLORD 277 || Curmonster.m_type == SM_SHRIEKER 278 /* can always run from D. L. and shrieker */ 279 || drandom() * Player.p_speed * Player.p_brains 280 > drandom() * Curmonster.m_speed * Curmonster.m_brains) 281 && (Curmonster.m_type != SM_MIMIC)) 282 /* cannot run from mimic */ 283 { 284 mvaddstr(Lines++, 0, "You got away!"); 285 cancelmonster(); 286 altercoordinates(0.0, 0.0, A_NEAR); 287 } else 288 mvprintw(Lines++, 0, "%s is still after you!", Enemyname); 289 290 break; 291 292 case 'M': 293 case '4': /* magic spell */ 294 throwspell(); 295 break; 296 297 case '5': /* nick */ 298 /* hit 1 plus sword; give some experience */ 299 inflict = 1.0 + Player.p_sword; 300 Player.p_experience += floor(Curmonster.m_experience / 10.0); 301 Curmonster.m_experience *= 0.92; 302 /* monster gets meaner */ 303 Curmonster.m_maxspeed += 2.0; 304 Curmonster.m_speed = (Curmonster.m_speed < 0.0) ? 0.0 : Curmonster.m_speed + 2.0; 305 if (Curmonster.m_type == SM_DARKLORD) 306 /* Dark Lord; doesn't like to be nicked */ 307 { 308 mvprintw(Lines++, 0, 309 "You hit %s %.0f times, and made him mad!", Enemyname, inflict); 310 Player.p_quickness /= 2.0; 311 altercoordinates(0.0, 0.0, A_FAR); 312 cancelmonster(); 313 } else 314 hitmonster(inflict); 315 break; 316 317 case 'B': 318 case '6': /* luckout */ 319 if (Luckout) 320 mvaddstr(Lines++, 0, "You already tried that."); 321 else { 322 Luckout = TRUE; 323 if (Curmonster.m_type == SM_MORGOTH) 324 /* Morgoth; ally */ 325 { 326 if (drandom() < Player.p_sin / 100.0) { 327 mvprintw(Lines++, 0, "%s accepted!", Enemyname); 328 cancelmonster(); 329 } else 330 mvaddstr(Lines++, 0, "Nope, he's not interested."); 331 } else 332 /* normal monster; use brains for success */ 333 { 334 if ((drandom() + 0.333) * Player.p_brains 335 < (drandom() + 0.333) * Curmonster.m_brains) 336 mvprintw(Lines++, 0, "You blew it, %s.", Player.p_name); 337 else { 338 mvaddstr(Lines++, 0, "You made it!"); 339 Curmonster.m_energy = 0.0; 340 } 341 } 342 } 343 break; 344 345 case '7': /* use ring */ 346 if (Player.p_ring.ring_type != R_NONE) { 347 mvaddstr(Lines++, 0, "Now using ring."); 348 Player.p_ring.ring_inuse = TRUE; 349 if (Player.p_ring.ring_type != R_DLREG) 350 /* age ring */ 351 --Player.p_ring.ring_duration; 352 } 353 break; 354 } 355 356} 357 358static void 359monsthits(void) 360{ 361 double inflict; /* damage inflicted */ 362 int ch; /* input */ 363 364 switch (Curmonster.m_type) 365 /* may be a special monster */ 366 { 367 case SM_DARKLORD: 368 /* hits just enough to kill player */ 369 inflict = (Player.p_energy + Shield) * 1.02; 370 goto SPECIALHIT; 371 372 case SM_SHRIEKER: 373 /* call a big monster */ 374 mvaddstr(Lines++, 0, 375 "Shrieeeek!! You scared it, and it called one of its friends."); 376 more(Lines); 377 Whichmonster = (int) ROLL(70.0, 30.0); 378 longjmp(Fightenv, 0); 379 /* NOTREACHED */ 380 381 case SM_BALROG: 382 /* take experience away */ 383 inflict = ROLL(10.0, Curmonster.m_strength); 384 inflict = MIN(Player.p_experience, inflict); 385 mvprintw(Lines++, 0, 386 "%s took away %.0f experience points.", Enemyname, inflict); 387 Player.p_experience -= inflict; 388 return; 389 390 case SM_FAERIES: 391 if (Player.p_holywater > 0) 392 /* holy water kills when monster tries to hit */ 393 { 394 mvprintw(Lines++, 0, "Your holy water killed it!"); 395 --Player.p_holywater; 396 Curmonster.m_energy = 0.0; 397 return; 398 } 399 break; 400 401 case SM_NONE: 402 /* normal hit */ 403 break; 404 405 default: 406 if (drandom() > 0.2) 407 /* normal hit */ 408 break; 409 410 /* else special things */ 411 switch (Curmonster.m_type) { 412 case SM_LEANAN: 413 /* takes some of the player's strength */ 414 inflict = ROLL(1.0, (Circle - 1.0) / 2.0); 415 inflict = MIN(Player.p_strength, inflict); 416 mvprintw(Lines++, 0, "%s sapped %.0f of your strength!", 417 Enemyname, inflict); 418 Player.p_strength -= inflict; 419 Player.p_might -= inflict; 420 break; 421 422 case SM_SARUMAN: 423 if (Player.p_palantir) 424 /* take away palantir */ 425 { 426 mvprintw(Lines++, 0, "Wormtongue stole your palantir!"); 427 Player.p_palantir = FALSE; 428 } else 429 if (drandom() > 0.5) 430 /* gems turn to gold */ 431 { 432 mvprintw(Lines++, 0, 433 "%s transformed your gems into gold!", Enemyname); 434 Player.p_gold += Player.p_gems; 435 Player.p_gems = 0.0; 436 } else 437 /* scramble some stats */ 438 { 439 mvprintw(Lines++, 0, "%s scrambled your stats!", Enemyname); 440 scramblestats(); 441 } 442 break; 443 444 case SM_THAUMATURG: 445 /* transport player */ 446 mvprintw(Lines++, 0, "%s transported you!", Enemyname); 447 altercoordinates(0.0, 0.0, A_FAR); 448 cancelmonster(); 449 break; 450 451 case SM_VORTEX: 452 /* suck up some mana */ 453 inflict = ROLL(0, 7.5 * Circle); 454 inflict = MIN(Player.p_mana, floor(inflict)); 455 mvprintw(Lines++, 0, 456 "%s sucked up %.0f of your mana!", Enemyname, inflict); 457 Player.p_mana -= inflict; 458 break; 459 460 case SM_NAZGUL: 461 /* try to take ring if player has one */ 462 if (Player.p_ring.ring_type != R_NONE) 463 /* player has a ring */ 464 { 465 mvaddstr(Lines++, 0, "Will you relinguish your ring ? "); 466 ch = getanswer("YN", FALSE); 467 if (ch == 'Y') 468 /* take ring away */ 469 { 470 Player.p_ring.ring_type = R_NONE; 471 Player.p_ring.ring_inuse = FALSE; 472 cancelmonster(); 473 break; 474 } 475 } 476 /* otherwise, take some brains */ 477 mvprintw(Lines++, 0, 478 "%s neutralized 1/5 of your brain!", Enemyname); 479 Player.p_brains *= 0.8; 480 break; 481 482 case SM_TIAMAT: 483 /* take some gold and gems */ 484 mvprintw(Lines++, 0, 485 "%s took half your gold and gems and flew off.", Enemyname); 486 Player.p_gold /= 2.0; 487 Player.p_gems /= 2.0; 488 cancelmonster(); 489 break; 490 491 case SM_KOBOLD: 492 /* steal a gold piece and run */ 493 mvprintw(Lines++, 0, 494 "%s stole one gold piece and ran away.", Enemyname); 495 Player.p_gold = MAX(0.0, Player.p_gold - 1.0); 496 cancelmonster(); 497 break; 498 499 case SM_SHELOB: 500 /* bite and (medium) poison */ 501 mvprintw(Lines++, 0, 502 "%s has bitten and poisoned you!", Enemyname); 503 Player.p_poison -= 1.0; 504 break; 505 506 case SM_LAMPREY: 507 /* bite and (small) poison */ 508 mvprintw(Lines++, 0, "%s bit and poisoned you!", Enemyname); 509 Player.p_poison += 0.25; 510 break; 511 512 case SM_BONNACON: 513 /* fart and run */ 514 mvprintw(Lines++, 0, "%s farted and scampered off.", Enemyname); 515 Player.p_energy /= 2.0; /* damage from fumes */ 516 cancelmonster(); 517 break; 518 519 case SM_SMEAGOL: 520 if (Player.p_ring.ring_type != R_NONE) 521 /* try to steal ring */ 522 { 523 mvprintw(Lines++, 0, 524 "%s tried to steal your ring, ", Enemyname); 525 if (drandom() > 0.1) 526 addstr("but was unsuccessful."); 527 else { 528 addstr("and ran away with it!"); 529 Player.p_ring.ring_type = R_NONE; 530 cancelmonster(); 531 } 532 } 533 break; 534 535 case SM_SUCCUBUS: 536 /* inflict damage through shield */ 537 inflict = ROLL(15.0, Circle * 10.0); 538 inflict = MIN(inflict, Player.p_energy); 539 mvprintw(Lines++, 0, "%s sapped %.0f of your energy.", 540 Enemyname, inflict); 541 Player.p_energy -= inflict; 542 break; 543 544 case SM_CERBERUS: 545 /* take all metal treasures */ 546 mvprintw(Lines++, 0, 547 "%s took all your metal treasures!", Enemyname); 548 Player.p_crowns = 0; 549 Player.p_sword = 550 Player.p_shield = 551 Player.p_gold = 0.0; 552 cancelmonster(); 553 break; 554 555 case SM_UNGOLIANT: 556 /* (large) poison and take a quickness */ 557 mvprintw(Lines++, 0, 558 "%s poisoned you, and took one quik.", Enemyname); 559 Player.p_poison += 5.0; 560 Player.p_quickness -= 1.0; 561 break; 562 563 case SM_JABBERWOCK: 564 /* fly away, and leave either a Jubjub bird or 565 * Bonnacon */ 566 mvprintw(Lines++, 0, 567 "%s flew away, and left you to contend with one of its friends.", 568 Enemyname); 569 Whichmonster = 55 + ((drandom() > 0.5) ? 22 : 0); 570 longjmp(Fightenv, 0); 571 /* NOTREACHED */ 572 573 case SM_TROLL: 574 /* partially regenerate monster */ 575 mvprintw(Lines++, 0, 576 "%s partially regenerated his energy.!", Enemyname); 577 Curmonster.m_energy += 578 floor((Curmonster.m_o_energy - Curmonster.m_energy) / 2.0); 579 Curmonster.m_strength = Curmonster.m_o_strength; 580 Curmonster.m_melee = Curmonster.m_skirmish = 0.0; 581 Curmonster.m_maxspeed = Curmonster.m_o_speed; 582 break; 583 584 case SM_WRAITH: 585 if (!Player.p_blindness) 586 /* make blind */ 587 { 588 mvprintw(Lines++, 0, "%s blinded you!", Enemyname); 589 Player.p_blindness = TRUE; 590 Enemyname = "A monster"; 591 } 592 break; 593 } 594 return; 595 } 596 597 /* fall through to here if monster inflicts a normal hit */ 598 inflict = drandom() * Curmonster.m_strength + 0.5; 599SPECIALHIT: 600 mvprintw(Lines++, 0, "%s hit you %.0f times!", Enemyname, inflict); 601 602 if ((Shield -= inflict) < 0) { 603 Player.p_energy += Shield; 604 Shield = 0.0; 605 } 606} 607 608static void 609cancelmonster(void) 610{ 611 Curmonster.m_energy = 0.0; 612 Curmonster.m_experience = 0.0; 613 Curmonster.m_treasuretype = 0; 614 Curmonster.m_flock = 0.0; 615} 616 617static void 618hitmonster(double inflict) 619{ 620 mvprintw(Lines++, 0, "You hit %s %.0f times!", Enemyname, inflict); 621 Curmonster.m_energy -= inflict; 622 if (Curmonster.m_energy > 0.0) { 623 if (Curmonster.m_type == SM_DARKLORD || Curmonster.m_type == SM_SHRIEKER) 624 /* special monster didn't die */ 625 monsthits(); 626 } else 627 /* monster died. print message. */ 628 { 629 if (Curmonster.m_type == SM_MORGOTH) 630 mvaddstr(Lines++, 0, "You have defeated Morgoth, but he may return. . ."); 631 else 632 /* all other types of monsters */ 633 { 634 mvprintw(Lines++, 0, "You killed it. Good work, %s.", Player.p_name); 635 636 if (Curmonster.m_type == SM_MIMIC 637 && strcmp(Curmonster.m_name, "A Mimic") != 0 638 && !Player.p_blindness) 639 mvaddstr(Lines++, 0, "The body slowly changes into the form of a mimic."); 640 } 641 } 642} 643 644static void 645throwspell(void) 646{ 647 double inflict; /* damage inflicted */ 648 double dtemp; /* for dtemporary calculations */ 649 int ch; /* input */ 650 651 inflict = 0; 652 mvaddstr(7, 0, "\n\n"); /* clear menu area */ 653 654 if (Player.p_magiclvl >= ML_ALLORNOTHING) 655 mvaddstr(7, 0, "1:All or Nothing "); 656 if (Player.p_magiclvl >= ML_MAGICBOLT) 657 addstr("2:Magic Bolt "); 658 if (Player.p_magiclvl >= ML_FORCEFIELD) 659 addstr("3:Force Field "); 660 if (Player.p_magiclvl >= ML_XFORM) 661 addstr("4:Transform "); 662 if (Player.p_magiclvl >= ML_INCRMIGHT) 663 addstr("5:Increase Might\n"); 664 if (Player.p_magiclvl >= ML_INVISIBLE) 665 mvaddstr(8, 0, "6:Invisibility "); 666 if (Player.p_magiclvl >= ML_XPORT) 667 addstr("7:Transport "); 668 if (Player.p_magiclvl >= ML_PARALYZE) 669 addstr("8:Paralyze "); 670 if (Player.p_specialtype >= SC_COUNCIL) 671 addstr("9:Specify"); 672 mvaddstr(4, 0, "Spell ? "); 673 674 ch = getanswer(" ", TRUE); 675 676 mvaddstr(7, 0, "\n\n"); /* clear menu area */ 677 678 if (Curmonster.m_type == SM_MORGOTH && ch != '3') 679 /* can only throw force field against Morgoth */ 680 ILLSPELL(); 681 else 682 switch (ch) { 683 case '1': /* all or nothing */ 684 if (drandom() < 0.25) 685 /* success */ 686 { 687 inflict = Curmonster.m_energy * 1.01 + 1.0; 688 689 if (Curmonster.m_type == SM_DARKLORD) 690 /* all or nothing doesn't quite work 691 * against D. L. */ 692 inflict *= 0.9; 693 } else 694 /* failure -- monster gets stronger and 695 * quicker */ 696 { 697 Curmonster.m_o_strength = Curmonster.m_strength *= 2.0; 698 Curmonster.m_maxspeed *= 2.0; 699 Curmonster.m_o_speed *= 2.0; 700 701 /* paralyzed monsters wake up a bit */ 702 Curmonster.m_speed = MAX(1.0, Curmonster.m_speed * 2.0); 703 } 704 705 if (Player.p_mana >= MM_ALLORNOTHING) 706 /* take a mana if player has one */ 707 Player.p_mana -= MM_ALLORNOTHING; 708 709 hitmonster(inflict); 710 break; 711 712 case '2': /* magic bolt */ 713 if (Player.p_magiclvl < ML_MAGICBOLT) 714 ILLSPELL(); 715 else { 716 do 717 /* prompt for amount to expend */ 718 { 719 mvaddstr(4, 0, "How much mana for bolt? "); 720 dtemp = floor(infloat()); 721 } 722 while (dtemp < 0.0 || dtemp > Player.p_mana); 723 724 Player.p_mana -= dtemp; 725 726 if (Curmonster.m_type == SM_DARKLORD) 727 /* magic bolts don't work against D. 728 * L. */ 729 inflict = 0.0; 730 else 731 inflict = dtemp * ROLL(15.0, sqrt(Player.p_magiclvl / 3.0 + 1.0)); 732 mvaddstr(5, 0, "Magic Bolt fired!\n"); 733 hitmonster(inflict); 734 } 735 break; 736 737 case '3': /* force field */ 738 if (Player.p_magiclvl < ML_FORCEFIELD) 739 ILLSPELL(); 740 else 741 if (Player.p_mana < MM_FORCEFIELD) 742 NOMANA(); 743 else { 744 Player.p_mana -= MM_FORCEFIELD; 745 Shield = (Player.p_maxenergy + Player.p_shield) * 4.2 + 45.0; 746 mvaddstr(5, 0, "Force Field up.\n"); 747 } 748 break; 749 750 case '4': /* transform */ 751 if (Player.p_magiclvl < ML_XFORM) 752 ILLSPELL(); 753 else 754 if (Player.p_mana < MM_XFORM) 755 NOMANA(); 756 else { 757 Player.p_mana -= MM_XFORM; 758 Whichmonster = (int) ROLL(0.0, 100.0); 759 longjmp(Fightenv, 0); 760 /* NOTREACHED */ 761 } 762 break; 763 764 case '5': /* increase might */ 765 if (Player.p_magiclvl < ML_INCRMIGHT) 766 ILLSPELL(); 767 else 768 if (Player.p_mana < MM_INCRMIGHT) 769 NOMANA(); 770 else { 771 Player.p_mana -= MM_INCRMIGHT; 772 Player.p_might += 773 (1.2 * (Player.p_strength + Player.p_sword) 774 + 5.0 - Player.p_might) / 2.0; 775 mvprintw(5, 0, "New strength: %.0f\n", Player.p_might); 776 } 777 break; 778 779 case '6': /* invisible */ 780 if (Player.p_magiclvl < ML_INVISIBLE) 781 ILLSPELL(); 782 else 783 if (Player.p_mana < MM_INVISIBLE) 784 NOMANA(); 785 else { 786 Player.p_mana -= MM_INVISIBLE; 787 Player.p_speed += 788 (1.2 * (Player.p_quickness + Player.p_quksilver) 789 + 5.0 - Player.p_speed) / 2.0; 790 mvprintw(5, 0, "New quickness: %.0f\n", Player.p_speed); 791 } 792 break; 793 794 case '7': /* transport */ 795 if (Player.p_magiclvl < ML_XPORT) 796 ILLSPELL(); 797 else 798 if (Player.p_mana < MM_XPORT) 799 NOMANA(); 800 else { 801 Player.p_mana -= MM_XPORT; 802 if (Player.p_brains + Player.p_magiclvl 803 < Curmonster.m_experience / 200.0 * drandom()) { 804 mvaddstr(5, 0, "Transport backfired!\n"); 805 altercoordinates(0.0, 0.0, A_FAR); 806 cancelmonster(); 807 } else { 808 mvprintw(5, 0, "%s is transported.\n", Enemyname); 809 if (drandom() < 0.3) 810 /* monster didn't drop 811 * its treasure */ 812 Curmonster.m_treasuretype = 0; 813 814 Curmonster.m_energy = 0.0; 815 } 816 } 817 break; 818 819 case '8': /* paralyze */ 820 if (Player.p_magiclvl < ML_PARALYZE) 821 ILLSPELL(); 822 else 823 if (Player.p_mana < MM_PARALYZE) 824 NOMANA(); 825 else { 826 Player.p_mana -= MM_PARALYZE; 827 if (Player.p_magiclvl > 828 Curmonster.m_experience / 1000.0 * drandom()) { 829 mvprintw(5, 0, "%s is held.\n", Enemyname); 830 Curmonster.m_speed = -2.0; 831 } else 832 mvaddstr(5, 0, "Monster unaffected.\n"); 833 } 834 break; 835 836 case '9': /* specify */ 837 if (Player.p_specialtype < SC_COUNCIL) 838 ILLSPELL(); 839 else 840 if (Player.p_mana < MM_SPECIFY) 841 NOMANA(); 842 else { 843 Player.p_mana -= MM_SPECIFY; 844 mvaddstr(5, 0, "Which monster do you want [0-99] ? "); 845 Whichmonster = (int) infloat(); 846 Whichmonster = MAX(0, MIN(99, Whichmonster)); 847 longjmp(Fightenv, 0); 848 /* NOTREACHED */ 849 } 850 break; 851 } 852} 853 854static void 855callmonster(int which) 856{ 857 struct monster Othermonster; /* to find a name for mimics */ 858 859 which = MIN(which, 99); /* make sure within range */ 860 861 /* fill structure */ 862 fseek(Monstfp, (long) which * (long) SZ_MONSTERSTRUCT, SEEK_SET); 863 fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp); 864 865 /* handle some special monsters */ 866 if (Curmonster.m_type == SM_MODNAR) { 867 if (Player.p_specialtype < SC_COUNCIL) 868 /* randomize some stats */ 869 { 870 Curmonster.m_strength *= drandom() + 0.5; 871 Curmonster.m_brains *= drandom() + 0.5; 872 Curmonster.m_speed *= drandom() + 0.5; 873 Curmonster.m_energy *= drandom() + 0.5; 874 Curmonster.m_experience *= drandom() + 0.5; 875 Curmonster.m_treasuretype = 876 (int) ROLL(0.0, (double) Curmonster.m_treasuretype); 877 } else 878 /* make Modnar into Morgoth */ 879 { 880 strcpy(Curmonster.m_name, "Morgoth"); 881 Curmonster.m_strength = drandom() * (Player.p_maxenergy + Player.p_shield) / 1.4 882 + drandom() * (Player.p_maxenergy + Player.p_shield) / 1.5; 883 Curmonster.m_brains = Player.p_brains; 884 Curmonster.m_energy = Player.p_might * 30.0; 885 Curmonster.m_type = SM_MORGOTH; 886 Curmonster.m_speed = Player.p_speed * 1.1 887 + ((Player.p_specialtype == SC_EXVALAR) ? Player.p_speed : 0.0); 888 Curmonster.m_flock = 0.0; 889 Curmonster.m_treasuretype = 0; 890 Curmonster.m_experience = 0.0; 891 } 892 } else 893 if (Curmonster.m_type == SM_MIMIC) 894 /* pick another name */ 895 { 896 which = (int) ROLL(0.0, 100.0); 897 fseek(Monstfp, (long) which * (long) SZ_MONSTERSTRUCT, SEEK_SET); 898 fread(&Othermonster, SZ_MONSTERSTRUCT, 1, Monstfp); 899 strcpy(Curmonster.m_name, Othermonster.m_name); 900 } 901 truncstring(Curmonster.m_name); 902 903 if (Curmonster.m_type != SM_MORGOTH) 904 /* adjust stats based on which circle player is in */ 905 { 906 Curmonster.m_strength *= (1.0 + Circle / 2.0); 907 Curmonster.m_brains *= Circle; 908 Curmonster.m_speed += Circle * 1.e-9; 909 Curmonster.m_energy *= Circle; 910 Curmonster.m_experience *= Circle; 911 } 912 if (Player.p_blindness) 913 /* cannot see monster if blind */ 914 Enemyname = "A monster"; 915 else 916 Enemyname = Curmonster.m_name; 917 918 if (Player.p_speed <= 0.0) 919 /* make Player.p_speed positive */ 920 { 921 Curmonster.m_speed += -Player.p_speed; 922 Player.p_speed = 1.0; 923 } 924 /* fill up the rest of the structure */ 925 Curmonster.m_o_strength = Curmonster.m_strength; 926 Curmonster.m_o_speed = Curmonster.m_maxspeed = Curmonster.m_speed; 927 Curmonster.m_o_energy = Curmonster.m_energy; 928 Curmonster.m_melee = Curmonster.m_skirmish = 0.0; 929} 930 931static void 932awardtreasure(void) 933{ 934 int whichtreasure; /* calculated treasure to grant */ 935 int temp; /* temporary */ 936 int ch; /* input */ 937 double treasuretype; /* monster's treasure type */ 938 double gold = 0.0; /* gold awarded */ 939 double gems = 0.0; /* gems awarded */ 940 double dtemp; /* for temporary calculations */ 941 942 whichtreasure = (int) ROLL(1.0, 3.0); /* pick a treasure */ 943 treasuretype = (double) Curmonster.m_treasuretype; 944 945 move(4, 0); 946 clrtobot(); 947 move(6, 0); 948 949 if (drandom() > 0.65) 950 /* gold and gems */ 951 { 952 if (Curmonster.m_treasuretype > 7) 953 /* gems */ 954 { 955 gems = ROLL(1.0, (treasuretype - 7.0) 956 * (treasuretype - 7.0) * (Circle - 1.0) / 4.0); 957 printw("You have discovered %.0f gems!", gems); 958 } else 959 /* gold */ 960 { 961 gold = ROLL(treasuretype * 10.0, treasuretype 962 * treasuretype * 10.0 * (Circle - 1.0)); 963 printw("You have found %.0f gold pieces.", gold); 964 } 965 966 addstr(" Do you want to pick them up ? "); 967 ch = getanswer("NY", FALSE); 968 addstr("\n\n"); 969 970 if (ch == 'Y') { 971 if (drandom() < treasuretype / 35.0 + 0.04) 972 /* cursed */ 973 { 974 addstr("They were cursed!\n"); 975 cursedtreasure(); 976 } else 977 collecttaxes(gold, gems); 978 } 979 980 return; 981 } else 982 /* other treasures */ 983 { 984 addstr("You have found some treasure. Do you want to inspect it ? "); 985 ch = getanswer("NY", FALSE); 986 addstr("\n\n"); 987 988 if (ch != 'Y') 989 return; 990 else 991 if (drandom() < 0.08 && Curmonster.m_treasuretype != 4) { 992 addstr("It was cursed!\n"); 993 cursedtreasure(); 994 return; 995 } else 996 switch (Curmonster.m_treasuretype) { 997 case 1: /* treasure type 1 */ 998 switch (whichtreasure) { 999 case 1: 1000 addstr("You've discovered a power booster!\n"); 1001 Player.p_mana += ROLL(Circle * 4.0, Circle * 30.0); 1002 break; 1003 1004 case 2: 1005 addstr("You have encountered a druid.\n"); 1006 Player.p_experience += 1007 ROLL(0.0, 2000.0 + Circle * 400.0); 1008 break; 1009 1010 case 3: 1011 addstr("You have found a holy orb.\n"); 1012 Player.p_sin = MAX(0.0, Player.p_sin - 0.25); 1013 break; 1014 } 1015 break; 1016 /* end treasure type 1 */ 1017 1018 case 2: /* treasure type 2 */ 1019 switch (whichtreasure) { 1020 case 1: 1021 addstr("You have found an amulet.\n"); 1022 ++Player.p_amulets; 1023 break; 1024 1025 case 2: 1026 addstr("You've found some holy water!\n"); 1027 ++Player.p_holywater; 1028 break; 1029 1030 case 3: 1031 addstr("You've met a hermit!\n"); 1032 Player.p_sin *= 0.75; 1033 Player.p_mana += 12.0 * Circle; 1034 break; 1035 } 1036 break; 1037 /* end treasure type 2 */ 1038 1039 case 3: /* treasure type 3 */ 1040 switch (whichtreasure) { 1041 case 1: 1042 dtemp = ROLL(7.0, 30.0 + Circle / 10.0); 1043 printw("You've found a +%.0f shield!\n", dtemp); 1044 if (dtemp >= Player.p_shield) 1045 Player.p_shield = dtemp; 1046 else 1047 SOMEBETTER(); 1048 break; 1049 1050 case 2: 1051 addstr("You have rescued a virgin. Will you be honorable ? "); 1052 ch = getanswer("NY", FALSE); 1053 addstr("\n\n"); 1054 if (ch == 'Y') 1055 Player.p_virgin = TRUE; 1056 else { 1057 Player.p_experience += 2000.0 * Circle; 1058 ++Player.p_sin; 1059 } 1060 break; 1061 1062 case 3: 1063 addstr("You've discovered some athelas!\n"); 1064 --Player.p_poison; 1065 break; 1066 } 1067 break; 1068 /* end treasure type 3 */ 1069 1070 case 4: /* treasure type 4 */ 1071 addstr("You've found a scroll. Will you read it ? "); 1072 ch = getanswer("NY", FALSE); 1073 addstr("\n\n"); 1074 1075 if (ch == 'Y') 1076 switch ((int) ROLL(1, 6)) { 1077 case 1: 1078 addstr("It throws up a shield for you next monster.\n"); 1079 getyx(stdscr, whichtreasure, ch); 1080 more(whichtreasure); 1081 Shield = 1082 (Player.p_maxenergy + Player.p_energy) * 5.5 + Circle * 50.0; 1083 Whichmonster = pickmonster(); 1084 longjmp(Fightenv, 0); 1085 /* NOTREACHED */ 1086 1087 case 2: 1088 addstr("It makes you invisible for you next monster.\n"); 1089 getyx(stdscr, whichtreasure, ch); 1090 more(whichtreasure); 1091 Player.p_speed = 1e6; 1092 Whichmonster = pickmonster(); 1093 longjmp(Fightenv, 0); 1094 /* NOTREACHED */ 1095 1096 case 3: 1097 addstr("It increases your strength ten fold to fight your next monster.\n"); 1098 getyx(stdscr, whichtreasure, ch); 1099 more(whichtreasure); 1100 Player.p_might *= 10.0; 1101 Whichmonster = pickmonster(); 1102 longjmp(Fightenv, 0); 1103 /* NOTREACHED */ 1104 1105 case 4: 1106 addstr("It is a general knowledge scroll.\n"); 1107 Player.p_brains += ROLL(2.0, Circle); 1108 Player.p_magiclvl += ROLL(1.0, Circle / 2.0); 1109 break; 1110 1111 case 5: 1112 addstr("It tells you how to pick your next monster.\n"); 1113 addstr("Which monster do you want [0-99] ? "); 1114 Whichmonster = (int) infloat(); 1115 Whichmonster = MIN(99, MAX(0, Whichmonster)); 1116 longjmp(Fightenv, 0); 1117 1118 case 6: 1119 addstr("It was cursed!\n"); 1120 cursedtreasure(); 1121 break; 1122 } 1123 break; 1124 /* end treasure type 4 */ 1125 1126 case 5: /* treasure type 5 */ 1127 switch (whichtreasure) { 1128 case 1: 1129 dtemp = ROLL(Circle / 4.0 + 5.0, Circle / 2.0 + 9.0); 1130 printw("You've discovered a +%.0f dagger.\n", dtemp); 1131 if (dtemp >= Player.p_sword) 1132 Player.p_sword = dtemp; 1133 else 1134 SOMEBETTER(); 1135 break; 1136 1137 case 2: 1138 dtemp = ROLL(7.5 + Circle * 3.0, Circle * 2.0 + 160.0); 1139 printw("You have found some +%.0f armour!\n", dtemp); 1140 if (dtemp >= Player.p_shield) 1141 Player.p_shield = dtemp; 1142 else 1143 SOMEBETTER(); 1144 break; 1145 1146 case 3: 1147 addstr("You've found a tablet.\n"); 1148 Player.p_brains += 4.5 * Circle; 1149 break; 1150 } 1151 break; 1152 /* end treasure type 5 */ 1153 1154 case 6: /* treasure type 6 */ 1155 switch (whichtreasure) { 1156 case 1: 1157 addstr("You've found a priest.\n"); 1158 Player.p_energy = Player.p_maxenergy + Player.p_shield; 1159 Player.p_sin /= 2.0; 1160 Player.p_mana += 24.0 * Circle; 1161 Player.p_brains += Circle; 1162 break; 1163 1164 case 2: 1165 addstr("You have come upon Robin Hood!\n"); 1166 Player.p_shield += Circle * 2.0; 1167 Player.p_strength += Circle / 2.5 + 1.0; 1168 break; 1169 1170 case 3: 1171 dtemp = ROLL(2.0 + Circle / 4.0, Circle / 1.2 + 10.0); 1172 printw("You have found a +%.0f axe!\n", dtemp); 1173 if (dtemp >= Player.p_sword) 1174 Player.p_sword = dtemp; 1175 else 1176 SOMEBETTER(); 1177 break; 1178 } 1179 break; 1180 /* end treasure type 6 */ 1181 1182 case 7: /* treasure type 7 */ 1183 switch (whichtreasure) { 1184 case 1: 1185 addstr("You've discovered a charm!\n"); 1186 ++Player.p_charms; 1187 break; 1188 1189 case 2: 1190 addstr("You have encountered Merlyn!\n"); 1191 Player.p_brains += Circle + 5.0; 1192 Player.p_magiclvl += Circle / 3.0 + 5.0; 1193 Player.p_mana += Circle * 10.0; 1194 break; 1195 1196 case 3: 1197 dtemp = ROLL(5.0 + Circle / 3.0, Circle / 1.5 + 20.0); 1198 printw("You have found a +%.0f war hammer!\n", dtemp); 1199 if (dtemp >= Player.p_sword) 1200 Player.p_sword = dtemp; 1201 else 1202 SOMEBETTER(); 1203 break; 1204 } 1205 break; 1206 /* end treasure type 7 */ 1207 1208 case 8: /* treasure type 8 */ 1209 switch (whichtreasure) { 1210 case 1: 1211 addstr("You have found a healing potion.\n"); 1212 Player.p_poison = MIN(-2.0, Player.p_poison - 2.0); 1213 break; 1214 1215 case 2: 1216 addstr("You have discovered a transporter. Do you wish to go anywhere ? "); 1217 ch = getanswer("NY", FALSE); 1218 addstr("\n\n"); 1219 if (ch == 'Y') { 1220 double x, y; 1221 1222 addstr("X Y Coordinates ? "); 1223 getstring(Databuf, SZ_DATABUF); 1224 sscanf(Databuf, "%lf %lf", &x, &y); 1225 altercoordinates(x, y, A_FORCED); 1226 } 1227 break; 1228 1229 case 3: 1230 dtemp = ROLL(10.0 + Circle / 1.2, Circle * 3.0 + 30.0); 1231 printw("You've found a +%.0f sword!\n", dtemp); 1232 if (dtemp >= Player.p_sword) 1233 Player.p_sword = dtemp; 1234 else 1235 SOMEBETTER(); 1236 break; 1237 } 1238 break; 1239 /* end treasure type 8 */ 1240 1241 case 10: 1242 case 11: 1243 case 12: 1244 case 13: /* treasure types 10 - 13 */ 1245 if (drandom() < 0.33) { 1246 if (Curmonster.m_treasuretype == 10) { 1247 addstr("You've found a pair of elven boots!\n"); 1248 Player.p_quickness += 2.0; 1249 break; 1250 } else 1251 if (Curmonster.m_treasuretype == 11 1252 && !Player.p_palantir) { 1253 addstr("You've acquired Saruman's palantir.\n"); 1254 Player.p_palantir = TRUE; 1255 break; 1256 } else 1257 if (Player.p_ring.ring_type == R_NONE 1258 && Player.p_specialtype < SC_COUNCIL 1259 && (Curmonster.m_treasuretype == 12 1260 || Curmonster.m_treasuretype == 13)) 1261 /* roll 1262 * up 1263 * a 1264 * ring 1265 * */ 1266 { 1267 if (drandom() < 0.8) 1268 /* r 1269 * e 1270 * g 1271 * u 1272 * l 1273 * a 1274 * r 1275 * 1276 * ri 1277 * n 1278 * g 1279 * s 1280 * */ 1281 { 1282 if (Curmonster.m_treasuretype == 12) { 1283 whichtreasure = R_NAZREG; 1284 temp = 35; 1285 } else { 1286 whichtreasure = R_DLREG; 1287 temp = 0; 1288 } 1289 } else 1290 /* b 1291 * a 1292 * d 1293 * 1294 * ri 1295 * n 1296 * g 1297 * s 1298 * */ 1299 { 1300 whichtreasure = R_BAD; 1301 temp = 15 + Statptr->c_ringduration + (int) ROLL(0, 5); 1302 } 1303 1304 addstr("You've discovered a ring. Will you pick it up ? "); 1305 ch = getanswer("NY", FALSE); 1306 addstr("\n\n"); 1307 1308 if (ch == 'Y') { 1309 Player.p_ring.ring_type = whichtreasure; 1310 Player.p_ring.ring_duration = temp; 1311 } 1312 break; 1313 } 1314 } 1315 /* end treasure types 10 - 13 */ 1316 /* fall through to treasure type 9 if 1317 * no treasure from above */ 1318 1319 case 9: /* treasure type 9 */ 1320 switch (whichtreasure) { 1321 case 1: 1322 if (Player.p_level <= 1000.0 1323 && Player.p_crowns <= 3 1324 && Player.p_level >= 10.0) { 1325 addstr("You have found a golden crown!\n"); 1326 ++Player.p_crowns; 1327 break; 1328 } 1329 /* fall through otherwise */ 1330 1331 case 2: 1332 addstr("You've been blessed!\n"); 1333 Player.p_blessing = TRUE; 1334 Player.p_sin /= 3.0; 1335 Player.p_energy = Player.p_maxenergy + Player.p_shield; 1336 Player.p_mana += 100.0 * Circle; 1337 break; 1338 1339 case 3: 1340 dtemp = ROLL(1.0, Circle / 5.0 + 5.0); 1341 dtemp = MIN(dtemp, 99.0); 1342 printw("You have discovered some +%.0f quicksilver!\n", dtemp); 1343 if (dtemp >= Player.p_quksilver) 1344 Player.p_quksilver = dtemp; 1345 else 1346 SOMEBETTER(); 1347 break; 1348 } 1349 break; 1350 /* end treasure type 9 */ 1351 } 1352 } 1353} 1354 1355static void 1356cursedtreasure(void) 1357{ 1358 if (Player.p_charms > 0) { 1359 addstr("But your charm saved you!\n"); 1360 --Player.p_charms; 1361 } else 1362 if (Player.p_amulets > 0) { 1363 addstr("But your amulet saved you!\n"); 1364 --Player.p_amulets; 1365 } else { 1366 Player.p_energy = 1367 (Player.p_maxenergy + Player.p_shield) / 10.0; 1368 Player.p_poison += 0.25; 1369 } 1370} 1371 1372static void 1373scramblestats(void) 1374{ 1375 double dbuf[6]; /* to put statistic in */ 1376 double dtemp1, dtemp2; /* for swapping values */ 1377 int first, second; /* indices for swapping */ 1378 double *dptr; /* pointer for filling and emptying buf[] */ 1379 1380 /* fill buffer */ 1381 dptr = &dbuf[0]; 1382 *dptr++ = Player.p_strength; 1383 *dptr++ = Player.p_mana; 1384 *dptr++ = Player.p_brains; 1385 *dptr++ = Player.p_magiclvl; 1386 *dptr++ = Player.p_energy; 1387 *dptr = Player.p_sin; 1388 1389 /* pick values to swap */ 1390 first = (int) ROLL(0, 5); 1391 second = (int) ROLL(0, 5); 1392 1393 /* swap values */ 1394 dptr = &dbuf[0]; 1395 dtemp1 = dptr[first]; 1396 /* this expression is split to prevent a compiler loop on some 1397 * compilers */ 1398 dtemp2 = dptr[second]; 1399 dptr[first] = dtemp2; 1400 dptr[second] = dtemp1; 1401 1402 /* empty buffer */ 1403 Player.p_strength = *dptr++; 1404 Player.p_mana = *dptr++; 1405 Player.p_brains = *dptr++; 1406 Player.p_magiclvl = *dptr++; 1407 Player.p_energy = *dptr++; 1408 Player.p_sin = *dptr; 1409} 1410