Mercurial > hg > early-roguelike
diff rogue5/fight.c @ 33:f502bf60e6e4
Import Rogue 5.4 from the Roguelike Restoration Project (r1490)
author | elwin |
---|---|
date | Mon, 24 May 2010 20:10:59 +0000 |
parents | |
children | e7aab31362af |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rogue5/fight.c Mon May 24 20:10:59 2010 +0000 @@ -0,0 +1,687 @@ +/* + * All the fighting gets done here + * + * @(#)fight.c 4.67 (Berkeley) 09/06/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include <stdlib.h> +#include <curses.h> +#include <string.h> +#include <ctype.h> +#include "rogue.h" + +#define EQSTR(a, b) (strcmp(a, b) == 0) + +static const char *h_names[] = { /* strings for hitting */ + " scored an excellent hit on ", + " hit ", + " have injured ", + " swing and hit ", + " scored an excellent hit on ", + " hit ", + " has injured ", + " swings and hits " +}; + +static const char *m_names[] = { /* strings for missing */ + " miss", + " swing and miss", + " barely miss", + " don't hit", + " misses", + " swings and misses", + " barely misses", + " doesn't hit", +}; + +/* + * adjustments to hit probabilities due to strength + */ +static int str_plus[] = { + -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, +}; + +/* + * adjustments to damage done due to strength + */ +static int add_dam[] = { + -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, + 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6 +}; + +/* + * fight: + * The player attacks the monster. + */ +int +fight(const coord *mp, const THING *weap, int thrown) +{ + THING *tp; + int did_hit = TRUE; + const char *mname; + int ch; + + /* + * Find the monster we want to fight + */ + if ((tp = moat(mp->y, mp->x)) == NULL) + { +#ifdef MASTER + debug("Fight what @ %d,%d", mp->y, mp->x); +#endif + return FALSE; + } + /* + * Since we are fighting, things are not quiet so no healing takes + * place. + */ + count = 0; + quiet = 0; + runto(mp); + /* + * Let him know it was really a xeroc (if it was one). + */ + ch = '\0'; + if (tp->t_type == 'X' && tp->t_disguise != 'X' && !on(player, ISBLIND)) + { + tp->t_disguise = 'X'; + if (on(player, ISHALU)) { + ch = rnd(26) + 'A'; + mvaddch(tp->t_pos.y, tp->t_pos.x, ch); + } + msg(choose_str("heavy! That's a nasty critter!", + "wait! That's a xeroc!")); + if (!thrown) + return FALSE; + } + mname = set_mname(tp); + did_hit = FALSE; + has_hit = (terse && !to_death); + if (roll_em(&player, tp, weap, thrown)) + { + did_hit = FALSE; + if (thrown) + thunk(weap, mname, terse); + else + hit(NULL, mname, terse); + if (on(player, CANHUH)) + { + did_hit = TRUE; + tp->t_flags |= ISHUH; + player.t_flags &= ~CANHUH; + endmsg(); + has_hit = FALSE; + msg("your hands stop glowing %s", pick_color("red")); + } + if (tp->t_stats.s_hpt <= 0) + killed(tp, TRUE); + else if (did_hit && !on(player, ISBLIND)) + msg("%s appears confused", mname); + did_hit = TRUE; + } + else + if (thrown) + bounce(weap, mname, terse); + else + miss(NULL, mname, terse); + return did_hit; +} + +/* + * attack: + * The monster attacks the player + */ +int +attack(THING *mp) +{ + const char *mname; + int oldhp; + + /* + * Since this is an attack, stop running and any healing that was + * going on at the time. + */ + running = FALSE; + count = 0; + quiet = 0; + if (to_death && !on(*mp, ISTARGET)) + { + to_death = FALSE; + kamikaze = FALSE; + } + if (mp->t_type == 'X' && mp->t_disguise != 'X' && !on(player, ISBLIND)) + { + mp->t_disguise = 'X'; + if (on(player, ISHALU)) + mvaddch(mp->t_pos.y, mp->t_pos.x, rnd(26) + 'A'); + } + mname = set_mname(mp); + oldhp = pstats.s_hpt; + if (roll_em(mp, &player, NULL, FALSE)) + { + if (mp->t_type != 'I') + { + if (has_hit) + addmsg(". "); + hit(mname, NULL, FALSE); + } + else + if (has_hit) + endmsg(); + has_hit = FALSE; + if (pstats.s_hpt <= 0) + death(mp->t_type); /* Bye bye life ... */ + else if (!kamikaze) + { + oldhp -= pstats.s_hpt; + if (oldhp > max_hit) + max_hit = oldhp; + if (pstats.s_hpt <= max_hit) + to_death = FALSE; + } + if (!on(*mp, ISCANC)) + switch (mp->t_type) + { + case 'A': + /* + * If an aquator hits, you can lose armor class. + */ + rust_armor(cur_armor); + when 'I': + /* + * The ice monster freezes you + */ + player.t_flags &= ~ISRUN; + if (!no_command) + { + addmsg("you are frozen"); + if (!terse) + addmsg(" by the %s", mname); + endmsg(); + } + no_command += rnd(2) + 2; + if (no_command > BORE_LEVEL) + death('h'); + when 'R': + /* + * Rattlesnakes have poisonous bites + */ + if (!save(VS_POISON)) + { + if (!ISWEARING(R_SUSTSTR)) + { + chg_str(-1); + if (!terse) + msg("you feel a bite in your leg and now feel weaker"); + else + msg("a bite has weakened you"); + } + else if (!to_death) + { + if (!terse) + msg("a bite momentarily weakens you"); + else + msg("bite 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)) + { + 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, 3); + pstats.s_hpt -= fewer; + max_hp -= fewer; + if (pstats.s_hpt <= 0) + pstats.s_hpt = 1; + if (max_hp <= 0) + death(mp->t_type); + msg("you suddenly feel weaker"); + } + when 'F': + /* + * Venus Flytrap stops the poor guy from moving + */ + player.t_flags |= ISHELD; + sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dx1", ++vf_hit); + if (--pstats.s_hpt <= 0) + death('F'); + when 'L': + { + /* + * Leperachaun steals some gold + */ + int lastpurse; + + lastpurse = purse; + purse -= GOLDCALC; + if (!save(VS_MAGIC)) + purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; + if (purse < 0) + purse = 0; + remove_mon(&mp->t_pos, mp, FALSE); + mp=NULL; + if (purse != lastpurse) + msg("your purse feels lighter"); + } + when 'N': + { + THING *obj, *steal; + 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_mon(&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE); + mp=NULL; + steal = leave_pack(steal, TRUE, FALSE); + msg("she stole %s!", inv_name(steal, TRUE)); + discard(steal); + } + } + otherwise: + break; + } + } + else if (mp->t_type != 'I') + { + if (has_hit) + { + addmsg(". "); + has_hit = FALSE; + } + if (mp->t_type == 'F') + { + pstats.s_hpt -= vf_hit; + if (pstats.s_hpt <= 0) + death(mp->t_type); /* Bye bye life ... */ + } + miss(mname, NULL, FALSE); + } + if (fight_flush && !to_death) + flush_type(); + count = 0; + status(); + if (mp == NULL) + return(-1); + else + return(0); +} + +/* + * set_mname: + * return the monster name for the given monster + */ +const char * +set_mname(const THING *tp) +{ + int ch; + const char *mname; + static char tbuf[MAXSTR] = { 't', 'h', 'e', ' ' }; + + if (!see_monst(tp) && !on(player, SEEMONST)) + return (terse ? "it" : "something"); + else if (on(player, ISHALU)) + { + move(tp->t_pos.y, tp->t_pos.x); + ch = toascii(CCHAR(inch())); + if (!isupper(ch)) + ch = rnd(26); + else + ch -= 'A'; + mname = monsters[ch].m_name; + } + else + mname = monsters[tp->t_type - 'A'].m_name; + strcpy(&tbuf[4], mname); + return tbuf; +} + +/* + * swing: + * Returns true if the swing hits + */ +int +swing(int at_lvl, int op_arm, int wplus) +{ + int res = rnd(20); + int need = (20 - at_lvl) - op_arm; + + return (res + wplus >= need); +} + +/* + * roll_em: + * Roll several attacks + */ +int +roll_em(const THING *thatt, THING *thdef, const THING *weap, int hurl) +{ + const struct stats *att; + struct stats *def; + const char *cp; + int ndice, nsides, def_arm; + int did_hit = FALSE; + int hplus; + int dplus; + 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_arm; + else if (ISRING(LEFT, R_ADDHIT)) + hplus += cur_ring[LEFT]->o_arm; + if (ISRING(RIGHT, R_ADDDAM)) + dplus += cur_ring[RIGHT]->o_arm; + else if (ISRING(RIGHT, R_ADDHIT)) + hplus += cur_ring[RIGHT]->o_arm; + } + cp = weap->o_damage; + 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 if (weap->o_launch < 0) + cp = weap->o_hurldmg; + } + } + /* + * 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_arm; + if (ISRING(LEFT, R_PROTECT)) + def_arm -= cur_ring[LEFT]->o_arm; + if (ISRING(RIGHT, R_PROTECT)) + def_arm -= cur_ring[RIGHT]->o_arm; + } + while(cp != NULL && *cp != '\0') + { + ndice = atoi(cp); + if ((cp = strchr(cp, 'x')) == NULL) + break; + nsides = atoi(++cp); + if (swing(att->s_lvl, def_arm, hplus + str_plus[att->s_str])) + { + int proll; + + proll = roll(ndice, nsides); +#ifdef MASTER + if (ndice + nsides > 0 && proll <= 0) + 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); +#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 *mname, int upper) +{ + static char tbuf[MAXSTR]; + + *tbuf = '\0'; + if (mname == 0) + strcpy(tbuf, "you"); + else + strcpy(tbuf, mname); + if (upper) + *tbuf = (char) toupper(*tbuf); + return tbuf; +} + +/* + * thunk: + * A missile hits a monster + */ +void +thunk(const THING *weap, const char *mname, int noend) +{ + if (to_death) + return; + if (weap->o_type == WEAPON) + addmsg("the %s hits ", weap_info[weap->o_which].oi_name); + else + addmsg("you hit "); + addmsg("%s", mname); + if (!noend) + endmsg(); +} + +/* + * hit: + * Print a message to indicate a succesful hit + */ + +void +hit(const char *er, const char *ee, int noend) +{ + int i; + const char *s; + + if (to_death) + return; + addmsg(prname(er, TRUE)); + if (terse) + s = " hit"; + else + { + i = rnd(4); + if (er != NULL) + i += 4; + s = h_names[i]; + } + addmsg(s); + if (!terse) + addmsg(prname(ee, FALSE)); + if (!noend) + endmsg(); +} + +/* + * miss: + * Print a message to indicate a poor swing + */ +void +miss(const char *er, const char *ee, int noend) +{ + int i; + + if (to_death) + return; + addmsg(prname(er, TRUE)); + if (terse) + i = 0; + else + i = rnd(4); + if (er != NULL) + i += 4; + addmsg(m_names[i]); + if (!terse) + addmsg(" %s", prname(ee, FALSE)); + if (!noend) + endmsg(); +} + +/* + * bounce: + * A missile misses a monster + */ +void +bounce(const THING *weap, const char *mname, int noend) +{ + if (to_death) + return; + if (weap->o_type == WEAPON) + addmsg("the %s misses ", weap_info[weap->o_which].oi_name); + else + addmsg("you missed "); + addmsg(mname); + if (!noend) + endmsg(); +} + +/* + * remove_mon: + * Remove a monster from the screen + */ +void +remove_mon(const coord *mp, THING *tp, int waskill) +{ + 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); + if (on(*tp, ISTARGET)) + { + kamikaze = FALSE; + to_death = FALSE; + if (fight_flush) + flush_type(); + } + discard(tp); +} + +/* + * killed: + * Called to put a monster to death + */ +void +killed(THING *tp, int pr) +{ + const char *mname; + + pstats.s_exp += tp->t_stats.s_exp; + + /* + * If the monster was a venus flytrap, un-hold him + */ + switch (tp->t_type) + { + case 'F': + player.t_flags &= ~ISHELD; + vf_hit = 0; + strcpy(monsters['F'-'A'].m_stats.s_dmg, "000x0"); + when 'L': + { + THING *gold; + + if (fallpos(&tp->t_pos, &tp->t_room->r_gold) && level >= max_level) + { + 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. + */ + mname = set_mname(tp); + remove_mon(&tp->t_pos, tp, TRUE); + if (pr) + { + if (has_hit) + { + addmsg(". Defeated "); + has_hit = FALSE; + } + else + { + if (!terse) + addmsg("you have "); + addmsg("defeated "); + } + msg(mname); + } + /* + * Do adjustments if he went up a level + */ + check_level(); + if (fight_flush) + flush_type(); +}