]> granicus.if.org Git - nethack/commitdiff
combat fixes
authornethack.rankin <nethack.rankin>
Sat, 15 Mar 2003 05:38:44 +0000 (05:38 +0000)
committernethack.rankin <nethack.rankin>
Sat, 15 Mar 2003 05:38:44 +0000 (05:38 +0000)
1) make two-weapon combat perform two attacks instead of always either
   hitting twice or missing twice;
2) address <Someone>'s report of weapon skill to-hit adjustment being ignored
   for bare-handed and martial arts attacks;
3) address newsgroup complaints about the intrusive "your armor is rather
   cumbersome" message given every time a monk wearing a suit attacks;
   this implements the suggestion that it only occur for those times where
   you miss because of the penalty involved, suppressing it when you miss
   due to other reasons and when you successfully hit;
4) bonus fix:  a side-effect of #3 is that the order of the messages "your
   armor is cumbersome" and Stormbringer's "bloodthirsty blade attacks" is
   inverted, making a sensible sequence instead of implying precognition.

doc/fixes35.0
include/extern.h
src/dokick.c
src/uhitm.c

index fb58beaeea56aa5d990d204c9d09a551cbd9018a..a53a1adcd52e807b196b56ce851288930681a9d9 100644 (file)
@@ -16,6 +16,11 @@ paper, straw and wood golems resist cold
 the options lootabc, showrace, travelcmd, and runmode are now saved
 use mons[] array offsets in mnum field in save file rather than storing
        the ptr and calculating the distance from beginning of array
+two-weapon combat makes two attacks instead of having one attack hit with
+       each weapon
+apply weapon skill to-hit bonus or penalty to bare-handed attacks
+only give monk's "cumbersome armor" message when the armor penalty causes
+       an attack to miss
 
 
 Platform- and/or Interface-Specific Fixes
index b8f59878971804adc399ec2de3282463f55801f8..dc083631fb8e0e3e0a2ab79c3b69a68cd7184222 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)extern.h   3.4     2003/03/10      */
+/*     SCCS Id: @(#)extern.h   3.4     2003/03/14      */
 /* Copyright (c) Steve Creps, 1988.                              */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -2016,11 +2016,11 @@ E void NDECL(u_init);
 E void FDECL(hurtmarmor,(struct monst *,int));
 E boolean FDECL(attack_checks, (struct monst *,struct obj *));
 E void FDECL(check_caitiff, (struct monst *));
-E schar FDECL(find_roll_to_hit, (struct monst *));
+E int FDECL(find_roll_to_hit, (struct monst *,int,struct obj *,int *,int *));
 E boolean FDECL(attack, (struct monst *));
 E boolean FDECL(hmon, (struct monst *,struct obj *,int));
 E int FDECL(damageum, (struct monst *,struct attack *));
-E void FDECL(missum, (struct monst *,struct attack *));
+E void FDECL(missum, (struct monst *,struct attack *,BOOLEAN_P));
 E int FDECL(passive, (struct monst *,BOOLEAN_P,int,UCHAR_P));
 E void FDECL(passive_obj, (struct monst *,struct obj *,struct attack *));
 E void FDECL(stumble_onto_mimic, (struct monst *));
index 966c889deaceeed7bbb7ba77c4ff853e224528e6..3bc3fc1c1224be25fa39d692b923382b315afb63 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)dokick.c   3.4     2003/01/08      */
+/*     SCCS Id: @(#)dokick.c   3.4     2003/03/14      */
 /* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -135,8 +135,9 @@ register xchar x, y;
         */
        if (Upolyd && attacktype(youmonst.data, AT_KICK)) {
            struct attack *uattk;
-           int sum;
-           schar tmp = find_roll_to_hit(mon);
+           int sum, kickdieroll, armorpenalty, attknum = 0,
+               tmp = find_roll_to_hit(mon, AT_KICK, (struct obj *)0,
+                                      &attknum, &armorpenalty);
 
            for (i = 0; i < NATTK; i++) {
                /* first of two kicks might have provoked counterattack
@@ -153,14 +154,14 @@ register xchar x, y;
                       and shades have no passive counterattack */
                    Your("%s %s.", kick_passes_thru, mon_nam(mon));
                    break;      /* skip any additional kicks */
-               } else if (tmp > rnd(20)) {
+               } else if (tmp > (kickdieroll = rnd(20))) {
                    You("kick %s.", mon_nam(mon));
                    sum = damageum(mon, uattk);
                    (void)passive(mon, (boolean)(sum > 0), (sum != 2), AT_KICK);
                    if (sum == 2)
                        break;          /* Defender died */
                } else {
-                   missum(mon, uattk);
+                   missum(mon, uattk, (tmp + armorpenalty > kickdieroll));
                    (void)passive(mon, 0, 1, AT_KICK);
                }
            }
index 919d26ac253cebbddb85a0d27949c8dbcf8d10e2..1e5a27f224abdc5b9a42706d7052eb0850c2b35d 100644 (file)
@@ -1,12 +1,13 @@
-/*     SCCS Id: @(#)uhitm.c    3.4     2003/02/18      */
+/*     SCCS Id: @(#)uhitm.c    3.4     2003/03/14      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 
-STATIC_DCL boolean FDECL(known_hitum, (struct monst *,int *,struct attack *));
+STATIC_DCL boolean FDECL(known_hitum, (struct monst *,struct obj *,
+                                      int *,int,int,struct attack *));
 STATIC_DCL void FDECL(steal_it, (struct monst *, struct attack *));
-STATIC_DCL boolean FDECL(hitum, (struct monst *,int,struct attack *));
+STATIC_DCL boolean FDECL(hitum, (struct monst *,struct attack *));
 STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *,struct obj *,int));
 #ifdef STEED
 STATIC_DCL int FDECL(joust, (struct monst *,struct obj *));
@@ -17,7 +18,7 @@ STATIC_DCL int FDECL(explum, (struct monst *,struct attack *));
 STATIC_DCL void FDECL(start_engulf, (struct monst *));
 STATIC_DCL void NDECL(end_engulf);
 STATIC_DCL int FDECL(gulpum, (struct monst *,struct attack *));
-STATIC_DCL boolean FDECL(hmonas, (struct monst *,int));
+STATIC_DCL boolean FDECL(hmonas, (struct monst *));
 STATIC_DCL void FDECL(nohandglow, (struct monst *));
 STATIC_DCL boolean FDECL(shade_aware, (struct obj *));
 
@@ -226,27 +227,34 @@ struct monst *mtmp;
        }
 }
 
-schar
-find_roll_to_hit(mtmp)
+int
+find_roll_to_hit(mtmp, aatyp, weapon, attk_count, role_roll_penalty)
 register struct monst *mtmp;
+int aatyp;             /* usually AT_WEAP or AT_KICK */
+struct obj *weapon;    /* uwep or uswapwep or NULL */
+int *attk_count, *role_roll_penalty;
 {
-       schar tmp;
-       int tmp2;
+       int tmp, tmp2;
+
+       *role_roll_penalty = 0; /* default is `none' */
 
        tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc +
                maybe_polyd(youmonst.data->mlevel, u.ulevel);
 
-       check_caitiff(mtmp);
+       /* some actions should occur only once during multiple attacks */
+       if (!(*attk_count)++) {
+           /* knight's chivalry */
+           check_caitiff(mtmp);
 
-/*     attacking peaceful creatures is bad for the samurai's giri */
-       if (Role_if(PM_SAMURAI) && mtmp->mpeaceful &&
-           u.ualign.record > -10) {
-           You("dishonorably attack the innocent!");
-           adjalign(-1);
+           /* attacking peaceful creatures is bad for the samurai's giri */
+           if (Role_if(PM_SAMURAI) && mtmp->mpeaceful &&
+                   u.ualign.record > -10) {
+               You("dishonorably attack the innocent!");
+               adjalign(-1);
+           }
        }
 
-/*     Adjust vs. (and possibly modify) monster state.         */
-
+       /* adjust vs. (and possibly modify) monster state */
        if(mtmp->mstun) tmp += 2;
        if(mtmp->mflee) tmp += 2;
 
@@ -261,29 +269,34 @@ register struct monst *mtmp;
                        mtmp->mfrozen = 0;
                }
        }
-       if (is_orc(mtmp->data) && maybe_polyd(is_elf(youmonst.data),
-                       Race_if(PM_ELF)))
-           tmp++;
-       if(Role_if(PM_MONK) && !Upolyd) {
-           if (uarm) {
-               Your("armor is rather cumbersome...");
-               tmp -= urole.spelarmr;
-           } else if (!uwep && !uarms) {
+
+       /* role/race adjustments */
+       if (Role_if(PM_MONK) && !Upolyd) {
+           if (uarm)
+               tmp -= (*role_roll_penalty = urole.spelarmr);
+           else if (!uwep && !uarms)
                tmp += (u.ulevel / 3) + 2;
-           }
        }
+       if (is_orc(mtmp->data) &&
+               maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF)))
+           tmp++;
 
-/*     with a lot of luggage, your agility diminishes */
+       /* encumbrance: with a lot of luggage, your agility diminishes */
        if ((tmp2 = near_capacity()) != 0) tmp -= (tmp2*2) - 1;
        if (u.utrap) tmp -= 3;
-/*     Some monsters have a combination of weapon attacks and non-weapon
- *     attacks.  It is therefore wrong to add hitval to tmp; we must add
- *     it only for the specific attack (in hmonas()).
- */
-       if (uwep && !Upolyd) {
-               tmp += hitval(uwep, mtmp);
-               tmp += weapon_hit_bonus(uwep);
+
+       /*
+        * hitval applies if making a weapon attack while wielding a weapon;
+        * weapon_hit_bonus applies if doing a weapon attack even bare-handed
+        * or if kicking as martial artist
+        */
+       if (aatyp == AT_WEAP || aatyp == AT_CLAW) {
+           if (weapon) tmp += hitval(weapon, mtmp);
+           tmp += weapon_hit_bonus(weapon);
+       } else if (aatyp == AT_KICK && martial_bonus()) {
+           tmp += weapon_hit_bonus((struct obj *)0);
        }
+
        return tmp;
 }
 
@@ -387,11 +400,10 @@ register struct monst *mtmp;
           mtmp->mx != u.ux+u.dx || mtmp->my != u.uy+u.dy)) /* it moved */
                return(FALSE);
 
-       tmp = find_roll_to_hit(mtmp);
        if (Upolyd)
-               (void) hmonas(mtmp, tmp);
+               (void) hmonas(mtmp);
        else
-               (void) hitum(mtmp, tmp, youmonst.data->mattk);
+               (void) hitum(mtmp, youmonst.data->mattk);
        mtmp->mstrategy &= ~STRAT_WAITMASK;
 
 atk_done:
@@ -408,10 +420,13 @@ atk_done:
        return(TRUE);
 }
 
+/* really hit target monster; returns TRUE if it still lives */
 STATIC_OVL boolean
-known_hitum(mon, mhit, uattk)  /* returns TRUE if monster still lives */
+known_hitum(mon, weapon, mhit, rollneeded, armorpenalty, uattk)
 register struct monst *mon;
-register int *mhit;
+struct obj *weapon;
+int *mhit;
+int rollneeded, armorpenalty;  /* for monks */
 struct attack *uattk;
 {
        register boolean malive = TRUE;
@@ -423,31 +438,27 @@ struct attack *uattk;
        }
 
        if(!*mhit) {
-           missum(mon, uattk);
+           missum(mon, uattk, (rollneeded + armorpenalty > dieroll));
        } else {
            int oldhp = mon->mhp,
                x = u.ux + u.dx, y = u.uy + u.dy;
+           long oldweaphit = u.uconduct.weaphit;
 
            /* KMH, conduct */
-           if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))
+           if (weapon &&
+                   (weapon->oclass == WEAPON_CLASS || is_weptool(weapon)))
                u.uconduct.weaphit++;
 
            /* we hit the monster; be careful: it might die or
               be knocked into a different location */
            notonhead = (mon->mx != x || mon->my != y);
-           malive = hmon(mon, uwep, 0);
-           /* this assumes that Stormbringer was uwep not uswapwep */ 
-           if (malive && u.twoweap && !override_confirmation &&
-                   m_at(x, y) == mon)
-               malive = hmon(mon, uswapwep, 0);
+           malive = hmon(mon, weapon, 0);
            if (malive) {
                /* monster still alive */
                if(!rn2(25) && mon->mhp < mon->mhpmax/2
                            && !(u.uswallow && mon == u.ustuck)) {
                    /* maybe should regurgitate if swallowed? */
-                   if(!rn2(3)) {
-                       monflee(mon, rnd(100), FALSE, TRUE);
-                   } else monflee(mon, 0, FALSE, TRUE);
+                   monflee(mon, !rn2(3) ? rnd(100) : 0, FALSE, TRUE);
 
                    if(u.ustuck == mon && !u.uswallow && !sticks(youmonst.data))
                        u.ustuck = 0;
@@ -457,28 +468,42 @@ struct attack *uattk;
                if (mon->mhp == oldhp) {
                    *mhit = 0;
                    /* a miss does not break conduct */
-                   if (uwep &&
-                       (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))
-                       --u.uconduct.weaphit;
+                   u.uconduct.weaphit = oldweaphit;
                }
                if (mon->wormno && *mhit)
-                   cutworm(mon, x, y, uwep);
+                   cutworm(mon, x, y, weapon);
            }
        }
        return(malive);
 }
 
+/* hit target monster; returns TRUE if it still lives */
 STATIC_OVL boolean
-hitum(mon, tmp, uattk)         /* returns TRUE if monster still lives */
+hitum(mon, uattk)
 struct monst *mon;
-int tmp;
 struct attack *uattk;
 {
        boolean malive;
+       int armorpenalty, attknum = 0,
+           x = u.ux + u.dx, y = u.uy + u.dy,
+           tmp = find_roll_to_hit(mon, uattk->aatyp, uwep,
+                                  &attknum, &armorpenalty);
        int mhit = (tmp > (dieroll = rnd(20)) || u.uswallow);
 
        if(tmp > dieroll) exercise(A_DEX, TRUE);
-       malive = known_hitum(mon, &mhit, uattk);
+       malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk);
+       /* second attack for two-weapon combat; won't occur if Stormbringer
+          overrode confirmation (assumes Stormbringer is primary weapon)
+          or if the monster was killed or knocked to different location */
+       if (u.twoweap && !override_confirmation &&
+               malive && m_at(x, y) == mon) {
+           tmp = find_roll_to_hit(mon, uattk->aatyp, uswapwep,
+                                  &attknum, &armorpenalty);
+           mhit = (tmp > (dieroll = rnd(20)) || u.uswallow);
+           malive = known_hitum(mon, uswapwep, &mhit,
+                          tmp, armorpenalty, uattk);
+       }
+
        (void) passive(mon, mhit, malive, AT_WEAP);
        return(malive);
 }
@@ -1876,10 +1901,14 @@ register struct attack *mattk;
 }
 
 void
-missum(mdef,mattk)
+missum(mdef, mattk, wouldhavehit)
 register struct monst *mdef;
 register struct attack *mattk;
+boolean wouldhavehit;
 {
+       if (wouldhavehit) /* monk is missing due to penalty for wearing suit */
+               Your("armor is rather cumbersome...");
+
        if (could_seduce(&youmonst, mdef, mattk))
                You("pretend to be friendly to %s.", mon_nam(mdef));
        else if(canspotmon(mdef) && flags.verbose)
@@ -1891,17 +1920,16 @@ register struct attack *mattk;
 }
 
 STATIC_OVL boolean
-hmonas(mon, tmp)               /* attack monster as a monster. */
+hmonas(mon)            /* attack monster as a monster. */
 register struct monst *mon;
-register int tmp;
 {
        struct attack *mattk, alt_attk;
-       int     i, sum[NATTK], hittmp = 0;
-       int     nsum = 0;
-       int     dhit = 0;
+       struct obj *weapon;
+       boolean altwep = FALSE;
+       int i, tmp, armorpenalty, sum[NATTK],
+           nsum = 0, dhit = 0, attknum = 0;
 
        for(i = 0; i < NATTK; i++) {
-
            sum[i] = 0;
            mattk = getmattk(youmonst.data, i, sum, &alt_attk);
            switch(mattk->aatyp) {
@@ -1916,21 +1944,21 @@ use_weapon:
         * we currently allow the player to get each of these as a weapon
         * attack.  Is this really desirable?
         */
-                       if (uwep) {
-                           hittmp = hitval(uwep, mon);
-                           hittmp += weapon_hit_bonus(uwep);
-                           tmp += hittmp;
-                       }
+                       /* approximate two-weapon mode */
+                       weapon = (altwep && uswapwep) ? uswapwep : uwep;
+                       altwep = !altwep;   /* toggle for next attack */
+                       tmp = find_roll_to_hit(mon, AT_WEAP, weapon,
+                                              &attknum, &armorpenalty);
                        dhit = (tmp > (dieroll = rnd(20)) || u.uswallow);
-                       /* KMH -- Don't accumulate to-hit bonuses */
-                       if (uwep) tmp -= hittmp;
                        /* Enemy dead, before any special abilities used */
-                       if (!known_hitum(mon,&dhit,mattk)) {
+                       if (!known_hitum(mon, weapon, &dhit,
+                                        tmp, armorpenalty, mattk)) {
                            sum[i] = 2;
                            break;
                        } else sum[i] = dhit;
                        /* might be a worm that gets cut in half */
-                       if (m_at(u.ux+u.dx, u.uy+u.dy) != mon) return((boolean)(nsum != 0));
+                       if (m_at(u.ux+u.dx, u.uy+u.dy) != mon)
+                           return (boolean)(nsum != 0);
                        /* Do not print "You hit" message, since known_hitum
                         * already did it.
                         */
@@ -1939,7 +1967,8 @@ use_weapon:
                                sum[i] = damageum(mon,mattk);
                        break;
                case AT_CLAW:
-                       if (i==0 && uwep && !cantwield(youmonst.data)) goto use_weapon;
+                       if (i == 0 && uwep && !cantwield(youmonst.data))
+                           goto use_weapon;
 #ifdef SEDUCE
                        /* succubi/incubi are humanoid, but their _second_
                         * attack is AT_CLAW, not their first...
@@ -1953,8 +1982,13 @@ use_weapon:
                case AT_TUCH:
                case AT_BUTT:
                case AT_TENT:
-                       if (i==0 && uwep && (youmonst.data->mlet==S_LICH)) goto use_weapon;
-                       if ((dhit = (tmp > rnd(20) || u.uswallow)) != 0) {
+                       if (i == 0 && uwep && youmonst.data->mlet == S_LICH)
+                           goto use_weapon;
+                       tmp = find_roll_to_hit(mon, mattk->aatyp,
+                                              (struct obj *)0,
+                                              &attknum, &armorpenalty);
+                       dhit = (tmp > (dieroll = rnd(20)) || u.uswallow);
+                       if (dhit) {
                            int compat;
 
                            if (!u.uswallow &&
@@ -1991,8 +2025,10 @@ use_weapon:
                                    Your("tentacles suck %s.", mon_nam(mon));
                            else You("hit %s.", mon_nam(mon));
                            sum[i] = damageum(mon, mattk);
-                       } else
-                           missum(mon, mattk);
+                       } else {
+                           missum(mon, mattk,
+                                  (tmp + armorpenalty > dieroll));
+                       }
                        break;
 
                case AT_HUGS:
@@ -2025,6 +2061,9 @@ use_weapon:
                        break;
 
                case AT_ENGL:
+                       tmp = find_roll_to_hit(mon, mattk->aatyp,
+                                              (struct obj *)0,
+                                              &attknum, &armorpenalty);
                        if((dhit = (tmp > rnd(20+i)))) {
                                wakeup(mon);
                                if (mon->data == &mons[PM_SHADE])
@@ -2042,8 +2081,9 @@ use_weapon:
                                        mdamageu(mon, rnd(8));
                                    }
                                }
-                       } else
-                               missum(mon, mattk);
+                       } else {
+                               missum(mon, mattk, FALSE);
+                       }
                        break;
 
                case AT_MAGC: