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.");