]> granicus.if.org Git - nethack/commitdiff
fix #H2589 - theft vs donning/doffing (trunk only)
authornethack.rankin <nethack.rankin>
Sat, 4 Feb 2012 08:13:26 +0000 (08:13 +0000)
committernethack.rankin <nethack.rankin>
Sat, 4 Feb 2012 08:13:26 +0000 (08:13 +0000)
     From a bug report, having some
armor stolen while in the midst of putting on armor--when both items have
a multiple turn completion delay--could result in side-effects for the
latter item being reversed even though they hadn't been applied yet.  So
you'd lose points of Int and Wis when attempting to put on a positively
enchanted helm of brilliance, or gain such with a negatively enchanted one.

     steal() was assigning to afternmv before it had been used to finish the
action of putting on or taking off armor.  Fix by interrupting the attempt
to put on or take off armor when being victimized by theft (or being hit by
succubus or incubus seduction).  The existing stop_occupation() call wasn't
sufficient because afternmv is different from occupation.

include/extern.h
src/do_wear.c
src/mhitu.c
src/steal.c

index 2f36c3d74399f901f11654397f228a5189bb6c93..abf3e410c7555f0ad18135bcc24a81711ab80b72 100644 (file)
@@ -417,6 +417,7 @@ E void FDECL(off_msg, (struct obj *));
 E void FDECL(set_wear, (struct obj *));
 E boolean FDECL(donning, (struct obj *));
 E void NDECL(cancel_don);
+E int FDECL(stop_donning, (struct obj *));
 E int NDECL(Armor_off);
 E int NDECL(Armor_gone);
 E int NDECL(Helmet_off);
index 0607b019022f7de18092409263dad84a8c460101..def059815f95270c0e51fca887c11d8bc50f8195 100644 (file)
@@ -1203,6 +1203,46 @@ cancel_don()
        context.takeoff.what = 0L;
 }
 
+/* called by steal() during theft from hero; interrupt donning/doffing */
+int
+stop_donning(stolenobj)
+struct obj *stolenobj; /* no message if stolenobj is already being doffing */
+{
+    char buf[BUFSZ];
+    struct obj *otmp;
+    boolean putting_on;
+    int result;
+
+    for (otmp = invent; otmp; otmp = otmp->nobj)
+       if ((otmp->owornmask & W_ARMOR) && donning(otmp)) break;
+       /* at most one item will pass donning() test at any given time */
+    if (!otmp) return 0;
+
+    result = -multi;   /* remember this before calling unmul() */
+    /* donning() returns True when doffing too */
+    putting_on = !(context.takeoff.mask & otmp->owornmask);
+    /* cancel_don() looks at afternmv; it also serves as cancel_doff() */
+    cancel_don();
+    /* don't want <armor>_on() or <armor>_off() being called
+       by unmul() since the on or off action isn't completing */
+    afternmv = 0;
+    if (putting_on || otmp != stolenobj)
+       Sprintf(buf, "You stop %s %s.",
+               putting_on ? "putting on" : "taking off",
+               thesimpleoname(otmp));
+    else
+       buf[0] = '\0';  /* silently stop doffing stolenobj */
+    unmul(buf);
+    /* while putting on, item becomes worn immediately but side-effects are
+       deferred until the delay expires; when interrupted, make it unworn
+       (while taking off, item stays worn until the delay expires; when
+       interrupted, leave it worn) */
+    if (putting_on)
+       remove_worn_item(otmp, FALSE);
+
+    return result;
+}
+
 static NEARDATA const char clothes[] = {ARMOR_CLASS, 0};
 static NEARDATA const char accessories[] = {RING_CLASS, AMULET_CLASS, TOOL_CLASS, FOOD_CLASS, 0};
 
index d36d1eb07876a2a53c639490c76cfadfc610b0a6..1f379388b47d559955a89f90cf4f82ccd04a6b45 100644 (file)
@@ -2261,6 +2261,9 @@ register struct monst *mon;
 
        if (Blind) pline("It caresses you...");
        else You_feel("very attracted to %s.", mon_nam(mon));
+       /* if in the process of putting armor on or taking armor off,
+          interrupt that activity now */
+       stop_donning((struct obj *)0);
        /* don't try to take off gloves if cursed weapon blocks them */
        if (welded(uwep)) tried_gloves = 1;
 
index 64b8deb7b7fbc38d6a05215dbdb543f47b4d02e4..6016e41a9558c8e4b4645c652006cf58e3b1ac34 100644 (file)
@@ -271,7 +271,7 @@ struct monst *mtmp;
 char *objnambuf;
 {
        struct obj *otmp;
-       int tmp, could_petrify, named = 0, armordelay, retrycnt = 0;
+       int tmp, could_petrify, armordelay, olddelay, named = 0, retrycnt = 0;
        boolean monkey_business; /* true iff an animal is doing the thievery */
 
        if (objnambuf) *objnambuf = '\0';
@@ -391,6 +391,9 @@ gotobj:
            o_unleash(otmp);
        }
 
+       /* stop donning/doffing now so that afternmv won't be clobbered
+          below; stop_occupation doesn't handle donning/doffing */
+       olddelay = stop_donning(otmp);
        /* you're going to notice the theft... */
        stop_occupation();
 
@@ -404,14 +407,13 @@ gotobj:
                    break;
                case ARMOR_CLASS:
                    armordelay = objects[otmp->otyp].oc_delay;
-                   /* Stop putting on armor which has been stolen. */
-                   if (donning(otmp)) {
-                       remove_worn_item(otmp, TRUE);
-                       break;
-                   } else if (monkey_business) {
+                   if (olddelay > 0 && olddelay < armordelay)
+                       armordelay = olddelay;
+                   if (monkey_business) {
                        /* animals usually don't have enough patience
                           to take off items which require extra time */
-                       if (armordelay >= 1 && rn2(10)) goto cant_take;
+                       if (armordelay >= 1 && !olddelay && rn2(10))
+                           goto cant_take;
                        remove_worn_item(otmp, TRUE);
                        break;
                    } else {