vampshifters vs closed doors

If a vampire in fog cloud form moves under a closed door and then
before moving further gets killed and revives in vampire form,
destroy the door instead of moving the vampire to a nearby open
spot (which might be a distant spot if the map is crowded).  If the
door is trapped, explode the trap.  That will damage the vampire but
usually not by enough to kill it.

This probably ought to be generalized to be done for any shape change
at a closed location but I ran out of gas.
This commit is contained in:
PatR
2024-11-26 22:15:41 -08:00
parent 6a6378d886
commit 1fe23ff4d9
2 changed files with 57 additions and 17 deletions

View File

@@ -1937,7 +1937,7 @@ can_touch_safely(struct monst *mtmp, struct obj *otmp)
*
* to support the new pet behavior, this now returns the max # of objects
* that a given monster could pick up from a pile. frequently this will be
* otmp->quan, but special cases for 'only one' now exist so.
* otmp->quan, but special cases for 'only one' now exist.
*
* this will probably cause very amusing behavior with pets and gold coins.
*
@@ -2812,15 +2812,20 @@ lifesaved_monster(struct monst *mtmp)
}
/* when a shape-shifted vampire is killed, it reverts to base form instead
of dying; moved into separate routine to unclutter mondead() */
of dying; returns True if mtmp successfully revives, False otherwise;
"successfully revived" vampire might be killed by a booby trapped door */
staticfn boolean
vamprises(struct monst *mtmp)
{
int mndx = mtmp->cham;
/*
* Protection from shape changers protects against this because
* the vampire will always be in normal form instead of shifted.
* So there's no need to check for that attribute being active.
*/
if (ismnum(mndx) && mndx != monsndx(mtmp->data)
&& !(svm.mvitals[mndx].mvflags & G_GENOD)) {
coord new_xy;
char action[BUFSZ];
/* alternate message phrasing for some monster types */
boolean spec_mon = (nonliving(mtmp->data)
@@ -2833,30 +2838,28 @@ vamprises(struct monst *mtmp)
/* construct a 'before' argument to pass to pline(); this used
to construct a dynamic format string but that's overkill */
Snprintf(action, sizeof action, "%s suddenly %s and rises as",
Snprintf(action, sizeof action, "%s%s %s%s and rises as",
Unaware ? "you dream that " : "",
x_monnam(mtmp, ARTICLE_THE,
spec_mon ? (char *) 0 : "seemingly dead",
(SUPPRESS_INVISIBLE | AUGMENT_IT), FALSE),
Unaware ? "" : "suddenly ",
spec_death ? "reconstitutes" : "transforms");
mtmp->mcanmove = 1;
mtmp->mfrozen = 0;
set_mon_min_mhpmax(mtmp, 10); /* mtmp->mhpmax=max(m_lev+1,10) */
mtmp->mhp = mtmp->mhpmax;
/* mtmp==u.ustuck can happen if previously a fog cloud
or poly'd hero is hugging a vampire bat */
/* mtmp==u.ustuck can happen if previously a fog cloud or if
poly'd hero is hugging a vampire bat */
if (mtmp == u.ustuck) {
if (u.uswallow)
expels(mtmp, mtmp->data, FALSE);
else
uunstick();
}
/* if fog cloud is on a closed door space, move it to a more
appropriate spot for its intended new form */
if (amorphous(mtmp->data) && closed_door(mtmp->mx, mtmp->my)) {
if (enexto(&new_xy, mtmp->mx, mtmp->my, &mons[mndx]))
rloc_to(mtmp, new_xy.x, new_xy.y);
}
(void) newcham(mtmp, &mons[mndx], NO_NC_FLAGS);
if (!newcham(mtmp, &mons[mndx], NO_NC_FLAGS))
return !DEADMONSTER(mtmp);
mtmp->cham = (mtmp->data == &mons[mndx]) ? NON_PM : mndx;
if (canspotmon(mtmp)) {
@@ -2869,6 +2872,42 @@ vamprises(struct monst *mtmp)
FALSE));
gv.vamp_rise_msg = TRUE;
}
/* revived vampire is in normal shape, so can't be amorphous; if on
a closed door spot, destroy the door and if trapped, blow it up */
if (closed_door(x, y)) {
static const char
door_smashed[] = "a door being smashed",
door_go_boom[] = "a door exploding";
struct rm *door = &levl[x][y];
boolean trapped = (door->doormask & D_TRAPPED) != 0,
seeit = cansee(x, y);
set_msg_xy(x, y); /* You()/pline() will reset this */
if (!seeit)
You_hear("%s.", trapped ? "an explosion" : door_smashed);
else if (!canspotmon(mtmp))
You_see("%s.", trapped ? door_go_boom : door_smashed);
else if (!Unaware)
pline_The("door is smashed%s",
trapped ? " and it explodes!" : ".");
set_msg_xy(0, 0); /* in case none of the messages was delivered */
door->doormask = D_NODOOR;
unblock_point(x, y);
if (trapped) {
boolean trap_killed, save_verbose = flags.verbose;
flags.verbose = FALSE; /* suppress mb_trapped() messages
* (that makes the 'seeit' arg moot) */
trap_killed = mb_trapped(mtmp, seeit);
flags.verbose = save_verbose;
/* if the booby trap has killed the monster, mondied() will
have been called but no message about its death given yet;
mtmp was a vampire so use unconditional "destroyed" */
if (trap_killed && canspotmon(mtmp) && !Unaware)
pline_mon(mtmp, "%s is destroyed!", Monnam(mtmp));
}
}
newsym(x, y);
return TRUE;
}
@@ -4593,6 +4632,7 @@ hideunder(struct monst *mtmp)
if (undetected && seenmon && seenobj) {
if (!locomo)
locomo = locomotion(mtmp->data, "hide");
set_msg_xy(mtmp->mx, mtmp->my); /* pline() will reset this */
You_see("%s %s under %s.", seenmon, locomo, seenobj);
iflags.last_msg = PLNMSG_HIDE_UNDER;
gl.last_hider = mtmp->m_id;

View File

@@ -1491,9 +1491,9 @@ postmov(
&& amorphous(ptr)) {
if (flags.verbose && canseemon(mtmp))
pline_mon(mtmp, "%s %s under the door.", YMonnam(mtmp),
(ptr == &mons[PM_FOG_CLOUD]
|| ptr->mlet == S_LIGHT) ? "flows" : "oozes");
} else if (here->doormask & D_LOCKED && can_unlock) {
(ptr == &mons[PM_FOG_CLOUD]
|| ptr->mlet == S_LIGHT) ? "flows" : "oozes");
} else if ((here->doormask & D_LOCKED) != 0 && can_unlock) {
/* like the vampshift hack, there are sequencing
issues when the monster is moved to the door's spot
first then door handling plus feedback comes after */
@@ -1532,7 +1532,7 @@ postmov(
}
}
}
} else if (here->doormask & (D_LOCKED | D_CLOSED)) {
} else if ((here->doormask & (D_LOCKED | D_CLOSED)) != 0) {
/* mfndpos guarantees this must be a doorbuster */
unsigned mask;