]> granicus.if.org Git - nethack/commitdiff
fix issue #836 - engulfing mounter hero
authorPatR <rankin@nethack.org>
Tue, 9 Aug 2022 23:22:50 +0000 (16:22 -0700)
committerPatR <rankin@nethack.org>
Tue, 9 Aug 2022 23:22:50 +0000 (16:22 -0700)
Reported by copperwater:  if an engulfer swallowed a mounted hero,
odd things could happen if the hero dismounted.  The steed would be
silently expelled and float-down flooreffects were attempted.

It turns out that if the engulfer is classified as an animal (so
purple worm, lurker above, trapper), the hero got "plucked from
<steed>'s saddle" and was forcibly dismounted prior to completing
the engulf operation, but non-animals (vortices, air elemental,
ocher jelly, Juiblex) swallowed the hero+steed intact.  The most
straightforward fix to dismounting-while-engulfed issues is to change
engulfing to always pluck the hero from the saddle even when the
engulfer isn't an animal.

If there's no room on the level to place the former steed, it gets
killed off.  I looked at changing that to put the steed into limbo,
waiting to migrate back to the current level if hero leaves and
subsequently returns, but that breaks movemon()'s assumption that
when monsters are in the process of moving, only the currently moving
one can be taken off the fmon list to be placed on migrating_mons.

[The recently added monster knockback code violates that assumption
too when knocking the victim into a level changer trap.  It needs to
be fixed in one fashion or another.]

include/extern.h
src/mhitm.c
src/mhitu.c
src/mon.c
src/monmove.c
src/steed.c

index 50203521937a14f7696a2780e9c8ddbeff57ea9a..ee0a4dc75850f1f8f5d4bd3f25ac295d2eddaaed 100644 (file)
@@ -1559,6 +1559,7 @@ extern void mon_to_stone(struct monst *);
 extern void m_into_limbo(struct monst *);
 extern void migrate_mon(struct monst *, coordxy, coordxy);
 extern void mnexto(struct monst *, unsigned);
+extern void deal_with_overcrowding(struct monst *);
 extern void maybe_mnexto(struct monst *);
 extern int mnearto(struct monst *, coordxy, coordxy, boolean, unsigned);
 extern void m_respond(struct monst *);
@@ -1571,7 +1572,9 @@ extern void seemimic(struct monst *);
 extern void normal_shape(struct monst *);
 extern void iter_mons(void (*)(struct monst *));
 extern struct monst *get_iter_mons(boolean (*)(struct monst *));
-extern struct monst *get_iter_mons_xy(boolean (*)(struct monst *, coordxy, coordxy), coordxy, coordxy);
+extern struct monst *get_iter_mons_xy(boolean (*)(struct monst *,
+                                                  coordxy, coordxy),
+                                      coordxy, coordxy);
 extern void rescham(void);
 extern void restartcham(void);
 extern void restore_cham(struct monst *);
index 7bdd4849592e9ee883836b99f441ee73cce4295b..a51dc9db504a44d63dceb9eeaf0dda9e550a8d60 100644 (file)
@@ -719,7 +719,8 @@ engulf_target(struct monst *magr, struct monst *mdef)
     int dx, dy;
 
     /* can't swallow something that's too big */
-    if (mdef->data->msize >= MZ_HUGE)
+    if (mdef->data->msize >= MZ_HUGE
+        || (magr->data->msize < mdef->data->msize && !is_whirly(magr->data)))
         return FALSE;
 
     /* can't (move to) swallow if trapped. TODO: could do some? */
index 3e9a13b7ade80be7f4ce9a4435514e4a0208d5e9..d7f3ced28ea290a3e7299f129e8c0272f3ce04d0 100644 (file)
@@ -1151,16 +1151,17 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
         place_monster(mtmp, u.ux, u.uy);
         set_ustuck(mtmp);
         newsym(mtmp->mx, mtmp->my);
-        if (is_animal(mtmp->data) && u.usteed) {
+        /* 3.7: dismount for all engulfers, not just for purple worms */
+        if (u.usteed) {
             char buf[BUFSZ];
 
-            /* Too many quirks presently if hero and steed
-             * are swallowed. Pretend purple worms don't
-             * like horses for now :-)
-             */
             Strcpy(buf, mon_nam(u.usteed));
-            urgent_pline("%s lunges forward and plucks you off %s!",
-                         Monnam(mtmp), buf);
+            urgent_pline("%s %s forward and plucks you off %s!",
+                         Some_Monnam(mtmp),
+                         is_animal(mtmp->data) ? "lunges"
+                           : amorphous(mtmp->data) ? "oozes"
+                             : "surges",
+                         buf);
             dismount_steed(DISMOUNT_ENGULFED);
         } else {
             urgent_pline("%s engulfs you!", Monnam(mtmp));
@@ -1184,7 +1185,11 @@ gulpmu(struct monst *mtmp, struct attack *mattk)
 
         if (touch_petrifies(g.youmonst.data) && !resists_ston(mtmp)) {
             /* put the attacker back where it started;
-               the resulting statue will end up there */
+               the resulting statue will end up there
+               [note: if poly'd hero could ride or non-poly'd hero could
+               acquire touch_petrifies() capability somehow, this code
+               would need to deal with possibility of steed having taken
+               engulfer's previous spot when hero was forcibly dismounted] */
             remove_monster(mtmp->mx, mtmp->my); /* u.ux,u.uy */
             place_monster(mtmp, omx, omy);
             minstapetrify(mtmp, TRUE);
index 2e253a7d59c97e066502df1caa234519b9f4f9f5..dd4c9a0aff8c9047649d96f232d63e10436aaaab 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -20,7 +20,6 @@ static void m_detach(struct monst *, struct permonst *);
 static void set_mon_min_mhpmax(struct monst *, int);
 static void lifesaved_monster(struct monst *);
 static boolean ok_to_obliterate(struct monst *);
-static void deal_with_overcrowding(struct monst *);
 static void m_restartcham(struct monst *);
 static boolean restrap(struct monst *);
 static int pick_animal(void);
@@ -3444,7 +3443,7 @@ elemental_clog(struct monst *mon)
 /* make monster mtmp next to you (if possible);
    might place monst on far side of a wall or boulder */
 void
-mnexto(struct monstmtmp, unsigned int rlocflags)
+mnexto(struct monst *mtmp, unsigned int rlocflags)
 {
     coord mm;
 
@@ -3463,8 +3462,8 @@ mnexto(struct monst* mtmp, unsigned int rlocflags)
     return;
 }
 
-static void
-deal_with_overcrowding(struct monstmtmp)
+void
+deal_with_overcrowding(struct monst *mtmp)
 {
     if (In_endgame(&u.uz)) {
         debugpline1("overcrowding: elemental_clog on %s", m_monnam(mtmp));
@@ -3477,7 +3476,7 @@ deal_with_overcrowding(struct monst* mtmp)
 
 /* like mnexto() but requires destination to be directly accessible */
 void
-maybe_mnexto(struct monstmtmp)
+maybe_mnexto(struct monst *mtmp)
 {
     coord mm;
     struct permonst *ptr = mtmp->data;
index 2a280f806e31779203185026889d7c90b0305453..9608fdb52be4a546edaefcbdf88a7d48a30f53ac 100644 (file)
@@ -218,9 +218,10 @@ onscary(coordxy x, coordxy y, struct monst* mtmp)
 
 /* regenerate lost hit points */
 void
-mon_regen(struct monstmon, boolean digest_meal)
+mon_regen(struct monst *mon, boolean digest_meal)
 {
-    if (mon->mhp < mon->mhpmax && (g.moves % 20 == 0 || regenerates(mon->data)))
+    if (mon->mhp < mon->mhpmax
+        && (g.moves % 20 == 0 || regenerates(mon->data)))
         mon->mhp++;
     if (mon->mspec_used)
         mon->mspec_used--;
@@ -238,7 +239,7 @@ mon_regen(struct monst* mon, boolean digest_meal)
  * jolted awake.
  */
 static int
-disturb(register struct monstmtmp)
+disturb(register struct monst *mtmp)
 {
     /*
      * + Ettins are hard to surprise.
@@ -273,7 +274,7 @@ disturb(register struct monst* mtmp)
 
 /* ungrab/expel held/swallowed hero */
 static void
-release_hero(struct monstmon)
+release_hero(struct monst *mon)
 {
     if (mon == u.ustuck) {
         if (u.uswallow) {
@@ -416,7 +417,9 @@ monflee(
 }
 
 static void
-distfleeck(register struct monst* mtmp, int* inrange, int* nearby, int* scared)
+distfleeck(
+    struct monst *mtmp,
+    int *inrange, int *nearby, int *scared) /* output */
 {
     int seescaryx, seescaryy;
     boolean sawscary = FALSE, bravegremlin = (rn2(5) == 0);
@@ -1123,12 +1126,22 @@ m_move(register struct monst* mtmp, register int after)
 
     /* likewise for shopkeeper, guard, or priest */
     if (mtmp->isshk || mtmp->isgd || mtmp->ispriest) {
-        int xm = mtmp->isshk ? shk_move(mtmp) : (mtmp->isgd ? gd_move(mtmp) : pri_move(mtmp));
+        int xm = mtmp->isshk ? shk_move(mtmp)
+                 : mtmp->isgd ? gd_move(mtmp)
+                   : pri_move(mtmp);
+
         switch (xm) {
-        case -2: return MMOVE_DIED;
-        case -1: mmoved = MMOVE_NOTHING; break; /* shk follow hero outside shop */
-        case 0: mmoved = MMOVE_NOTHING; goto postmov;
-        case 1: mmoved = MMOVE_MOVED; goto postmov;
+        case -2:
+            return MMOVE_DIED;
+        case -1:
+            mmoved = MMOVE_NOTHING; /* shk follow hero outside shop */
+            break;
+        case 0:
+            mmoved = MMOVE_NOTHING;
+            goto postmov;
+        case 1:
+            mmoved = MMOVE_MOVED;
+            goto postmov;
         default: impossible("unknown shk/gd/pri_move return value (%i)", xm);
             mmoved = MMOVE_NOTHING;
             goto postmov;
@@ -1343,10 +1356,10 @@ m_move(register struct monst* mtmp, register int after)
     niy = omy;
     flag = mon_allowflags(mtmp);
     {
-        register int i, j, nx, ny, nearer;
+        int i, j, nx, ny, nearer;
         int jcnt, cnt;
         int ndist, nidist;
-        register coord *mtrk;
+        coord *mtrk;
         coord poss[9];
 
         cnt = mfndpos(mtmp, poss, info, flag);
index e9848c42e6e478f9f1ef60d464422fe0fa2e0c5e..515d3581932cb617a0ac3fc45f1996ff599a6293 100644 (file)
@@ -589,11 +589,13 @@ dismount_steed(
             (void) enexto(&steedcc, u.ux, u.uy, &mons[PM_GHOST]);
     }
     if (!m_at(steedcc.x, steedcc.y)) {
-        if (mtmp->mhp < 1)
-            mtmp->mhp = 0; /* make sure it isn't negative */
-        mtmp->mhp++; /* force at least one hit point, possibly resurrecting */
+        if (mtmp->mhp < 1) /* make sure it isn't negative so that */
+            mtmp->mhp = 0; /* ++mhp produces a positive value     */
+        mtmp->mhp++; /* force at least one hit point, possibly resurrecting
+                      * to avoid impossible("placing defunct monst on map") */
         place_monster(mtmp, steedcc.x, steedcc.y);
-        mtmp->mhp--; /* take the extra hit point away: cancel resurrection */
+        mtmp->mhp--; /* take the extra hit point away: cancel resurrection
+                      * if former steed has died */
     } else {
         impossible("Dismounting: can't place former steed on map.");
     }
@@ -610,7 +612,9 @@ dismount_steed(
             return;
         }
 
-        /* Set hero's and/or steed's positions.  Try moving the hero first. */
+        /* Set hero's and/or steed's positions.  Usually try moving the
+           hero first.  Note: for DISMOUNT_ENGULFED, caller hasn't set
+           u.uswallow yet but has set u.ustuck. */
         if (!u.uswallow && !u.ustuck && have_spot) {
             struct permonst *mdat = mtmp->data;
 
@@ -673,8 +677,9 @@ dismount_steed(
             rloc_to(mtmp, cc.x, cc.y);
             /* Player stays put */
 
-        /* Otherwise, kill the steed. */
+        /* Otherwise, steed goes bye-bye. */
         } else {
+#if 1       /* original there's-no-room handling */
             if (reason == DISMOUNT_BYCHOICE) {
                 /* [un]#ride: hero gets credit/blame for killing steed */
                 killed(mtmp);
@@ -684,6 +689,18 @@ dismount_steed(
                    damage type is just "neither AD_DGST nor -AD_RBRE" */
                 monkilled(mtmp, "", -AD_PHYS);
             }
+#else
+            /* Can't use this [yet?] because it violates monmove()'s
+             * assumption that a moving monster (engulfer) can't cause
+             * another monster (steed) to be removed from the fmon list.
+             * That other monster (steed) might be cached as the next one
+             * to move.
+             */
+            /* migrate back to this level if hero leaves and returns
+               or to next level if it is happening in the endgame */
+            mdrop_special_objs(mtmp);
+            deal_with_overcrowding(mtmp);
+#endif
         }
     } /* !DEADMONST(mtmp) */