From 149cb96020c9fb6c88e3b405f2fc5637dcc2fe11 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Nov 2024 23:30:04 -0800 Subject: [PATCH] github issue #1299 - sleeping mimics Issue reported by elunna: sleeping mimics can grab the hero, and zapping a concealed mimic with a wand of sleep describes the target as a mimic but doesn't bring it out of concealment. The grab-when-asleep case is reasonable. It's a reflexive counter- attack by a magical creature. And the mimic wakes up in the process. But the mimic wasn't being brought out of concealment. Do that. Unconceal mimics hit by wand of sleep unless already sleeping. Fixes #1299 --- src/mhitm.c | 8 +++++++- src/objnam.c | 4 ++-- src/pager.c | 13 +++++++++---- src/uhitm.c | 40 +++++++++++++++++++++++++++++++++------- src/zap.c | 38 ++++++++++++++++++++++++++------------ 5 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/mhitm.c b/src/mhitm.c index 10fa8cfc8..2473c1417 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mhitm.c $NHDT-Date: 1698939796 2023/11/02 15:43:16 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.244 $ */ +/* NetHack 3.7 mhitm.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.253 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1209,6 +1209,12 @@ paralyze_monst(struct monst *mon, int amt) int sleep_monst(struct monst *mon, int amt, int how) { + /* reveal mimic unless already asleep or paralyzed (won't be 'busy') */ + if (how >= 0 && !mon->msleeping && !mon->mfrozen + && mon->data->mlet == S_MIMIC && (M_AP_TYPE(mon) == M_AP_FURNITURE + || M_AP_TYPE(mon) == M_AP_OBJECT)) + seemimic(mon); + if (resists_sleep(mon) || defended(mon, AD_SLEE) || (how >= 0 && resist(mon, (char) how, 0, NOTELL))) { shieldeff(mon->mx, mon->my); diff --git a/src/objnam.c b/src/objnam.c index 47c9eca44..334d5b7ed 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 objnam.c $NHDT-Date: 1711809641 2024/03/30 14:40:41 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.427 $ */ +/* NetHack 3.7 objnam.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.439 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2475,7 +2475,7 @@ static const char wrpsym[] = { WAND_CLASS, RING_CLASS, POTION_CLASS, /* return form of the verb (input plural) if xname(otmp) were the subject */ char * -otense(struct obj* otmp,const char * verb) +otense(struct obj *otmp, const char *verb) { char *buf; diff --git a/src/pager.c b/src/pager.c index 74ceeccbe..079d182a7 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 pager.c $NHDT-Date: 1724094301 2024/08/19 19:05:01 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.279 $ */ +/* NetHack 3.7 pager.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.282 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -280,7 +280,10 @@ mhidden_description( /* extracted from lookat(); also used by namefloorobj() */ boolean -object_from_map(int glyph, coordxy x, coordxy y, struct obj **obj_p) +object_from_map( + int glyph, + coordxy x, coordxy y, + struct obj **obj_p) { boolean fakeobj = FALSE, mimic_obj = FALSE; struct monst *mtmp; @@ -305,8 +308,10 @@ object_from_map(int glyph, coordxy x, coordxy y, struct obj **obj_p) if (!otmp || otmp->otyp != glyphotyp) { /* this used to exclude STRANGE_OBJECT; now caller deals with it */ otmp = mksobj(glyphotyp, FALSE, FALSE); - if (!otmp) - return FALSE; + /* even though we pass False for mksobj()'s 'init' arg, corpse-rot, + egg-hatch, and figurine-transform timers get initialized */ + if (otmp->timed) + obj_stop_timers(otmp); fakeobj = TRUE; if (otmp->oclass == COIN_CLASS) otmp->quan = 2L; /* to force pluralization */ diff --git a/src/uhitm.c b/src/uhitm.c index e0a403ebb..da34794a9 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 uhitm.c $NHDT-Date: 1713334817 2024/04/17 06:20:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.444 $ */ +/* NetHack 3.7 uhitm.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.451 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6030,8 +6030,11 @@ DISABLE_WARNING_FORMAT_NONLITERAL void stumble_onto_mimic(struct monst *mtmp) { - const char *fmt = "Wait! That's %s!", *generic = "a monster", *what = 0; + static char generic[] = "a monster"; + char fmt[QBUFSZ]; + const char *what = NULL; + Strcpy(fmt, "Wait! That's %s!"); if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK) /* must be adjacent; attack via polearm could be from farther away */ && m_next2u(mtmp)) @@ -6045,16 +6048,39 @@ stumble_onto_mimic(struct monst *mtmp) } else { int glyph = levl[u.ux + u.dx][u.uy + u.dy].glyph; - if (glyph_is_cmap(glyph) && (glyph_to_cmap(glyph) == S_hcdoor - || glyph_to_cmap(glyph) == S_vcdoor)) - fmt = "The door actually was %s!"; - else if (glyph_is_object(glyph) && glyph_to_obj(glyph) == GOLD_PIECE) - fmt = "That gold was %s!"; + if (glyph_is_cmap(glyph)) { + Sprintf(fmt, "%s %s actually is %%s!", + is_cmap_stairs(glyph) ? "Those" : "That", + defsyms[mtmp->mappearance].explanation); + /* BUG: this will misclassify a paralyzed mimic as sleeping */ + what = x_monnam(mtmp, ARTICLE_A, "sleeping", 0, FALSE); + } else if (glyph_is_object(glyph)) { + boolean fakeobj; + const char *otmp_name; + struct obj *otmp = NULL; + + fakeobj = object_from_map(glyph, mtmp->mx, mtmp->my, &otmp); + otmp_name = (otmp && otmp->otyp != STRANGE_OBJECT) + ? simpleonames(otmp) : "strange object"; + Sprintf(fmt, "%s %s %s %%s!", + otmp && is_plural(otmp) ? "Those" : "That", + otmp_name, otmp ? otense(otmp, "are") : "is"); + if (fakeobj && otmp) { + otmp->where = OBJ_FREE; /* object_from_map set to OBJ_FLOOR */ + dealloc_obj(otmp); + } + } /* cloned Wiz starts out mimicking some other monster and might make himself invisible before being revealed */ if (mtmp->minvis && !See_invisible) what = generic; + else if (mtmp->data->mlet == S_MIMIC + && (M_AP_TYPE(mtmp) == M_AP_OBJECT + || M_AP_TYPE(mtmp) == M_AP_FURNITURE) + && (mtmp->msleeping || mtmp->mfrozen)) + /* BUG: this will misclassify a paralyzed mimic as sleeping */ + what = x_monnam(mtmp, ARTICLE_A, "sleeping", 0, FALSE); else what = a_monnam(mtmp); } diff --git a/src/zap.c b/src/zap.c index 2748acd77..dede87a0d 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 zap.c $NHDT-Date: 1723946858 2024/08/18 02:07:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.542 $ */ +/* NetHack 3.7 zap.c $NHDT-Date: 1732979463 2024/11/30 07:11:03 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.551 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -148,7 +148,11 @@ learnwand(struct obj *obj) } } -/* Routines for IMMEDIATE wands and spells. */ +/* + * Routines for IMMEDIATE wands and spells. + * Also RAY or NODIR for wands that are being broken rather than zapped. + */ + /* bhitm: monster mtmp was hit by the effect of wand or spell otmp */ int bhitm(struct monst *mtmp, struct obj *otmp) @@ -350,6 +354,10 @@ bhitm(struct monst *mtmp, struct obj *otmp) } case WAN_LOCKING: case SPE_WIZARD_LOCK: + /* can't use Is_box() here */ + if (disguised_mimic && (is_obj_mappear(mtmp, CHEST) + || is_obj_mappear(mtmp, LARGE_BOX))) + seemimic(mtmp); wake = closeholdingtrap(mtmp, &learn_it); break; case WAN_PROBING: @@ -360,6 +368,9 @@ bhitm(struct monst *mtmp, struct obj *otmp) break; case WAN_OPENING: case SPE_KNOCK: + if (disguised_mimic && (is_obj_mappear(mtmp, CHEST) + || is_obj_mappear(mtmp, LARGE_BOX))) + seemimic(mtmp); wake = FALSE; /* don't want immediate counterattack */ if (mtmp == u.ustuck) { /* zapping either holder/holdee or self [zapyourself()] will @@ -450,7 +461,8 @@ bhitm(struct monst *mtmp, struct obj *otmp) break; case WAN_SLEEP: /* (broken wand) */ /* [wakeup() doesn't rouse victims of temporary sleep, - so it's okay to leave `wake' set to TRUE here] */ + so it's okay to leave `wake' set to TRUE here; + revealing concealed mimic is handled by sleep_monst()] */ reveal_invis = TRUE; if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS)) slept_monst(mtmp); @@ -458,6 +470,8 @@ 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); @@ -3491,7 +3505,8 @@ exclam(int force) } void -hit(const char *str, /* zap text or missile name */ +hit( + const char *str, /* zap text or missile name */ struct monst *mtmp, /* target; for missile, might be hero */ const char *force) /* usually either "." or "!" via exclam() */ { @@ -3500,11 +3515,8 @@ hit(const char *str, /* zap text or missile name */ && (cansee(gb.bhitpos.x, gb.bhitpos.y) || canspotmon(mtmp) || engulfing_u(mtmp)))); - if (!verbosely) - pline("%s %s it.", The(str), vtense(str, "hit")); - else - pline("%s %s %s%s", The(str), vtense(str, "hit"), - mon_nam(mtmp), force); + pline("%s %s %s%s", The(str), vtense(str, "hit"), + verbosely ? mon_nam(mtmp) : "it", force); } void @@ -4209,7 +4221,8 @@ zhitm( tmp += destroy_items(mon, AD_COLD, orig_dmg); break; case ZT_SLEEP: - /* possibly resistance and shield effect handled by sleep_monst() */ + /* resistance and shield effect and revealing concealed mimic are + handled by sleep_monst() */ tmp = 0; (void) sleep_monst(mon, d(nd, 25), type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0'); @@ -4576,8 +4589,9 @@ burn_floor_objects( /* will zap/spell/breath attack score a hit against armor class `ac'? */ staticfn int -zap_hit(int ac, - int type) /* either hero cast spell type or 0 */ +zap_hit( + int ac, + int type) /* either hero cast spell type or 0 */ { int chance = rn2(20); int spell_bonus = type ? spell_hit_bonus(type) : 0;