stone-to-flesh vs mimics

Handle a FIXME in zap.c:  stone-to-flesh spell hitting a mimic that
is disguised as a stone object or stone furniture should bring it out
of hiding.
This commit is contained in:
PatR
2025-04-24 12:47:00 -07:00
parent c6906a78a4
commit 92255708f3
5 changed files with 89 additions and 31 deletions

View File

@@ -1624,6 +1624,8 @@ extern void costly_alteration(struct obj *, int) NONNULLARG1;
extern void clear_dknown(struct obj *);
extern void unknow_object(struct obj *);
extern struct obj *mksobj(int, boolean, boolean) NONNULL;
extern boolean stone_object_type(unsigned);
extern boolean stone_furniture_type(unsigned);
extern int bcsign(struct obj *) NONNULLARG1;
extern int weight(struct obj *) NONNULLARG1;
extern struct obj *mkgold(long, coordxy, coordxy);
@@ -3385,7 +3387,7 @@ extern boolean mhitm_knockback(struct monst *, struct monst *,struct attack *,
extern int passive(struct monst *, struct obj *, boolean, boolean, uchar,
boolean) NONNULLARG1;
extern void passive_obj(struct monst *, struct obj *, struct attack *) NONNULLARG1;
extern void that_is_a_mimic(struct monst *, boolean) NONNULLARG1;
extern void that_is_a_mimic(struct monst *, unsigned) NONNULLARG1;
extern void stumble_onto_mimic(struct monst *) NONNULLARG1;
extern int flash_hits_mon(struct monst *, struct obj *) NONNULLARG12;
extern void light_hits_gremlin(struct monst *, int) NONNULLARG1;

View File

@@ -1155,6 +1155,10 @@ typedef uint32_t mmflags_nht; /* makemon MM_ flags */
#define MHID_ALTMON 4 /* if mimicking a monster, include that */
#define MHID_REGION 8 /* include region when mon is in one */
/* flags for that_is_a_mimic() */
#define MIM_REVEAL 1 /* seemimic() */
#define MIM_OMIT_WAIT 2 /* strip beginning from "Wait! That is a <foo>" */
/* flags for make_corpse() and mkcorpstat(); 0..7 are recorded in obj->spe */
#define CORPSTAT_NONE 0x00
#define CORPSTAT_GENDER 0x03 /* 0x01 | 0x02 */

View File

@@ -1251,6 +1251,42 @@ mksobj(int otyp, boolean init, boolean artif)
return otmp;
}
/* potential mimic shapes that should be undone by stone-to-flesh;
not used for objects that will be transformed when hit by stone-to-flesh */
boolean
stone_object_type(unsigned mappearance)
{
int otyp = (int) mappearance;
/* we exclude wands, rings, and gems even though some qualify as stone;
there aren't any weapons or armor classified as made out of stone */
return (otyp == BOULDER || otyp == STATUE || otyp == FIGURINE);
}
/* possible mimic shapes that are affected by stone-to-flesh;
mappearance for furniture is a display symbol rather than a terrain type */
boolean
stone_furniture_type(unsigned mappearance)
{
int sym = (int) mappearance;
switch (sym) {
case S_upstair:
case S_dnstair:
case S_brupstair:
case S_brdnstair:
case S_altar:
case S_throne:
case S_sink: /* stone sink is iffy; metal might be more appropriate */
return TRUE;
default:
if (sym >= S_vwall && sym <= S_trwall)
return TRUE;
break;
}
return FALSE;
}
/*
* Several areas of the code made direct reassignments
* to obj->corpsenm. Because some special handling is

View File

@@ -6103,11 +6103,13 @@ DISABLE_WARNING_FORMAT_NONLITERAL
void
that_is_a_mimic(
struct monst *mtmp, /* a hidden mimic (nonnull) */
boolean reveal_it) /* True: remove its disguise */
unsigned mimic_flags) /* 0, MIM_REVEAL, MIM_OMIT_WAIT, REVEAL+OMIT */
{
static char generic[] = "a monster";
char fmtbuf[BUFSZ];
const char *what = NULL;
boolean reveal_it = (mimic_flags & MIM_REVEAL) != 0,
omit_wait = (mimic_flags & MIM_OMIT_WAIT) != 0;
Strcpy(fmtbuf, "Wait! That's %s!");
if (Blind) {
@@ -6116,7 +6118,7 @@ that_is_a_mimic(
else if (M_AP_TYPE(mtmp) == M_AP_MONSTER)
what = a_monnam(mtmp); /* differs from what was sensed */
} else {
coordxy x = u.ux + u.dx, y = u.uy + u.dy;
coordxy x = mtmp->mx, y = mtmp->my;
int glyph = glyph_at(x, y);
if (glyph_is_cmap(glyph)) {
@@ -6167,8 +6169,11 @@ that_is_a_mimic(
what = a_monnam(mtmp);
}
if (what)
pline(fmtbuf, what);
if (what) {
int i = (omit_wait && !strncmp(fmtbuf, "Wait! ", 7)) ? 7 : 0;
pline(&fmtbuf[i], what);
}
if (reveal_it)
seemimic(mtmp);
}
@@ -6179,7 +6184,7 @@ RESTORE_WARNING_FORMAT_NONLITERAL
void
stumble_onto_mimic(struct monst *mtmp)
{
that_is_a_mimic(mtmp, TRUE);
that_is_a_mimic(mtmp, MIM_REVEAL);
if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK)
/* must be adjacent; attack via polearm could be from farther away */

View File

@@ -365,7 +365,7 @@ bhitm(struct monst *mtmp, struct obj *otmp)
case WAN_LOCKING:
case SPE_WIZARD_LOCK:
if (disguised_mimic && box_or_door(mtmp))
that_is_a_mimic(mtmp, TRUE); /*seemimic()*/
that_is_a_mimic(mtmp, MIM_REVEAL); /*seemimic()*/
wake = closeholdingtrap(mtmp, &learn_it);
break;
case WAN_PROBING:
@@ -377,7 +377,7 @@ bhitm(struct monst *mtmp, struct obj *otmp)
case WAN_OPENING:
case SPE_KNOCK:
if (disguised_mimic && box_or_door(mtmp))
that_is_a_mimic(mtmp, TRUE); /*seemimic()*/
that_is_a_mimic(mtmp, MIM_REVEAL); /*seemimic()*/
wake = FALSE; /* don't want immediate counterattack */
if (mtmp == u.ustuck) {
/* zapping either holder/holdee or self [zapyourself()] will
@@ -483,21 +483,35 @@ bhitm(struct monst *mtmp, struct obj *otmp)
learn_it = TRUE;
break;
case SPE_STONE_TO_FLESH:
/* FIXME: mimics disguished as stone furniture or stone object
should be taken out of concealment. */
if (monsndx(mtmp->data) == PM_STONE_GOLEM) {
char *name = Monnam(mtmp);
if (mtmp->data->mlet == S_GOLEM) {
const char *mesg;
char *name = Monnam(mtmp); /* before possible polymorph */
/* turn into flesh golem */
if (newcham(mtmp, &mons[PM_FLESH_GOLEM], NO_NC_FLAGS)) {
if (canseemon(mtmp))
pline("%s turns to flesh!", name);
} else {
if (canseemon(mtmp))
pline("%s looks rather fleshy for a moment.", name);
/* turn stone golem into flesh golem */
if (monsndx(mtmp->data) == PM_STONE_GOLEM
&& newcham(mtmp, &mons[PM_FLESH_GOLEM], NO_NC_FLAGS))
mesg = "turns to flesh!";
else if (monsndx(mtmp->data) == PM_FLESH_GOLEM)
mesg = "seems fleshier...";
else
mesg = "looks rather fleshy for a moment.";
if (canseemon(mtmp))
pline("%s %s", name, mesg);
} else if (mtmp->data->mlet == S_MIMIC
&& ((M_AP_TYPE(mtmp) == M_AP_FURNITURE
&& stone_furniture_type(mtmp->mappearance))
|| (M_AP_TYPE(mtmp) == M_AP_OBJECT
&& stone_object_type(mtmp->mappearance)))) {
/* note: if that_is_a_mimic() doesn't get called to reveal the
mimic, wakeup() below will call seemimic() */
if (cansee(mtmp->mx, mtmp->my)) {
set_msg_xy(mtmp->mx, mtmp->my);
that_is_a_mimic(mtmp, MIM_REVEAL | MIM_OMIT_WAIT);
}
} else
} else {
wake = FALSE;
}
break;
case SPE_DRAIN_LIFE:
if (disguised_mimic)
@@ -530,22 +544,19 @@ bhitm(struct monst *mtmp, struct obj *otmp)
impossible("What an interesting effect (%d)", otyp);
break;
}
if (wake) {
if (!DEADMONSTER(mtmp)) {
wakeup(mtmp, helpful_gesture ? FALSE : TRUE);
m_respond(mtmp);
if (mtmp->isshk && !*u.ushops)
hot_pursuit(mtmp);
} else if (M_AP_TYPE(mtmp))
seemimic(mtmp); /* might unblock if mimicking a boulder/door */
if (wake && !DEADMONSTER(mtmp)) {
/* seemimic() is done by wakeup() and might unblock vision */
wakeup(mtmp, helpful_gesture ? FALSE : TRUE);
m_respond(mtmp);
if (mtmp->isshk && !*u.ushops)
hot_pursuit(mtmp);
}
/* note: gb.bhitpos won't be set if swallowed, but that's okay since
* reveal_invis will be false. We can't use mtmp->mx, my since it
* might be an invisible worm hit on the tail.
*/
if (reveal_invis) {
if (!DEADMONSTER(mtmp) && cansee(gb.bhitpos.x, gb.bhitpos.y)
&& !canspotmon(mtmp))
if (reveal_invis && !DEADMONSTER(mtmp)) {
if (cansee(gb.bhitpos.x, gb.bhitpos.y) && !canspotmon(mtmp))
map_invisible(gb.bhitpos.x, gb.bhitpos.y);
}
/* if effect was observable then discover the wand type provided