From 695c6ef3ac6b73e2dacbc8f3224232338423efdc Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 5 Aug 2025 13:30:17 -0700 Subject: [PATCH] fix issue #1434 - engulfed gas spore explosion Issue reported by Umbire: a gas spore that got swallowed and killed didn't die but exploded anyway, with the explosion affecting the map instead of being contained in the swallower. There was code to handle that but it wasn't being executed. This fix feels unclean but seems to work. I couldn't reproduce the survival of the gas spore but since that isn't wanted I won't worry about it. Fixes #1434 --- doc/fixes3-7-0.txt | 2 ++ include/decl.h | 3 +++ src/decl.c | 2 ++ src/mhitm.c | 2 ++ src/mhitu.c | 4 +++- src/mon.c | 32 ++++++++++++++++++++++++-------- src/uhitm.c | 2 ++ 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 8a37c1f5c..d19a70a6e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1523,6 +1523,8 @@ monsters trapped in pits cannot kick create familiar spell can create harder creatures a vampire lord could choose to take on wolf form while flying over water or lava, then revert to vampire lord form and teleport unnecessarily +a gas spore that was killed when an engulfer swallowed it produced an explosion + on the map rather than inside the engulfer Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/include/decl.h b/include/decl.h index d5186158b..72f85eb90 100644 --- a/include/decl.h +++ b/include/decl.h @@ -604,6 +604,9 @@ struct instance_globals_m { /* dokick.c */ struct rm *maploc; + /* mhitm.c */ + struct monst *mswallower; /* for gas spore explosion when it's swallowed*/ + /* mhitu.c */ int mhitu_dieroll; diff --git a/src/decl.c b/src/decl.c index 7d0a47cb4..a7e52dd80 100644 --- a/src/decl.c +++ b/src/decl.c @@ -508,6 +508,8 @@ static const struct instance_globals_m g_init_m = { UNDEFINED_PTR, /* migrating_mons */ /* dokick.c */ UNDEFINED_PTR, /* maploc */ + /* mhitm.c */ + UNDEFINED_PTR, /* mswallower */ /* mhitu.c */ UNDEFINED_VALUE, /* mhitu_dieroll */ /* mklev.c */ diff --git a/src/mhitm.c b/src/mhitm.c index 078b7596c..322b65b22 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -906,7 +906,9 @@ gulpmm( newsym(ax, ay); /* erase old position */ newsym(dx, dy); /* update new position */ + gm.mswallower = magr; /* corpse_chance() wants this */ status = mdamagem(magr, mdef, mattk, (struct obj *) 0, 0); + gm.mswallower = (struct monst *) 0; /* reset */ if ((status & (M_ATTK_AGR_DIED | M_ATTK_DEF_DIED)) == (M_ATTK_AGR_DIED | M_ATTK_DEF_DIED)) { diff --git a/src/mhitu.c b/src/mhitu.c index ff32445e8..d6332c18d 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1271,7 +1271,7 @@ gulp_blnd_check(void) return FALSE; } -/* monster swallows you, or damage if u.uswallow */ +/* monster swallows you, or damage if already swallowed (u.uswallow != 0) */ staticfn int gulpmu(struct monst *mtmp, struct attack *mattk) { @@ -1537,7 +1537,9 @@ gulpmu(struct monst *mtmp, struct attack *mattk) if (physical_damage) tmp = Maybe_Half_Phys(tmp); + gm.mswallower = mtmp; /* match gulpmm() */ mdamageu(mtmp, tmp); + gm.mswallower = 0; if (tmp) stop_occupation(); diff --git a/src/mon.c b/src/mon.c index 40a005440..dce75521c 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2204,8 +2204,7 @@ mfndpos( if (IS_DOOR(ntyp) /* an amorphous creature can only move under/through a closed door if it doesn't currently have hero engulfed */ - && !((amorphous(mdat) || can_fog(mon)) - && (mon != u.ustuck || !u.uswallow)) + && !((amorphous(mdat) || can_fog(mon)) && !engulfing_u(mon)) && (((levl[nx][ny].doormask & D_CLOSED) && !(flag & OPENDOOR)) || ((levl[nx][ny].doormask & D_LOCKED) && !(flag & UNLOCKDOOR))) && !thrudoor) @@ -2670,11 +2669,25 @@ mon_leaving_level(struct monst *mon) remove_monster(mx, my); #if 0 /* mustn't do this; too many places assume that the stale - monst->mx,my values are still valid */ + * monst->mx,my values are still valid */ mon->mx = mon->my = 0; /* off normal map */ #endif } if (onmap) { + /* gulpmm() tries to deal with this, but without this extra + place_monster() the messages for exploding engulfed gas spore + are delivered without the engulfer being shown on the map */ + if (gm.mswallower && gm.mswallower != mon) { + if (gm.mswallower != &gy.youmonst) { + place_monster(gm.mswallower, + gm.mswallower->mx, gm.mswallower->my); + } else { + u_on_newpos(u.ux, u.uy); + if (canspotself()) + display_self(); + } + } + mon->mundetected = 0; /* for migration; doesn't matter for death */ /* unhide mimic in case its shape has been blocking line of sight or it is accompanying the hero to another level */ @@ -3146,6 +3159,9 @@ corpse_chance( struct permonst *mdat = mon->data; int i, tmp; + if (!magr && gm.mswallower && attacktype(gm.mswallower->data, AT_ENGL)) + magr = gm.mswallower, was_swallowed = TRUE; /* for gas spore boom */ + if (mdat == &mons[PM_VLAD_THE_IMPALER] || mdat->mlet == S_LICH) { if (cansee(mon->mx, mon->my) && !was_swallowed) pline_mon(mon, "%s body crumbles into dust.", @@ -3162,7 +3178,10 @@ corpse_chance( tmp = d((int) mdat->mlevel + 1, (int) mdat->mattk[i].damd); else tmp = 0; + if (was_swallowed && magr) { + /* mdef is a gas spore (AT_BOOM) that is exploding inside an + engulfer; suppress usual explosion since it's contained */ if (magr == &gy.youmonst) { There("is an explosion in your %s!", body_part(STOMACH)); Sprintf(svk.killer.name, "%s explosion", @@ -3176,14 +3195,11 @@ corpse_chance( mondied(magr); if (DEADMONSTER(magr)) { /* maybe lifesaved */ if (canspotmon(magr)) - pline_mon(magr, "%s rips open!", - Monnam(magr)); + pline_mon(magr, "%s rips open!", Monnam(magr)); } else if (canseemon(magr)) - pline_mon(magr, - "%s seems to have indigestion.", + pline_mon(magr, "%s seems to have indigestion.", Monnam(magr)); } - return FALSE; } diff --git a/src/uhitm.c b/src/uhitm.c index cb85c63e3..cbf797e8e 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -5005,6 +5005,7 @@ gulpum(struct monst *mdef, struct attack *mattk) "you totally digest " will be coming soon (after several turns) but the level-gain message seems out of order if the kill message is left implicit */ + gm.mswallower = &gy.youmonst; xkilled(mdef, XKILL_GIVEMSG | XKILL_NOCORPSE); if (!DEADMONSTER(mdef)) { /* monster lifesaved */ You("hurriedly regurgitate the sizzling in your %s.", @@ -5045,6 +5046,7 @@ gulpum(struct monst *mdef, struct attack *mattk) } else exercise(A_CON, TRUE); } + gm.mswallower = (struct monst *) 0; end_engulf(); return M_ATTK_DEF_DIED; case AD_PHYS: