/* * All the fighting gets done here * * @(#)fight.c 4.30 (Berkeley) 4/6/82 * * Rogue: Exploring the Dungeons of Doom * Copyright (C) 1980, 1981, 1982 Michael Toy, Ken Arnold and Glenn Wichman * All rights reserved. * * See the file LICENSE.TXT for full copyright and licensing information. */ #include #include #include #include #include "rogue.h" long e_levels[] = { 10L,20L,40L,80L,160L,320L,640L,1280L,2560L,5120L,10240L,20480L, 40920L, 81920L, 163840L, 327680L, 655360L, 1310720L, 2621440L, 0L }; bool roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl); void hit(const char *er, const char *ee); void miss(const char *er, const char *ee); int str_plus(str_t str); int add_dam(str_t str); void thunk(THING *weap, const char *mname); void bounce(THING *weap, const char *mname); /* * fight: * The player attacks the monster. */ bool fight(coord *mp, char mn, THING *weap, bool thrown) { register THING *tp; register bool did_hit = TRUE; register const char *mname; /* * Find the monster we want to fight */ #ifdef WIZARD if ((tp = moat(mp->y, mp->x)) == NULL) debug("Fight what @ %d,%d", mp->y, mp->x); #else tp = moat(mp->y, mp->x); #endif /* * Since we are fighting, things are not quiet so no healing takes * place. */ count = quiet = 0; runto(mp, &hero); /* * Let him know it was really a mimic (if it was one). */ if (tp->t_type == 'M' && tp->t_disguise != 'M' && !on(player, ISBLIND)) { tp->t_disguise = 'M'; if (!thrown) return FALSE; msg("wait! That's a mimic!"); } did_hit = FALSE; if (on(player, ISBLIND)) mname = "it"; else mname = monsters[mn-'A'].m_name; if (roll_em(&player, tp, weap, thrown)) { did_hit = FALSE; if (thrown) thunk(weap, mname); else hit(NULL, mname); if (on(player, CANHUH)) { did_hit = TRUE; tp->t_flags |= ISHUH; player.t_flags &= ~CANHUH; msg("your hands stop glowing red"); } if (tp->t_stats.s_hpt <= 0) killed(tp, TRUE); else if (did_hit && !on(player, ISBLIND)) msg("the %s appears confused", mname); did_hit = TRUE; } else if (thrown) bounce(weap, mname); else miss(NULL, mname); return did_hit; } /* * attack: * The monster attacks the player */ int attack(THING *mp) { register const char *mname; /* * Since this is an attack, stop running and any healing that was * going on at the time. */ running = FALSE; count = quiet = 0; if (mp->t_type == 'F') fung_hit = atoi(mp->t_stats.s_dmg); if (mp->t_type == 'M' && !on(player, ISBLIND)) mp->t_disguise = 'M'; if (on(player, ISBLIND)) mname = "it"; else mname = monsters[mp->t_type-'A'].m_name; if (roll_em(mp, &player, NULL, FALSE)) { if (mp->t_type != 'E') hit(mname, NULL); if (pstats.s_hpt <= 0) death(mp->t_type); /* Bye bye life ... */ if (!on(*mp, ISCANC)) switch (mp->t_type) { case 'R': /* * If a rust monster hits, you lose armor, unless * that armor is leather or there is a magic ring */ if (cur_armor != NULL && cur_armor->o_ac < 9 && cur_armor->o_which != LEATHER) if (ISWEARING(R_SUSTARM)) msg("The rust vanishes instantly"); else { cur_armor->o_ac++; if (!terse) msg("your armor appears to be weaker now. Oh my!"); else msg("your armor weakens"); } when 'E': /* * The gaze of the floating eye hypnotizes you */ if (on(player, ISBLIND)) break; player.t_flags &= ~ISRUN; if (!no_command) { addmsg("you are transfixed"); if (!terse) addmsg(" by the gaze of the floating eye"); endmsg(); } no_command += rnd(2) + 2; when 'A': /* * Ants have poisonous bites */ if (!save(VS_POISON)) if (!ISWEARING(R_SUSTSTR)) { chg_str(-1); if (!terse) msg("you feel a sting in your arm and now feel weaker"); else msg("a sting has weakened you"); } else if (!terse) msg("a sting momentarily weakens you"); else msg("sting has no effect"); when 'W': case 'V': /* * Wraiths might drain energy levels, and Vampires * can steal max_hp */ if (rnd(100) < (mp->t_type == 'W' ? 15 : 30)) { register int fewer; if (mp->t_type == 'W') { if (pstats.s_exp == 0) death('W'); /* All levels gone */ if (--pstats.s_lvl == 0) { pstats.s_exp = 0; pstats.s_lvl = 1; } else pstats.s_exp = e_levels[pstats.s_lvl-1]+1; fewer = roll(1, 10); } else fewer = roll(1, 5); pstats.s_hpt -= fewer; max_hp -= fewer; if (pstats.s_hpt < 1) pstats.s_hpt = 1; if (max_hp < 1) death(mp->t_type); msg("you suddenly feel weaker"); } when 'F': /* * Violet fungi stops the poor guy from moving */ player.t_flags |= ISHELD; sprintf(mp->t_stats.s_dmg,"%dd1",++fung_hit); when 'L': { /* * Leperachaun steals some gold */ register long lastpurse; lastpurse = purse; purse -= GOLDCALC; if (!save(VS_MAGIC)) purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; if (purse < 0) purse = 0; remove_monster(&mp->t_pos, mp, FALSE); mp = NULL; if (purse != lastpurse) msg("your purse feels lighter"); } when 'N': { register THING *obj, *steal; register int nobj; /* * Nymph's steal a magic item, look through the pack * and pick out one we like. */ steal = NULL; for (nobj = 0, obj = pack; obj != NULL; obj = next(obj)) if (obj != cur_armor && obj != cur_weapon && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT] && is_magic(obj) && rnd(++nobj) == 0) steal = obj; if (steal != NULL) { remove_monster(&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE); mp = NULL; inpack--; if (steal->o_count > 1 && steal->o_group == 0) { register int oc; oc = steal->o_count--; steal->o_count = 1; msg("she stole %s!", inv_name(steal, TRUE)); steal->o_count = oc; } else { detach(pack, steal); msg("she stole %s!", inv_name(steal, TRUE)); discard(steal); } } } otherwise: break; } } else if (mp->t_type != 'E') { if (mp->t_type == 'F') { pstats.s_hpt -= fung_hit; if (pstats.s_hpt <= 0) death(mp->t_type); /* Bye bye life ... */ } miss(mname, NULL); } if (fight_flush) flush_type(); count = 0; status(); if (mp == NULL) return(-1); else return(0); } /* * swing: * Returns true if the swing hits */ bool swing(int at_lvl, int op_arm, int wplus) { register int res = rnd(20); register int need = (20 - at_lvl) - op_arm; return (res + wplus >= need); } /* * check_level: * Check to see if the guy has gone up a level. */ void check_level(void) { register int i, add, olevel; for (i = 0; e_levels[i] != 0; i++) if (e_levels[i] > pstats.s_exp) break; i++; olevel = pstats.s_lvl; pstats.s_lvl = i; if (i > olevel) { add = roll(i - olevel, 10); max_hp += add; if ((pstats.s_hpt += add) > max_hp) pstats.s_hpt = max_hp; msg("welcome to level %d", i); } } /* * roll_em: * Roll several attacks */ bool roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl) { register struct stats *att, *def; register char *cp; register int ndice, nsides, def_arm; register bool did_hit = FALSE; register int hplus; register int dplus; register int damage; att = &thatt->t_stats; def = &thdef->t_stats; if (weap == NULL) { cp = att->s_dmg; dplus = 0; hplus = 0; } else { hplus = (weap == NULL ? 0 : weap->o_hplus); dplus = (weap == NULL ? 0 : weap->o_dplus); if (weap == cur_weapon) { if (ISRING(LEFT, R_ADDDAM)) dplus += cur_ring[LEFT]->o_ac; else if (ISRING(LEFT, R_ADDHIT)) hplus += cur_ring[LEFT]->o_ac; if (ISRING(RIGHT, R_ADDDAM)) dplus += cur_ring[RIGHT]->o_ac; else if (ISRING(RIGHT, R_ADDHIT)) hplus += cur_ring[RIGHT]->o_ac; } if (hurl) if ((weap->o_flags&ISMISL) && cur_weapon != NULL && cur_weapon->o_which == weap->o_launch) { cp = weap->o_hurldmg; hplus += cur_weapon->o_hplus; dplus += cur_weapon->o_dplus; } else cp = weap->o_hurldmg; else { cp = weap->o_damage; /* * Drain a staff of striking */ if (weap->o_type == STICK && weap->o_which == WS_HIT && --weap->o_charges < 0) { strcpy(weap->o_damage,"0d0"); cp = weap->o_damage; weap->o_hplus = weap->o_dplus = 0; weap->o_charges = 0; } } } /* * If the creature being attacked is not running (alseep or held) * then the attacker gets a plus four bonus to hit. */ if (!on(*thdef, ISRUN)) hplus += 4; def_arm = def->s_arm; if (def == &pstats) { if (cur_armor != NULL) def_arm = cur_armor->o_ac; if (ISRING(LEFT, R_PROTECT)) def_arm -= cur_ring[LEFT]->o_ac; if (ISRING(RIGHT, R_PROTECT)) def_arm -= cur_ring[RIGHT]->o_ac; } for (;;) { ndice = atoi(cp); if ((cp = strchr(cp, 'd')) == NULL) break; nsides = atoi(++cp); if (swing(att->s_lvl, def_arm, hplus + str_plus(att->s_str))) { register int proll; proll = roll(ndice, nsides); #ifdef WIZARD if (ndice + nsides > 0 && proll < 1) 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); #endif damage = dplus + proll + add_dam(att->s_str); def->s_hpt -= max(0, damage); did_hit = TRUE; } if ((cp = strchr(cp, '/')) == NULL) break; cp++; } return did_hit; } /* * prname: * The print name of a combatant */ char * prname(const char *who, bool upper) { static char tbuf[MAXSTR]; *tbuf = '\0'; if (who == 0) strcpy(tbuf, "you"); else if (on(player, ISBLIND)) strcpy(tbuf, "it"); else { strcpy(tbuf, "the "); strcat(tbuf, who); } if (upper) *tbuf = toupper(*tbuf); return tbuf; } /* * hit: * Print a message to indicate a succesful hit */ void hit(const char *er, const char *ee) { register char *s = ""; addmsg(prname(er, TRUE)); if (terse) s = " hit"; else switch (rnd(4)) { case 0: s = " scored an excellent hit on "; when 1: s = " hit "; when 2: s = (er == 0 ? " have injured " : " has injured "); when 3: s = (er == 0 ? " swing and hit " : " swings and hits "); } addmsg(s); if (!terse) addmsg(prname(ee, FALSE)); endmsg(); } /* * miss: * Print a message to indicate a poor swing */ void miss(const char *er, const char *ee) { register char *s = ""; addmsg(prname(er, TRUE)); switch (terse ? 0 : rnd(4)) { case 0: s = (er == 0 ? " miss" : " misses"); when 1: s = (er == 0 ? " swing and miss" : " swings and misses"); when 2: s = (er == 0 ? " barely miss" : " barely misses"); when 3: s = (er == 0 ? " don't hit" : " doesn't hit"); } addmsg(s); if (!terse) addmsg(" %s", prname(ee, FALSE)); endmsg(); } /* * save_throw: * See if a creature save against something */ bool save_throw(int which, THING *tp) { register int need; need = 14 + which - tp->t_stats.s_lvl / 2; return (roll(1, 20) >= need); } /* * save: * See if he saves against various nasty things */ bool save(int which) { if (which == VS_MAGIC) { if (ISRING(LEFT, R_PROTECT)) which -= cur_ring[LEFT]->o_ac; if (ISRING(RIGHT, R_PROTECT)) which -= cur_ring[RIGHT]->o_ac; } return save_throw(which, &player); } /* * str_plus: * Compute bonus/penalties for strength on the "to hit" roll */ int str_plus(str_t str) { if (str == 31) return 3; if (str > 20) return 2; if (str > 16) return 1; if (str > 6) return 0; return str - 7; } /* * add_dam: * Compute additional damage done for exceptionally high or low strength */ int add_dam(str_t str) { if (str == 31) return 6; if (str > 21) return 5; if (str == 21) return 4; if (str > 18) return 3; if (str == 18) return 2; if (str > 15) return 1; if (str > 6) return 0; return str - 7; } /* * raise_level: * The guy just magically went up a level. */ void raise_level(void) { pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L; check_level(); } /* * thunk: * A missile hits a monster */ void thunk(THING *weap, const char *mname) { if (weap->o_type == WEAPON) addmsg("the %s hits ", w_names[weap->o_which]); else addmsg("you hit "); if (on(player, ISBLIND)) msg("it"); else msg("the %s", mname); } /* * bounce: * A missile misses a monster */ void bounce(THING *weap, const char *mname) { if (weap->o_type == WEAPON) addmsg("the %s misses ", w_names[weap->o_which]); else addmsg("you missed "); if (on(player, ISBLIND)) msg("it"); else msg("the %s", mname); } /* * remove: * Remove a monster from the screen */ void remove_monster(coord *mp, THING *tp, bool waskill) { register THING *obj, *nexti; for (obj = tp->t_pack; obj != NULL; obj = nexti) { nexti = next(obj); obj->o_pos = tp->t_pos; detach(tp->t_pack, obj); if (waskill) fall(obj, FALSE); else discard(obj); } moat(mp->y, mp->x) = NULL; mvaddch(mp->y, mp->x, tp->t_oldch); detach(mlist, tp); discard(tp); } /* * is_magic: * Returns true if an object radiates magic */ bool is_magic(THING *obj) { switch (obj->o_type) { case ARMOR: return obj->o_ac != a_class[obj->o_which]; case WEAPON: return obj->o_hplus != 0 || obj->o_dplus != 0; case POTION: case SCROLL: case STICK: case RING: case AMULET: return TRUE; } return FALSE; } /* * killed: * Called to put a monster to death */ void killed(THING *tp, bool pr) { pstats.s_exp += tp->t_stats.s_exp; /* * If the monster was a violet fungi, un-hold him */ switch (tp->t_type) { case 'F': player.t_flags &= ~ISHELD; fung_hit = 0; when 'L': { register THING *gold; if (fallpos(&tp->t_pos, &tp->t_room->r_gold, TRUE)) { gold = new_item(); gold->o_type = GOLD; gold->o_goldval = GOLDCALC; if (save(VS_MAGIC)) gold->o_goldval += GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; attach(tp->t_pack, gold); } } } /* * Get rid of the monster. */ if (pr) { if (!terse) addmsg("you have "); addmsg("defeated "); if (on(player, ISBLIND)) msg("it"); else { if (!terse) addmsg("the "); msg("%s", monsters[tp->t_type-'A'].m_name); } } remove_monster(&tp->t_pos, tp, TRUE); /* * Do adjustments if he went up a level */ check_level(); }