Annotation of early-roguelike/rogue4/fight.c, Revision 1.1
1.1 ! rubenllo 1: /*
! 2: * All the fighting gets done here
! 3: *
! 4: * @(#)fight.c 4.30 (Berkeley) 4/6/82
! 5: *
! 6: * Rogue: Exploring the Dungeons of Doom
! 7: * Copyright (C) 1980, 1981, 1982 Michael Toy, Ken Arnold and Glenn Wichman
! 8: * All rights reserved.
! 9: *
! 10: * See the file LICENSE.TXT for full copyright and licensing information.
! 11: */
! 12:
! 13: #include <stdlib.h>
! 14: #include <curses.h>
! 15: #include <ctype.h>
! 16: #include <string.h>
! 17: #include "rogue.h"
! 18:
! 19: long e_levels[] = {
! 20: 10L,20L,40L,80L,160L,320L,640L,1280L,2560L,5120L,10240L,20480L,
! 21: 40920L, 81920L, 163840L, 327680L, 655360L, 1310720L, 2621440L, 0L
! 22: };
! 23:
! 24: bool roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl);
! 25: void hit(const char *er, const char *ee);
! 26: void miss(const char *er, const char *ee);
! 27: int str_plus(str_t str);
! 28: int add_dam(str_t str);
! 29: void thunk(THING *weap, const char *mname);
! 30: void bounce(THING *weap, const char *mname);
! 31:
! 32: /*
! 33: * fight:
! 34: * The player attacks the monster.
! 35: */
! 36: bool
! 37: fight(coord *mp, char mn, THING *weap, bool thrown)
! 38: {
! 39: register THING *tp;
! 40: register bool did_hit = TRUE;
! 41: register const char *mname;
! 42:
! 43: /*
! 44: * Find the monster we want to fight
! 45: */
! 46: #ifdef WIZARD
! 47: if ((tp = moat(mp->y, mp->x)) == NULL)
! 48: debug("Fight what @ %d,%d", mp->y, mp->x);
! 49: #else
! 50: tp = moat(mp->y, mp->x);
! 51: #endif
! 52: /*
! 53: * Since we are fighting, things are not quiet so no healing takes
! 54: * place.
! 55: */
! 56: count = quiet = 0;
! 57: runto(mp, &hero);
! 58: /*
! 59: * Let him know it was really a mimic (if it was one).
! 60: */
! 61: if (tp->t_type == 'M' && tp->t_disguise != 'M' && !on(player, ISBLIND))
! 62: {
! 63: tp->t_disguise = 'M';
! 64: if (!thrown)
! 65: return FALSE;
! 66: msg("wait! That's a mimic!");
! 67: }
! 68: did_hit = FALSE;
! 69: if (on(player, ISBLIND))
! 70: mname = "it";
! 71: else
! 72: mname = monsters[mn-'A'].m_name;
! 73: if (roll_em(&player, tp, weap, thrown))
! 74: {
! 75: did_hit = FALSE;
! 76: if (thrown)
! 77: thunk(weap, mname);
! 78: else
! 79: hit(NULL, mname);
! 80: if (on(player, CANHUH))
! 81: {
! 82: did_hit = TRUE;
! 83: tp->t_flags |= ISHUH;
! 84: player.t_flags &= ~CANHUH;
! 85: msg("your hands stop glowing red");
! 86: }
! 87: if (tp->t_stats.s_hpt <= 0)
! 88: killed(tp, TRUE);
! 89: else if (did_hit && !on(player, ISBLIND))
! 90: msg("the %s appears confused", mname);
! 91: did_hit = TRUE;
! 92: }
! 93: else
! 94: if (thrown)
! 95: bounce(weap, mname);
! 96: else
! 97: miss(NULL, mname);
! 98: return did_hit;
! 99: }
! 100:
! 101: /*
! 102: * attack:
! 103: * The monster attacks the player
! 104: */
! 105: int
! 106: attack(THING *mp)
! 107: {
! 108: register const char *mname;
! 109:
! 110: /*
! 111: * Since this is an attack, stop running and any healing that was
! 112: * going on at the time.
! 113: */
! 114: running = FALSE;
! 115: count = quiet = 0;
! 116: if (mp->t_type == 'F')
! 117: fung_hit = atoi(mp->t_stats.s_dmg);
! 118: if (mp->t_type == 'M' && !on(player, ISBLIND))
! 119: mp->t_disguise = 'M';
! 120: if (on(player, ISBLIND))
! 121: mname = "it";
! 122: else
! 123: mname = monsters[mp->t_type-'A'].m_name;
! 124: if (roll_em(mp, &player, NULL, FALSE))
! 125: {
! 126: if (mp->t_type != 'E')
! 127: hit(mname, NULL);
! 128: if (pstats.s_hpt <= 0)
! 129: death(mp->t_type); /* Bye bye life ... */
! 130: if (!on(*mp, ISCANC))
! 131: switch (mp->t_type)
! 132: {
! 133: case 'R':
! 134: /*
! 135: * If a rust monster hits, you lose armor, unless
! 136: * that armor is leather or there is a magic ring
! 137: */
! 138: if (cur_armor != NULL && cur_armor->o_ac < 9
! 139: && cur_armor->o_which != LEATHER)
! 140: if (ISWEARING(R_SUSTARM))
! 141: msg("The rust vanishes instantly");
! 142: else
! 143: {
! 144: cur_armor->o_ac++;
! 145: if (!terse)
! 146: msg("your armor appears to be weaker now. Oh my!");
! 147: else
! 148: msg("your armor weakens");
! 149: }
! 150: when 'E':
! 151: /*
! 152: * The gaze of the floating eye hypnotizes you
! 153: */
! 154: if (on(player, ISBLIND))
! 155: break;
! 156: player.t_flags &= ~ISRUN;
! 157: if (!no_command)
! 158: {
! 159: addmsg("you are transfixed");
! 160: if (!terse)
! 161: addmsg(" by the gaze of the floating eye");
! 162: endmsg();
! 163: }
! 164: no_command += rnd(2) + 2;
! 165: when 'A':
! 166: /*
! 167: * Ants have poisonous bites
! 168: */
! 169: if (!save(VS_POISON))
! 170: if (!ISWEARING(R_SUSTSTR))
! 171: {
! 172: chg_str(-1);
! 173: if (!terse)
! 174: msg("you feel a sting in your arm and now feel weaker");
! 175: else
! 176: msg("a sting has weakened you");
! 177: }
! 178: else
! 179: if (!terse)
! 180: msg("a sting momentarily weakens you");
! 181: else
! 182: msg("sting has no effect");
! 183: when 'W':
! 184: case 'V':
! 185: /*
! 186: * Wraiths might drain energy levels, and Vampires
! 187: * can steal max_hp
! 188: */
! 189: if (rnd(100) < (mp->t_type == 'W' ? 15 : 30))
! 190: {
! 191: register int fewer;
! 192:
! 193: if (mp->t_type == 'W')
! 194: {
! 195: if (pstats.s_exp == 0)
! 196: death('W'); /* All levels gone */
! 197: if (--pstats.s_lvl == 0)
! 198: {
! 199: pstats.s_exp = 0;
! 200: pstats.s_lvl = 1;
! 201: }
! 202: else
! 203: pstats.s_exp = e_levels[pstats.s_lvl-1]+1;
! 204: fewer = roll(1, 10);
! 205: }
! 206: else
! 207: fewer = roll(1, 5);
! 208: pstats.s_hpt -= fewer;
! 209: max_hp -= fewer;
! 210: if (pstats.s_hpt < 1)
! 211: pstats.s_hpt = 1;
! 212: if (max_hp < 1)
! 213: death(mp->t_type);
! 214: msg("you suddenly feel weaker");
! 215: }
! 216: when 'F':
! 217: /*
! 218: * Violet fungi stops the poor guy from moving
! 219: */
! 220: player.t_flags |= ISHELD;
! 221: sprintf(mp->t_stats.s_dmg,"%dd1",++fung_hit);
! 222: when 'L':
! 223: {
! 224: /*
! 225: * Leperachaun steals some gold
! 226: */
! 227: register long lastpurse;
! 228:
! 229: lastpurse = purse;
! 230: purse -= GOLDCALC;
! 231: if (!save(VS_MAGIC))
! 232: purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC;
! 233: if (purse < 0)
! 234: purse = 0;
! 235: remove_monster(&mp->t_pos, mp, FALSE);
! 236: mp = NULL;
! 237: if (purse != lastpurse)
! 238: msg("your purse feels lighter");
! 239: }
! 240: when 'N':
! 241: {
! 242: register THING *obj, *steal;
! 243: register int nobj;
! 244:
! 245: /*
! 246: * Nymph's steal a magic item, look through the pack
! 247: * and pick out one we like.
! 248: */
! 249: steal = NULL;
! 250: for (nobj = 0, obj = pack; obj != NULL; obj = next(obj))
! 251: if (obj != cur_armor && obj != cur_weapon
! 252: && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT]
! 253: && is_magic(obj) && rnd(++nobj) == 0)
! 254: steal = obj;
! 255: if (steal != NULL)
! 256: {
! 257: remove_monster(&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE);
! 258: mp = NULL;
! 259: inpack--;
! 260: if (steal->o_count > 1 && steal->o_group == 0)
! 261: {
! 262: register int oc;
! 263:
! 264: oc = steal->o_count--;
! 265: steal->o_count = 1;
! 266: msg("she stole %s!", inv_name(steal, TRUE));
! 267: steal->o_count = oc;
! 268: }
! 269: else
! 270: {
! 271: detach(pack, steal);
! 272: msg("she stole %s!", inv_name(steal, TRUE));
! 273: discard(steal);
! 274: }
! 275: }
! 276: }
! 277: otherwise:
! 278: break;
! 279: }
! 280: }
! 281: else if (mp->t_type != 'E')
! 282: {
! 283: if (mp->t_type == 'F')
! 284: {
! 285: pstats.s_hpt -= fung_hit;
! 286: if (pstats.s_hpt <= 0)
! 287: death(mp->t_type); /* Bye bye life ... */
! 288: }
! 289: miss(mname, NULL);
! 290: }
! 291: if (fight_flush)
! 292: flush_type();
! 293: count = 0;
! 294: status();
! 295:
! 296: if (mp == NULL)
! 297: return(-1);
! 298: else
! 299: return(0);
! 300: }
! 301:
! 302: /*
! 303: * swing:
! 304: * Returns true if the swing hits
! 305: */
! 306: bool
! 307: swing(int at_lvl, int op_arm, int wplus)
! 308: {
! 309: register int res = rnd(20);
! 310: register int need = (20 - at_lvl) - op_arm;
! 311:
! 312: return (res + wplus >= need);
! 313: }
! 314:
! 315: /*
! 316: * check_level:
! 317: * Check to see if the guy has gone up a level.
! 318: */
! 319: void
! 320: check_level(void)
! 321: {
! 322: register int i, add, olevel;
! 323:
! 324: for (i = 0; e_levels[i] != 0; i++)
! 325: if (e_levels[i] > pstats.s_exp)
! 326: break;
! 327: i++;
! 328: olevel = pstats.s_lvl;
! 329: pstats.s_lvl = i;
! 330: if (i > olevel)
! 331: {
! 332: add = roll(i - olevel, 10);
! 333: max_hp += add;
! 334: if ((pstats.s_hpt += add) > max_hp)
! 335: pstats.s_hpt = max_hp;
! 336: msg("welcome to level %d", i);
! 337: }
! 338: }
! 339:
! 340: /*
! 341: * roll_em:
! 342: * Roll several attacks
! 343: */
! 344: bool
! 345: roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl)
! 346: {
! 347: register struct stats *att, *def;
! 348: register char *cp;
! 349: register int ndice, nsides, def_arm;
! 350: register bool did_hit = FALSE;
! 351: register int hplus;
! 352: register int dplus;
! 353: register int damage;
! 354:
! 355: att = &thatt->t_stats;
! 356: def = &thdef->t_stats;
! 357: if (weap == NULL)
! 358: {
! 359: cp = att->s_dmg;
! 360: dplus = 0;
! 361: hplus = 0;
! 362: }
! 363: else
! 364: {
! 365: hplus = (weap == NULL ? 0 : weap->o_hplus);
! 366: dplus = (weap == NULL ? 0 : weap->o_dplus);
! 367: if (weap == cur_weapon)
! 368: {
! 369: if (ISRING(LEFT, R_ADDDAM))
! 370: dplus += cur_ring[LEFT]->o_ac;
! 371: else if (ISRING(LEFT, R_ADDHIT))
! 372: hplus += cur_ring[LEFT]->o_ac;
! 373: if (ISRING(RIGHT, R_ADDDAM))
! 374: dplus += cur_ring[RIGHT]->o_ac;
! 375: else if (ISRING(RIGHT, R_ADDHIT))
! 376: hplus += cur_ring[RIGHT]->o_ac;
! 377: }
! 378: if (hurl)
! 379: if ((weap->o_flags&ISMISL) && cur_weapon != NULL &&
! 380: cur_weapon->o_which == weap->o_launch)
! 381: {
! 382: cp = weap->o_hurldmg;
! 383: hplus += cur_weapon->o_hplus;
! 384: dplus += cur_weapon->o_dplus;
! 385: }
! 386: else
! 387: cp = weap->o_hurldmg;
! 388: else
! 389: {
! 390: cp = weap->o_damage;
! 391: /*
! 392: * Drain a staff of striking
! 393: */
! 394: if (weap->o_type == STICK && weap->o_which == WS_HIT
! 395: && --weap->o_charges < 0)
! 396: {
! 397: strcpy(weap->o_damage,"0d0");
! 398: cp = weap->o_damage;
! 399: weap->o_hplus = weap->o_dplus = 0;
! 400: weap->o_charges = 0;
! 401: }
! 402: }
! 403: }
! 404: /*
! 405: * If the creature being attacked is not running (alseep or held)
! 406: * then the attacker gets a plus four bonus to hit.
! 407: */
! 408: if (!on(*thdef, ISRUN))
! 409: hplus += 4;
! 410: def_arm = def->s_arm;
! 411: if (def == &pstats)
! 412: {
! 413: if (cur_armor != NULL)
! 414: def_arm = cur_armor->o_ac;
! 415: if (ISRING(LEFT, R_PROTECT))
! 416: def_arm -= cur_ring[LEFT]->o_ac;
! 417: if (ISRING(RIGHT, R_PROTECT))
! 418: def_arm -= cur_ring[RIGHT]->o_ac;
! 419: }
! 420: for (;;)
! 421: {
! 422: ndice = atoi(cp);
! 423: if ((cp = strchr(cp, 'd')) == NULL)
! 424: break;
! 425: nsides = atoi(++cp);
! 426: if (swing(att->s_lvl, def_arm, hplus + str_plus(att->s_str)))
! 427: {
! 428: register int proll;
! 429:
! 430: proll = roll(ndice, nsides);
! 431: #ifdef WIZARD
! 432: if (ndice + nsides > 0 && proll < 1)
! 433: debug("Damage for %dd%d came out %d, dplus = %d, add_dam = %d, def_arm = %d", ndice, nsides, proll, dplus, add_dam(att->s_str), def_arm);
! 434: #endif
! 435: damage = dplus + proll + add_dam(att->s_str);
! 436: def->s_hpt -= max(0, damage);
! 437: did_hit = TRUE;
! 438: }
! 439: if ((cp = strchr(cp, '/')) == NULL)
! 440: break;
! 441: cp++;
! 442: }
! 443: return did_hit;
! 444: }
! 445:
! 446: /*
! 447: * prname:
! 448: * The print name of a combatant
! 449: */
! 450: char *
! 451: prname(const char *who, bool upper)
! 452: {
! 453: static char tbuf[MAXSTR];
! 454:
! 455: *tbuf = '\0';
! 456: if (who == 0)
! 457: strcpy(tbuf, "you");
! 458: else if (on(player, ISBLIND))
! 459: strcpy(tbuf, "it");
! 460: else
! 461: {
! 462: strcpy(tbuf, "the ");
! 463: strcat(tbuf, who);
! 464: }
! 465: if (upper)
! 466: *tbuf = toupper(*tbuf);
! 467: return tbuf;
! 468: }
! 469:
! 470: /*
! 471: * hit:
! 472: * Print a message to indicate a succesful hit
! 473: */
! 474: void
! 475: hit(const char *er, const char *ee)
! 476: {
! 477: register char *s = "";
! 478:
! 479: addmsg(prname(er, TRUE));
! 480: if (terse)
! 481: s = " hit";
! 482: else
! 483: switch (rnd(4))
! 484: {
! 485: case 0: s = " scored an excellent hit on ";
! 486: when 1: s = " hit ";
! 487: when 2: s = (er == 0 ? " have injured " : " has injured ");
! 488: when 3: s = (er == 0 ? " swing and hit " : " swings and hits ");
! 489: }
! 490: addmsg(s);
! 491: if (!terse)
! 492: addmsg(prname(ee, FALSE));
! 493: endmsg();
! 494: }
! 495:
! 496: /*
! 497: * miss:
! 498: * Print a message to indicate a poor swing
! 499: */
! 500: void
! 501: miss(const char *er, const char *ee)
! 502: {
! 503: register char *s = "";
! 504:
! 505: addmsg(prname(er, TRUE));
! 506: switch (terse ? 0 : rnd(4))
! 507: {
! 508: case 0: s = (er == 0 ? " miss" : " misses");
! 509: when 1: s = (er == 0 ? " swing and miss" : " swings and misses");
! 510: when 2: s = (er == 0 ? " barely miss" : " barely misses");
! 511: when 3: s = (er == 0 ? " don't hit" : " doesn't hit");
! 512: }
! 513: addmsg(s);
! 514: if (!terse)
! 515: addmsg(" %s", prname(ee, FALSE));
! 516: endmsg();
! 517: }
! 518:
! 519: /*
! 520: * save_throw:
! 521: * See if a creature save against something
! 522: */
! 523: bool
! 524: save_throw(int which, THING *tp)
! 525: {
! 526: register int need;
! 527:
! 528: need = 14 + which - tp->t_stats.s_lvl / 2;
! 529: return (roll(1, 20) >= need);
! 530: }
! 531:
! 532: /*
! 533: * save:
! 534: * See if he saves against various nasty things
! 535: */
! 536: bool
! 537: save(int which)
! 538: {
! 539: if (which == VS_MAGIC)
! 540: {
! 541: if (ISRING(LEFT, R_PROTECT))
! 542: which -= cur_ring[LEFT]->o_ac;
! 543: if (ISRING(RIGHT, R_PROTECT))
! 544: which -= cur_ring[RIGHT]->o_ac;
! 545: }
! 546: return save_throw(which, &player);
! 547: }
! 548:
! 549: /*
! 550: * str_plus:
! 551: * Compute bonus/penalties for strength on the "to hit" roll
! 552: */
! 553: int
! 554: str_plus(str_t str)
! 555: {
! 556: if (str == 31)
! 557: return 3;
! 558: if (str > 20)
! 559: return 2;
! 560: if (str > 16)
! 561: return 1;
! 562: if (str > 6)
! 563: return 0;
! 564: return str - 7;
! 565: }
! 566:
! 567: /*
! 568: * add_dam:
! 569: * Compute additional damage done for exceptionally high or low strength
! 570: */
! 571: int
! 572: add_dam(str_t str)
! 573: {
! 574: if (str == 31)
! 575: return 6;
! 576: if (str > 21)
! 577: return 5;
! 578: if (str == 21)
! 579: return 4;
! 580: if (str > 18)
! 581: return 3;
! 582: if (str == 18)
! 583: return 2;
! 584: if (str > 15)
! 585: return 1;
! 586: if (str > 6)
! 587: return 0;
! 588: return str - 7;
! 589: }
! 590:
! 591: /*
! 592: * raise_level:
! 593: * The guy just magically went up a level.
! 594: */
! 595: void
! 596: raise_level(void)
! 597: {
! 598: pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L;
! 599: check_level();
! 600: }
! 601:
! 602: /*
! 603: * thunk:
! 604: * A missile hits a monster
! 605: */
! 606: void
! 607: thunk(THING *weap, const char *mname)
! 608: {
! 609: if (weap->o_type == WEAPON)
! 610: addmsg("the %s hits ", w_names[weap->o_which]);
! 611: else
! 612: addmsg("you hit ");
! 613: if (on(player, ISBLIND))
! 614: msg("it");
! 615: else
! 616: msg("the %s", mname);
! 617: }
! 618:
! 619: /*
! 620: * bounce:
! 621: * A missile misses a monster
! 622: */
! 623: void
! 624: bounce(THING *weap, const char *mname)
! 625: {
! 626: if (weap->o_type == WEAPON)
! 627: addmsg("the %s misses ", w_names[weap->o_which]);
! 628: else
! 629: addmsg("you missed ");
! 630: if (on(player, ISBLIND))
! 631: msg("it");
! 632: else
! 633: msg("the %s", mname);
! 634: }
! 635:
! 636: /*
! 637: * remove:
! 638: * Remove a monster from the screen
! 639: */
! 640: void
! 641: remove_monster(coord *mp, THING *tp, bool waskill)
! 642: {
! 643: register THING *obj, *nexti;
! 644:
! 645: for (obj = tp->t_pack; obj != NULL; obj = nexti)
! 646: {
! 647: nexti = next(obj);
! 648: obj->o_pos = tp->t_pos;
! 649: detach(tp->t_pack, obj);
! 650: if (waskill)
! 651: fall(obj, FALSE);
! 652: else
! 653: discard(obj);
! 654: }
! 655: moat(mp->y, mp->x) = NULL;
! 656: mvaddch(mp->y, mp->x, tp->t_oldch);
! 657: detach(mlist, tp);
! 658: discard(tp);
! 659: }
! 660:
! 661: /*
! 662: * is_magic:
! 663: * Returns true if an object radiates magic
! 664: */
! 665: bool
! 666: is_magic(THING *obj)
! 667: {
! 668: switch (obj->o_type)
! 669: {
! 670: case ARMOR:
! 671: return obj->o_ac != a_class[obj->o_which];
! 672: case WEAPON:
! 673: return obj->o_hplus != 0 || obj->o_dplus != 0;
! 674: case POTION:
! 675: case SCROLL:
! 676: case STICK:
! 677: case RING:
! 678: case AMULET:
! 679: return TRUE;
! 680: }
! 681: return FALSE;
! 682: }
! 683:
! 684: /*
! 685: * killed:
! 686: * Called to put a monster to death
! 687: */
! 688: void
! 689: killed(THING *tp, bool pr)
! 690: {
! 691: pstats.s_exp += tp->t_stats.s_exp;
! 692: /*
! 693: * If the monster was a violet fungi, un-hold him
! 694: */
! 695: switch (tp->t_type)
! 696: {
! 697: case 'F':
! 698: player.t_flags &= ~ISHELD;
! 699: fung_hit = 0;
! 700: when 'L':
! 701: {
! 702: register THING *gold;
! 703:
! 704: if (fallpos(&tp->t_pos, &tp->t_room->r_gold, TRUE))
! 705: {
! 706: gold = new_item();
! 707: gold->o_type = GOLD;
! 708: gold->o_goldval = GOLDCALC;
! 709: if (save(VS_MAGIC))
! 710: gold->o_goldval += GOLDCALC + GOLDCALC
! 711: + GOLDCALC + GOLDCALC;
! 712: attach(tp->t_pack, gold);
! 713: }
! 714: }
! 715: }
! 716: /*
! 717: * Get rid of the monster.
! 718: */
! 719: if (pr)
! 720: {
! 721: if (!terse)
! 722: addmsg("you have ");
! 723: addmsg("defeated ");
! 724: if (on(player, ISBLIND))
! 725: msg("it");
! 726: else
! 727: {
! 728: if (!terse)
! 729: addmsg("the ");
! 730: msg("%s", monsters[tp->t_type-'A'].m_name);
! 731: }
! 732: }
! 733: remove_monster(&tp->t_pos, tp, TRUE);
! 734: /*
! 735: * Do adjustments if he went up a level
! 736: */
! 737: check_level();
! 738: }
CVSweb