Mercurial > hg > early-roguelike
diff xrogue/fight.c @ 133:e6179860cb76
Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 21 Apr 2015 08:55:20 -0400 |
parents | |
children | 856017d63519 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xrogue/fight.c Tue Apr 21 08:55:20 2015 -0400 @@ -0,0 +1,1473 @@ +/* + fight.c - All the fighting gets done here + + XRogue: Expeditions into the Dungeons of Doom + Copyright (C) 1991 Robert Pietkivitch + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include <curses.h> +#include <ctype.h> +#include <string.h> +#include "rogue.h" + +#define CONF_DAMAGE -1 +#define PARAL_DAMAGE -2 +#define DEST_DAMAGE -3 +#define DRAIN_DAMAGE -4 + +int killed_chance = 0; /* cumulative chance for goodies to loose it */ + +/* + * returns true if player has a any chance to hit the monster + */ + +player_can_hit(tp, weap) +register struct thing *tp; +register struct object *weap; +{ + if (off(*tp, CMAGICHIT) && off(*tp, BMAGICHIT) && off(*tp, MAGICHIT)) + return(TRUE); + if (weap && weap->o_type == RELIC) + return(TRUE); + if (on(*tp, CMAGICHIT) && weap && (weap->o_hplus>2 || weap->o_dplus>2)) + return(TRUE); + if (on(*tp, BMAGICHIT) && weap && (weap->o_hplus>1 || weap->o_dplus>1)) + return(TRUE); + if (on(*tp, MAGICHIT) && weap && (weap->o_hplus>0 || weap->o_dplus>0)) + return(TRUE); + if (player.t_ctype == C_MONK) { + if (on(*tp, CMAGICHIT) && pstats.s_lvl > 15) + return(TRUE); + if (on(*tp, BMAGICHIT) && pstats.s_lvl > 10) + return(TRUE); + if (on(*tp, MAGICHIT) && pstats.s_lvl > 5) + return(TRUE); + } + return(FALSE); +} + +/* + * fight: + * The player attacks the monster. + */ + +fight(mp, weap, thrown) +register coord *mp; +struct object *weap; +bool thrown; +{ + register struct thing *tp; + register struct linked_list *item; + register bool did_hit = TRUE; + bool see_def, back_stab = FALSE; + register char *mname; + + /* + * Find the monster we want to fight + */ + if ((item = find_mons(mp->y, mp->x)) == NULL) { + return(FALSE); /* must have killed him already */ + } + tp = THINGPTR(item); + + /* + * Since we are fighting, things are not quiet so no healing takes + * place. The -1 also tells us that we are in a fight. + */ + player.t_quiet = -1; + tp->t_quiet = -1; + + see_def = ((off(*tp, ISINVIS) || on(player, CANSEE)) && + (off(*tp, ISSHADOW) || on(player, CANSEE)) && + (!thrown || cansee(unc(tp->t_pos)))); + + mname = see_def ? monster_name(tp) : "something"; + + /* + * if its in the wall, we can't hit it + */ + if (on(*tp, ISINWALL) && off(player, CANINWALL)) + return(FALSE); + + if (on(*tp, ISSTONE)) { + killed(item, FALSE, FALSE, FALSE); + if (see_def) + msg("%s shatters into a million pieces!", prname(mname, TRUE)); + count = 0; + return (TRUE); + } + /* + * Let him know it was really a mimic (if it was one). + */ + if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise) && + off(player, ISBLIND)) + { + if (see_def) { + msg("Wait! That's a %s!", mname); + turn_off(*tp, ISDISGUISE); + } + did_hit = thrown; + } + if (on(*tp, CANSURPRISE) && off(player, ISBLIND) && !ISWEARING(R_ALERT)) { + if (see_def) { + msg("Wait! There's a %s!", mname); + turn_off(*tp, CANSURPRISE); + } + did_hit = thrown; + } + + /* + * if he's a thief or assassin and the creature is asleep then he gets + * a chance for a backstab + */ + if ((player.t_ctype == C_THIEF || player.t_ctype == C_ASSASSIN) && + !thrown && + !on(*tp, NOSTAB) && + !invisible(tp) && + (!on(*tp, ISRUN) || on(*tp, ISHELD) || tp->t_action == A_FREEZE)) + back_stab = TRUE; + + /* + * assassins get an assassination chance, if it fails then its normal + * damage + */ + if (back_stab && player.t_ctype == C_ASSASSIN) { + int chance; + + chance = 50 + (pstats.s_lvl - tp->t_stats.s_lvl) * 5; + if (cur_weapon && (cur_weapon->o_flags & ISPOISON)) + chance += 20; + if (roll(1,100) > chance || on(*tp, ISUNIQUE)) + back_stab = FALSE; + } + + runto(tp, &hero); + + /* Let the monster know that the player has missiles! */ + if (thrown) tp->t_wasshot = TRUE; + + if (did_hit) + { + + did_hit = FALSE; + if (!can_blink(tp) && + player_can_hit(tp, weap) && + roll_em(&player, tp, weap, thrown, cur_weapon, back_stab)) + { + did_hit = TRUE; + + if (on(*tp, NOMETAL) && weap != NULL && + weap->o_type != RELIC && weap->o_flags & ISMETAL) { + msg("Your %s passes right through %s!", + weaps[weap->o_which].w_name, prname(mname, FALSE)); + } + else if (weap != NULL && weap->o_type == MISSILE && on(*tp, CARRYBAMULET)) { + msg("The magic missile has no effect on %s. ", + prname(mname, FALSE)); + } + else { + hit(thrown ? (struct object *)NULL : weap, + TRUE, see_def, + thrown ? weap_name(weap) : NULL, + mname, back_stab, thrown, terse); + + /* See if there are any special effects */ + if (effect(&player, tp, weap, thrown, TRUE, see_def) != 0) + killed(item, FALSE, FALSE, TRUE); + + /* + * Merchants just disappear if hit + */ + else if (on(*tp, CANSELL)) { + if (see_def) + msg("%s disappears with his wares in a flash! ", + prname(mname, FALSE)); + killed(item, FALSE, FALSE, FALSE); + } + + else if (tp->t_stats.s_hpt <= 0) + killed(item, TRUE, TRUE, TRUE); + + else { + /* If the victim was charmed, it now gets a saving throw! */ + if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) { + msg("The eyes of %s turn clear.", prname(mname, FALSE)); + turn_off(*tp, ISCHARMED); + } + + dsrpt_monster(tp, FALSE, see_def); /* Disrupt a spell? */ + } + } + } + else { + miss(thrown ? (struct object *)NULL : weap, + TRUE, see_def, + thrown ? weap_name(weap) : (char *)NULL, + mname, thrown, terse); + } + } + count = 0; + return did_hit; +} + +/* + * attack: + * The monster attacks the player + */ + +attack(mp, weapon, thrown) +register struct thing *mp; +register struct object *weapon; +bool thrown; +{ + register char *mname; + register bool see_att, did_hit = FALSE; + register struct object *wielded; /* The wielded weapon */ + struct linked_list *get_wield; /* Linked list header for wielded */ + + /* + * Since this is an attack, stop running and any healing that was + * going on at the time. The -1 also tells us that we're fighting. + */ + running = FALSE; + player.t_quiet = -1; + mp->t_quiet = -1; + + if (on(*mp, ISDISGUISE) && off(player, ISBLIND)) + turn_off(*mp, ISDISGUISE); + + see_att = ((off(*mp, ISINVIS) || on(player, CANSEE)) && + (off(*mp, ISSHADOW) || on(player, CANSEE)) && + (!thrown || cansee(unc(mp->t_pos)))); + + mname = see_att ? monster_name(mp) : "something"; + + /* + * Try to find a weapon to wield. Wield_weap will return a + * projector if weapon is a projectile (eg. bow for arrow). + * If weapon is NULL, it will try to find a suitable weapon. + */ + get_wield = wield_weap(weapon, mp); + if (get_wield) wielded = OBJPTR(get_wield); + else wielded = NULL; + + /* If we aren't wielding a weapon, wield what we found (could be NULL) */ + if (weapon == NULL) weapon = wielded; + + if (roll_em(mp, &player, weapon, thrown, wielded, FALSE)) { + int death_type; /* From one of the effects of getting hit */ + + did_hit = TRUE; + + if (weapon != NULL && weapon->o_type == MISSILE && cur_relic[STONEBONES_AMULET]) { + hit(weapon, see_att, TRUE, mname, (char *)NULL, FALSE, thrown, terse); + msg("Your amulet absorbs the magic missile. "); + } + else { + hit(weapon, see_att, TRUE, mname, (char *)NULL, FALSE, thrown, terse); + dsrpt_player(); /* see if we disrupted some activity */ + if (pstats.s_hpt <= 0) + death(mp->t_index); /* Bye bye life ... */ + death_type = effect(mp, &player, weapon, thrown, see_att, TRUE); + if (death_type != 0) { + pstats.s_hpt = -1; + death(death_type); + } + } + + } + else { + /* If the thing was trying to surprise, no good */ + if (on(*mp, CANSURPRISE)) turn_off(*mp, CANSURPRISE); + + /* If it couldn't surprise, let's tell the player. */ + else miss(weapon, see_att, TRUE, mname, (char *)NULL, thrown, terse); + } + if (fight_flush) flushinp(); + count = 0; + status(FALSE); + return(did_hit); +} + +/* + * swing: + * returns true if the swing hits + */ + +swing(class, at_lvl, op_arm, wplus) +short class; +int at_lvl, op_arm, wplus; +{ + register int res = rnd(20)+1; + register int need; + + need = char_class[class].base - + char_class[class].factor * + ((min(at_lvl, char_class[class].max_lvl) - + char_class[class].offset)/char_class[class].range) + + (10 - op_arm); + if (need > 20 && need <= 25) need = 20; + + return (res+wplus >= need); +} + +/* + * roll_em: + * Roll several attacks + */ + +roll_em(att_er, def_er, weap, hurl, cur_weapon, back_stab) +struct thing *att_er, *def_er; +struct object *weap; +bool hurl; +struct object *cur_weapon; +bool back_stab; +{ + register struct stats *att, *def; + register char *cp = NULL; + register int ndice, nsides, nplus, def_arm; + char dmgbuf[20]; + bool did_hit = FALSE; + int prop_hplus, prop_dplus; + int vampiric_damage; + + /* Get statistics */ + att = &att_er->t_stats; + def = &def_er->t_stats; + + prop_hplus = prop_dplus = 0; + if (weap == NULL) { + /* + * monks damage grows with level + */ + if (att == &pstats && player.t_ctype == C_MONK) { + sprintf(dmgbuf, "%dd4", att->s_lvl/3+2); + cp = dmgbuf; + } + else + cp = att->s_dmg; + } + else if (weap->o_type == RELIC) { + switch (weap->o_which) { + case MUSTY_DAGGER: + if (player.t_ctype == C_THIEF) + cp = "4d8+2/4d8+2"; + else + cp = "4d8/4d8"; + when YEENOGHU_FLAIL: + cp = "4d8+3/paralyze/confuse"; + when HRUGGEK_MSTAR: + cp = "4d8+3"; + when AXE_AKLAD: + if (player.t_ctype == C_FIGHTER) { + if (hurl) + cp = "4d8+6/drain"; + else + cp = "4d8+4/drain"; + } + else { + if (hurl) + cp = "4d8+4/drain"; + else + cp = "4d8+2/drain"; + } + when MING_STAFF: + cp = "4d8+4"; + when ASMO_ROD: + cp = "4d8/4d8"; + when ORCUS_WAND: + cp = "4d8/destroy"; + } + } + else if (hurl) { + if ((weap->o_flags&ISMISL) && cur_weapon != NULL && + cur_weapon->o_which == weap->o_launch) + { + cp = weap->o_hurldmg; + prop_hplus = cur_weapon->o_hplus; + prop_dplus = cur_weapon->o_dplus; + } + else + cp = (weap->o_flags&ISMISL ? weap->o_damage : 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,"4d8"); + weap->o_hplus = weap->o_dplus = 0; + } + } + /* + * If defender is wearing a cloak of displacement -- no damage + * the first time. (unless its a hurled magic missile or the + * attacker is very smart and can see thru the illusion) + */ + if ((weap == NULL || weap->o_type != MISSILE) && + def == &pstats && + off(*att_er, MISSEDDISP) && + att->s_intel < 21 && + ((cur_misc[WEAR_CLOAK]!=NULL && + cur_misc[WEAR_CLOAK]->o_which==MM_DISP) || + cur_relic[EMORI_CLOAK])) { + turn_on(*att_er, MISSEDDISP); + if (cansee(att_er->t_pos.y, att_er->t_pos.x) && !invisible(att_er)) + msg("%s looks amazed! ", prname(monster_name(att_er), TRUE)); + return (FALSE); + } + if (on(*def_er, CARRYCLOAK) && + def != &pstats && + (weap == NULL || weap->o_type != MISSILE) && + off (*att_er, MISSEDDISP) && + pstats.s_intel < 21) { + turn_on(*att_er, MISSEDDISP); + msg("You feel amazed! "); + return(FALSE); + } + for (;;) + { + int damage; + int hplus = prop_hplus; + int dplus = prop_dplus; + + if (weap != NULL && weap->o_type == RELIC) { + switch (weap->o_which) { + case MUSTY_DAGGER: + if (att != &pstats || /* Not player or good stats */ + (str_compute() > 15 && dex_compute() > 15)) { + + hplus += 6; + dplus += 6; + + /* Give an additional strength and dex bonus */ + if (att == &pstats) { + hplus += str_plus(str_compute()) + + dext_plus(dex_compute()); + dplus += dext_plus(dex_compute()) + + add_dam(str_compute()); + } + else { + hplus += str_plus(att->s_str) + + dext_plus(att->s_dext); + dplus += dext_plus(att->s_dext) + + add_dam(att->s_str); + } + } + else { + hplus -= 3; + dplus -= 3; + } + when YEENOGHU_FLAIL: + case HRUGGEK_MSTAR: + hplus += 3; + dplus += 3; + when MING_STAFF: + hplus += 2; + dplus += 2; + when AXE_AKLAD: + hplus += 5; + dplus += 5; + } + } + else if (weap != NULL) { + hplus += weap->o_hplus; + dplus += weap->o_dplus; + } + + /* Is attacker weak? */ + if (on(*att_er, HASSTINK)) hplus -= 2; + + if (att == &pstats) /* Is the attacker the player? */ + { + hplus += hitweight(); /* adjust for encumberence */ + dplus += hung_dam(); /* adjust damage for hungry player */ + dplus += ring_value(R_ADDDAM); + } + if (back_stab || (weap && att != &pstats && on(*att_er, CANBSTAB))) + hplus += 4; /* add in pluses for backstabbing */ + + /* Get the damage */ + while (isspace(*cp)) cp++; + if (!isdigit(*cp)) { + if (strncmp(cp, "confuse", 7) == 0) ndice = CONF_DAMAGE; + else if (strncmp(cp, "paralyze", 8) == 0) ndice = PARAL_DAMAGE; + else if (strncmp(cp, "destroy", 6) == 0) ndice = DEST_DAMAGE; + else if (strncmp(cp, "drain", 5) == 0) ndice = DRAIN_DAMAGE; + else ndice = 0; + nsides = 0; + nplus = 0; + } + else { + char *oldcp; + + /* Get the number of damage dice */ + ndice = atoi(cp); + if ((cp = strchr(cp, 'd')) == NULL) + break; + + /* Skip the 'd' and get the number of sides per die */ + nsides = atoi(++cp); + + /* Check for an addition -- save old place in case none is found */ + oldcp = cp; + if ((cp = strchr(cp, '+')) != NULL) nplus = atoi(++cp); + else { + nplus = 0; + cp = oldcp; + } + } + + if (def == &pstats) { /* Monster attacks player */ + if (on(*att_er, NOMETAL)) + def_arm = ac_compute(TRUE) - dext_prot(dex_compute()); + else + def_arm = ac_compute(FALSE) - dext_prot(dex_compute()); + hplus += str_plus(att->s_str)+dext_plus(att->s_dext); + } + else if (att == &pstats) { /* Player attacks monster */ + def_arm = def->s_arm - dext_prot(def->s_dext); + if (player.t_ctype == C_MONK) /* no strength bonus for monk */ + if (weap == NULL) + hplus += att->s_lvl/5; /* monks hplus varies with level */ + else + hplus += str_plus(str_compute())+dext_plus(dex_compute()); + } + else { /* Monster attacks monster */ + def_arm = def->s_arm - dext_prot(def->s_dext); + hplus += str_plus(att->s_str)+dext_plus(att->s_dext); + } + + if (swing(att_er->t_ctype, att->s_lvl, def_arm, hplus)) { + register int proll; + + /* Take care of special effects */ + switch (ndice) { + case CONF_DAMAGE: + if (def == &pstats) { /* Monster attacks player */ + if (!save(VS_MAGIC, &player, 0) && off(player, ISCLEAR)) { + msg("You feel disoriented."); + if (find_slot(unconfuse)) + lengthen(unconfuse, HUHDURATION); + else + fuse(unconfuse, (VOID *)NULL, HUHDURATION, AFTER); + turn_on(player, ISHUH); + } + else msg("You feel dizzy, but it quickly passes."); + } + /* Player or monster hits monster */ + else if (!save(VS_MAGIC, def_er, 0) && off(*def_er, ISCLEAR)) { + if (att == &pstats) { + if (rnd(10) > 6) + msg("The artifact warms you with pleasure! "); + } + turn_on(*def_er, ISHUH); + } + did_hit = TRUE; + when PARAL_DAMAGE: + if (def == &pstats) { /* Monster attacks player */ + if (!save(VS_MAGIC, &player, 0) && off(player, CANINWALL)) { + msg("You stiffen up."); + player.t_no_move += movement(&player) * FREEZETIME; + player.t_action = A_FREEZE; + } + } + else if (!save(VS_MAGIC, def_er, 0)) { /* Player hits monster */ + if (att == &pstats) { + if (rnd(10) > 6) + msg("The artifact hums happily! "); + } + turn_off(*def_er, ISRUN); + turn_on(*def_er, ISHELD); + } + did_hit = TRUE; + when DEST_DAMAGE: + if (def == &pstats) { /* Monster attacks player */ + if (rnd(10) > 5) + msg("You feel a tug at your life force."); + if (!save(VS_MAGIC, &player, -4)) { + msg("The wand devours your soul! --More--"); + wait_for(' '); + def->s_hpt = -1; + death(D_RELIC); + } + } + /* Player hits monster */ + else if (!save(VS_MAGIC, def_er, -4)) { + if (att == &pstats) { + if (rnd(10) > 4) + msg("The artifact draws some energy.");