From 0bca93be87d6a92bef9f2fd113276bc827623a9e Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 18 Jul 2022 23:01:08 +0300 Subject: [PATCH] Large monster can knock back smaller ones 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 | 1 + include/extern.h | 2 + src/mhitm.c | 3 ++ src/mhitu.c | 3 ++ src/uhitm.c | 96 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 129b4d942..5c32f0b1b 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -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 diff --git a/include/extern.h b/include/extern.h index fdc912824..888e52c5b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *); diff --git a/src/mhitm.c b/src/mhitm.c index ca6bf05c1..0118bc95b 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -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; diff --git a/src/mhitu.c b/src/mhitu.c index e8682d489..06daf1cc9 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -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; diff --git a/src/uhitm.c b/src/uhitm.c index 87b3b6e06..a021206e6 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -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: -- 2.50.0