]> granicus.if.org Git - nethack/commitdiff
github issue #715 - losing stoning resistance \
authorPatR <rankin@nethack.org>
Thu, 31 Mar 2022 11:48:26 +0000 (04:48 -0700)
committerPatR <rankin@nethack.org>
Thu, 31 Mar 2022 11:48:26 +0000 (04:48 -0700)
while wielding a cockatrice corpse without gloves

Reported by vultur-cadens:  if safely wielding a cockatrice corpse
without gloves due to temporary stoning resistance or wearing yellow
dragon scales/mail, having the resistance be lost to timeout or to
taking off the dragon armor should have turned the hero to stone but
didn't.

Extend the handling for taking off gloves to cover these other two
cases too.  The feedback for these deaths is usually too verbose to
fit on the tombstone but does show up in logfile.

Fixes #715

doc/fixes3-7-0.txt
include/extern.h
src/do_wear.c
src/timeout.c

index bafbbfe096e06c5e677a3aec00e42c9d87a050e6..e6edc2760d00948650ecbf18d3a6c9e75a488a59 100644 (file)
@@ -1112,6 +1112,9 @@ dumplog's list of "major events" showed all logged events, not just major ones
 pickup via menu ignored player-specified count when picking up gold
 changes in encumbrance sometimes went unreported, leaving stale status line
        info until hero's next move
+taking off yellow dragon scales/mail or having temporary stoning resistance
+       time out while wielding a cockatrice corpse without gloves left that
+       corpse safely wielded instead of petrifying the hero
 
 curses: 'msg_window' option wasn't functional for curses unless the binary
        also included tty support
index 78036bc91d7342880453bd737cb3181f24ed9e9e..ecb0134fb0c65017cb2b3ef74145a599f3dcdd0c 100644 (file)
@@ -534,6 +534,7 @@ extern int stop_donning(struct obj *);
 extern int Armor_off(void);
 extern int Armor_gone(void);
 extern int Helmet_off(void);
+extern void wielding_corpse(struct obj *, struct obj *, boolean);
 extern int Gloves_off(void);
 extern int Boots_on(void);
 extern int Boots_off(void);
index 7f01697c7e48bbbdb02ff83b6f206f3059b65779..1df635f74823b7cadd2f51886d5765e7e164afa3 100644 (file)
@@ -27,10 +27,9 @@ static int Armor_on(void);
 static int Cloak_on(void);
 static int Helmet_on(void);
 static int Gloves_on(void);
-static void wielding_corpse(struct obj *, boolean);
 static int Shield_on(void);
 static int Shirt_on(void);
-static void dragon_armor_handling(struct obj *, boolean);
+static void dragon_armor_handling(struct obj *, boolean, boolean);
 static void Amulet_on(void);
 static void learnring(struct obj *, boolean);
 static void Ring_off_or_gone(struct obj *, boolean);
@@ -531,32 +530,50 @@ Gloves_on(void)
     return 0;
 }
 
-static void
-wielding_corpse(struct obj *obj,
-                boolean voluntary) /* taking gloves off on purpose? */
+/* check for wielding cockatrice corpse after taking off gloves or yellow
+   dragon scales/mail or having temporary stoning resistance time out */
+void
+wielding_corpse(
+    struct obj *obj,   /* uwep, potentially a wielded cockatrice corpse */
+    struct obj *how,   /* gloves or dragon armor or Null (resist timeout) */
+    boolean voluntary) /* True: taking protective armor off on purpose */
 {
-    char kbuf[BUFSZ];
-
-    if (!obj || obj->otyp != CORPSE)
+    if (!obj || obj->otyp != CORPSE || uarmg)
         return;
+    /* note: can't dual-wield with non-weapons/weapon-tools so u.twoweap
+       will always be false if uswapwep happens to be a corpse */
     if (obj != uwep && (obj != uswapwep || !u.twoweap))
         return;
 
     if (touch_petrifies(&mons[obj->corpsenm]) && !Stone_resistance) {
-        You("now wield %s in your bare %s.",
+        char kbuf[BUFSZ], hbuf[BUFSZ];
+
+        You("%s %s in your bare %s.",
+            (how && is_gloves(how)) ? "now wield" : "are wielding",
             corpse_xname(obj, (const char *) 0, CXN_ARTICLE),
             makeplural(body_part(HAND)));
-        Sprintf(kbuf, "%s gloves while wielding %s",
-                voluntary ? "removing" : "losing", killer_xname(obj));
+        /* "removing" ought to be "taking off" but that makes the
+           tombstone text more likely to be truncated */
+        if (how)
+            Sprintf(hbuf, "%s %s", voluntary ? "removing" : "losing",
+                    is_gloves(how) ? gloves_simple_name(how)
+                    : strsubst(simpleonames(how), "set of ", ""));
+        else
+            Strcpy(hbuf, "resistance timing out");
+        Snprintf(kbuf, sizeof kbuf, "%s while wielding %s",
+                 hbuf, killer_xname(obj));
         instapetrify(kbuf);
-        /* life-saved; can't continue wielding cockatrice corpse though */
-        remove_worn_item(obj, FALSE);
+        /* life-saved or got poly'd into a stone golem; can't continue
+           wielding cockatrice corpse unless have now become resistant */
+        if (!Stone_resistance)
+            remove_worn_item(obj, FALSE);
     }
 }
 
 int
 Gloves_off(void)
 {
+    struct obj *gloves = uarmg; /* needed after uarmg has been set to Null */
     long oldprop =
         u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
     boolean on_purpose = !g.context.mon_moving && !uarmg->in_use;
@@ -595,13 +612,17 @@ Gloves_off(void)
 
     /* prevent wielding cockatrice when not wearing gloves */
     if (uwep && uwep->otyp == CORPSE)
-        wielding_corpse(uwep, on_purpose);
-
+        wielding_corpse(uwep, gloves, on_purpose);
     /* KMH -- ...or your secondary weapon when you're wielding it
-       [This case can't actually happen; twoweapon mode won't
-       engage if a corpse has been set up as the alternate weapon.] */
+       [This case can't actually happen; twoweapon mode won't engage
+       if a corpse has been set up as either the primary or alternate
+       weapon.  If it could happen and /both/ uwep and uswapwep could
+       be cockatrice corpses, life-saving for the first would need to
+       prevent the second from being fatal since conceptually they'd
+       be being touched simultaneously.] */
     if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE)
-        wielding_corpse(uswapwep, on_purpose);
+        wielding_corpse(uswapwep, gloves, on_purpose);
+
     if (condtests[bl_bareh].enabled)
         g.context.botl = 1;
 
@@ -627,7 +648,7 @@ Shield_on(void)
     default:
         impossible(unknown_type, c_shield, uarms->otyp);
     }
-    if (uarms) /* no known instance of !uarmgs here but play it safe */
+    if (uarms) /* no known instance of !uarms here but play it safe */
         uarms->known = 1; /* shield's +/- evident because of status line AC */
     return 0;
 }
@@ -694,7 +715,10 @@ Shirt_off(void)
 
 /* handle extra abilities for hero wearing dragon scale armor */
 static void
-dragon_armor_handling(struct obj *otmp, boolean puton)
+dragon_armor_handling(
+    struct obj *otmp,   /* armor being put on or taken off */
+    boolean puton,      /* True: on, False: off */
+    boolean on_purpose) /* voluntary removal; not applicable for putting on */
 {
     if (!otmp)
         return;
@@ -759,6 +783,11 @@ dragon_armor_handling(struct obj *otmp, boolean puton)
             EStone_resistance |= W_ARM;
         } else {
             EStone_resistance &= ~W_ARM;
+
+            /* prevent wielding cockatrice after losing stoning resistance
+               when not wearing gloves; the uswapwep case is always a no-op */
+            wielding_corpse(uwep, otmp, on_purpose);
+            wielding_corpse(uswapwep, otmp, on_purpose);
         }
         break;
     case WHITE_DRAGON_SCALES:
@@ -781,11 +810,9 @@ Armor_on(void)
         return 0;
     uarm->known = 1; /* suit's +/- evident because of status line AC */
 
-    dragon_armor_handling(uarm, TRUE);
-
-    /*
-     * Gold DSM requires special handling since it emits light when worn.
-     */
+    dragon_armor_handling(uarm, TRUE, TRUE);
+    /* gold DSM requires special handling since it emits light when worn;
+       do that after the special armor handling */
     if (artifact_light(uarm) && !uarm->lamplit) {
         begin_burn(uarm, FALSE);
         if (!Blind)
@@ -806,13 +833,17 @@ Armor_off(void)
     setworn((struct obj *) 0, W_ARM);
     g.context.takeoff.cancelled_don = FALSE;
 
-    dragon_armor_handling(otmp, FALSE);
-
+    /* taking off yellow dragon scales/mail might be fatal; arti_light
+       comes from gold dragon scales/mail so they don't overlap, but
+       conceptually the non-fatal change should be done before the
+       potentially fatal change in case the latter results in bones */
     if (was_arti_light && !artifact_light(otmp)) {
         end_burn(otmp, FALSE);
         if (!Blind)
             pline("%s shining.", Tobjnam(otmp, "stop"));
     }
+    dragon_armor_handling(otmp, FALSE, TRUE);
+
     return 0;
 }
 
@@ -832,13 +863,17 @@ Armor_gone(void)
     setnotworn(uarm);
     g.context.takeoff.cancelled_don = FALSE;
 
-    dragon_armor_handling(otmp, FALSE);
-
+    /* losing yellow dragon scales/mail might be fatal; arti_light
+       comes from gold dragon scales/mail so they don't overlap, but
+       conceptually the non-fatal change should be done before the
+       potentially fatal change in case the latter results in bones */
     if (was_arti_light && !artifact_light(otmp)) {
         end_burn(otmp, FALSE);
         if (!Blind)
             pline("%s shining.", Tobjnam(otmp, "stop"));
     }
+    dragon_armor_handling(otmp, FALSE, FALSE);
+
     return 0;
 }
 
index 561208a79647e9499c2393cf83d60e8b876b901b..c49c5aca8911e23d08de1a41a888440aa1f99612 100644 (file)
@@ -748,8 +748,14 @@ nh_timeout(void)
                     You("no longer feel safe from acid.");
                 break;
             case STONE_RES:
-                if (!Stone_resistance && !Unaware)
-                    You("no longer feel secure from petrification.");
+                if (!Stone_resistance) {
+                    if (!Unaware)
+                        You("no longer feel secure from petrification.");
+                    /* no-op if not wielding a cockatrice corpse;
+                       uswapwep case is always a no-op (see Gloves_off()) */
+                    wielding_corpse(uwep, (struct obj *) 0, FALSE);
+                    wielding_corpse(uswapwep, (struct obj *) 0, FALSE);
+                }
                 break;
             case DISPLACED:
                 if (!Displaced) /* give a message */