fix issue #836 - engulfing mounter hero

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.]
This commit is contained in:
PatR
2022-08-09 16:22:50 -07:00
parent fd9745f9c6
commit e9ec89a903
6 changed files with 71 additions and 33 deletions

View 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) */