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