From 1fe23ff4d965ed9c028f47ced3260c798baa65ee Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 26 Nov 2024 22:15:41 -0800 Subject: [PATCH] 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. --- src/mon.c | 66 +++++++++++++++++++++++++++++++++++++++++---------- src/monmove.c | 8 +++---- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/mon.c b/src/mon.c index e3879d54b..bbfe06f4a 100644 --- a/src/mon.c +++ b/src/mon.c @@ -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; diff --git a/src/monmove.c b/src/monmove.c index 7705a3e1a..0b5cde2e1 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -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;