]> granicus.if.org Git - nethack/commitdiff
Large monster can knock back smaller ones
authorPasi Kallinen <paxed@alt.org>
Mon, 18 Jul 2022 20:01:08 +0000 (23:01 +0300)
committerPasi Kallinen <paxed@alt.org>
Mon, 18 Jul 2022 20:01:11 +0000 (23:01 +0300)
When a monster at least two sizes larger hits another one,
there's a chance the smaller defender will be knocked back.

This applies also to hero, attacking when polymorphed to
a large monster, or defending from a large monster.

Most of the monsters that can knock back are giants and dragons.

Idea and some of the code from EvilHack.

doc/fixes3-7-0.txt
include/extern.h
src/mhitm.c
src/mhitu.c
src/uhitm.c

index 129b4d942f78313aa2c6e3e1a7051212e0a39e02..5c32f0b1b869e46923dbdb0967844912046229ef 100644 (file)
@@ -971,6 +971,7 @@ wielding a bec de corbin makes ravens generate peaceful
 moving with 'm' prefix allows hero to enter a known pit carefully
 rangers always succeed in disarming bear traps, unless impaired
 bigroom variant 2 may have ice floor in unlit areas
+some large monsters can knock back smaller monsters with a hit
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index fdc91282419d3f7c1d8bd83fac46ba0bb8cc1cc5..888e52c5b29c572b1bb4efe71fde595ac6ba89c4 100644 (file)
@@ -2919,6 +2919,8 @@ extern void do_stone_mon(struct monst *, struct attack *, struct monst *,
 extern int damageum(struct monst *, struct attack *, int);
 extern int explum(struct monst *, struct attack *);
 extern void missum(struct monst *, struct attack *, boolean);
+extern boolean mhitm_knockback(struct monst *, struct monst *,struct attack *,
+                               int *, boolean);
 extern int passive(struct monst *, struct obj *, boolean, boolean, uchar,
                    boolean);
 extern void passive_obj(struct monst *, struct obj *, struct attack *);
index ca6bf05c1ad93d499e6331f937006c2448c74ffa..0118bc95b7ad7a64d884b11c38437e1c2c03d344 100644 (file)
@@ -946,6 +946,9 @@ mdamagem(struct monst *magr, struct monst *mdef,
     }
 
     mhitm_adtyping(magr, mattk, mdef, &mhm);
+
+    (void) mhitm_knockback(magr, mdef, mattk, &mhm.hitflags, (MON_WEP(magr) != 0));
+
     if (mhm.done)
         return mhm.hitflags;
 
index e8682d489d2c1d076364970e2ddc243d04725bb9..06daf1cc933d91e7c041b6f16e198fcf06c47b96 100644 (file)
@@ -1032,6 +1032,9 @@ hitmu(register struct monst *mtmp, register struct attack *mattk)
         mhm.damage += d((int) mattk->damn, (int) mattk->damd); /* extra dmg */
 
     mhitm_adtyping(mtmp, mattk, &g.youmonst, &mhm);
+
+    (void) mhitm_knockback(mtmp, &g.youmonst, mattk, &mhm.hitflags, (MON_WEP(mtmp) != 0));
+
     if (mhm.done)
         return mhm.hitflags;
 
index 87b3b6e0661f5a476c59782653bfc2c73ad43ed6..a021206e6eadd9ac0f641d35621d0efaac12b8bb 100644 (file)
@@ -3459,9 +3459,11 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
             if (!u.ustuck && rn2(2)) {
                 if (u_slip_free(magr, mattk)) {
                     mhm->damage = 0;
+                    mhm->hitflags |= MM_MISS;
                 } else {
                     set_ustuck(magr);
                     pline("%s grabs you!", Monnam(magr));
+                    mhm->hitflags |= MM_HIT;
                 }
             } else if (u.ustuck == magr) {
                 exercise(A_STR, FALSE);
@@ -3497,8 +3499,10 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                     mhm->damage = 1;
                 if (!(otmp->oartifact && artifact_hit(magr, mdef, otmp,
                                                       &mhm->damage,
-                                                      g.mhitu_dieroll)))
+                                                      g.mhitu_dieroll))) {
                     hitmsg(magr, mattk);
+                    mhm->hitflags |= MM_HIT;
+                }
                 if (!mhm->damage)
                     return;
                 if (objects[otmp->otyp].oc_material == SILVER
@@ -3531,8 +3535,10 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                 }
                 rustm(&g.youmonst, otmp);
             } else if (mattk->aatyp != AT_TUCH || mhm->damage != 0
-                       || magr != u.ustuck)
+                       || magr != u.ustuck) {
                 hitmsg(magr, mattk);
+                mhm->hitflags |= MM_HIT;
+            }
         }
     } else {
         /* mhitm */
@@ -3574,6 +3580,7 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                     if (g.vis)
                         pline("%s hits %s.", Monnam(magr),
                               mon_nam_too(mdef, magr));
+                    mhm->hitflags |= MM_HIT;
                 }
                 /* artifact_hit updates 'tmp' but doesn't inflict any
                    damage; however, it might cause carried items to be
@@ -4213,6 +4220,7 @@ damageum(
     }
 
     mhitm_adtyping(&g.youmonst, mattk, mdef, &mhm);
+
     if (mhm.done)
         return mhm.hitflags;
 
@@ -4543,6 +4551,87 @@ missum(struct monst *mdef, struct attack *mattk, boolean wouldhavehit)
         wakeup(mdef, TRUE);
 }
 
+/* monster hits another monster hard enough to knock it back? */
+boolean
+mhitm_knockback(struct monst *magr,
+                struct monst *mdef,
+                struct attack *mattk,
+                int *hitflags,
+                boolean weapon_used)
+{
+    boolean u_agr = (magr == &g.youmonst);
+    boolean u_def = (mdef == &g.youmonst);
+
+    /* 1/6 chance of attack knocking back a monster */
+    if (rn2(6))
+        return FALSE;
+
+    /* monsters must be alive */
+    if ((!u_agr && DEADMONSTER(magr))
+        || (!u_def && DEADMONSTER(mdef)))
+        return FALSE;
+
+    /* attacker must be much larger than defender */
+    if (!(magr->data->msize > (mdef->data->msize + 1)))
+        return FALSE;
+
+    /* only certain attacks qualify for knockback */
+    if (!((mattk->adtyp == AD_PHYS)
+          && (mattk->aatyp == AT_CLAW
+              || mattk->aatyp == AT_KICK
+              || mattk->aatyp == AT_BUTT
+              || (mattk->aatyp == AT_WEAP && !weapon_used))))
+        return FALSE;
+
+    /* the attack must have hit */
+    /* mon-vs-mon code path doesn't set up hitflags */
+    if ((u_agr || u_def) && !(*hitflags & MM_HIT))
+        return FALSE;
+
+    /* give the message */
+    if (u_def || canseemon(mdef)) {
+        boolean dosteed = u_def && u.usteed;
+
+        /* uhitm: You knock the gnome back with a powerful blow! */
+        /* mhitu: The red dragon knocks you back with a forceful blow! */
+        /* mhitm: The fire giant knocks the gnome back with a forceful strike! */
+
+        pline("%s knock%s %s %s with a %s %s!",
+              u_agr ? "You" : Monnam(magr),
+              u_agr ? "" : "s",
+              u_def ? "you" : y_monnam(mdef),
+              dosteed ? "out of your saddle" : "back",
+              rn2(2) ? "forceful" : "powerful",
+              rn2(2) ? "blow" : "strike");
+    }
+
+    /* do the actual knockback effect */
+    if (u_def) {
+        if (u.usteed)
+            dismount_steed(DISMOUNT_FELL);
+        else
+            hurtle(u.ux - magr->mx, u.uy - magr->my, rnd(2), FALSE);
+        if (!rn2(4))
+            make_stunned((HStun & TIMEOUT) + (long) rnd(2) + 1, TRUE);
+    } else {
+        coordxy x = u_agr ? u.ux : magr->mx;
+        coordxy y = u_agr ? u.uy : magr->my;
+
+        mhurtle(mdef, mdef->mx - x,
+                mdef->my - y, rnd(2));
+        if (DEADMONSTER(mdef))
+            *hitflags |= MM_DEF_DIED;
+        else if (!rn2(4))
+            mdef->mstun = 1;
+    }
+    if (!u_agr) {
+        if (DEADMONSTER(magr))
+            *hitflags |= MM_AGR_DIED;
+    }
+
+    return TRUE;
+}
+
 /* attack monster as a monster; returns True if mon survives */
 static boolean
 hmonas(struct monst *mon)
@@ -4920,6 +5009,9 @@ hmonas(struct monst *mon)
                            mattk->aatyp, FALSE);
         }
 
+        if (mhitm_knockback(&g.youmonst, mon, mattk, &sum[i], weapon_used))
+            break;
+
         /* don't use sum[i] beyond this point;
            'i' will be out of bounds if we get here via 'goto' */
  passivedone: