]> granicus.if.org Git - nethack/commitdiff
zapping unseen wands (trunk only)
authornethack.rankin <nethack.rankin>
Thu, 2 Feb 2006 06:15:13 +0000 (06:15 +0000)
committernethack.rankin <nethack.rankin>
Thu, 2 Feb 2006 06:15:13 +0000 (06:15 +0000)
     Noticed recently when a user reported that an unseen monster zapping
an unseen wand caused wand of striking to become discovered.  [That was
because the zap also hit a door and the code for the latter didn't check
whether the wand had been seen, and it got fixed a couple of weeks ago.]
When the player zaps wands while the hero is blinded, it was discovering
(for the cases where the effect can be observed without sight) the wand
even though the wand's description was unknown (showed in inventory as
"a wand").  This replaces the calls to makeknown() in zap.c with calls to
new learnwand(), which checks whether the wand description is known; you
no longer discover something you've never seen.  Reverse effect has also
been added:  if the type of wand has been discovered earlier, zapping an
unseen wand (another of the same type, picked up when blind and zapped
while still blind) will now mark it as seen (to show up in inventory as
"a wand of <whatever>" instead of just a "a wand").  The latter aspect
really ought to be independent of prior discovery, but we currently have
no way to record "we know what this particular item does even though we
don't know what type of item it is yet".  [If we add that, it would be
applicable to potions (when operating on stacks) and rings too.]

     Minor change:  zapping yourself with wand of opening or spell of
knock will remove attached ball&chain rather than give a "chain quivers"
message.  Explicitly zapping the chain already did that; if the unlocking
magic works on the chain connected to your leg then it really should also
work on your leg connected to the chain.  Zapping unlocking at yourself
probably should also scan inventory to check for carrying locked chests,
but I didn't add that.  (If added, then locking magic will need to be
augmented likewise.)

     This also fixes <Someone>'s recent complaint:  zapping an unknown
wand of teleportation at yourself didn't make it become discovered.
Now it will be, under the same circumstances as when you're riding:  if
teleport control causes you to be prompted for destination, or if random
destination moves you more than jumping distance away from your original
position.  (The Stunned exception to teleport control, which was missing
in zap_steed, perhaps ought to be moved into the macro definition of
Teleport_control itself so that all code always handles it consistently.)

doc/fixes35.0
src/zap.c

index 5d8433ce48d5a00bd61255ac6660130ed479befd..f838aedd9a88068f987dd92a93b2e3e4c2cec080 100644 (file)
@@ -109,6 +109,9 @@ fix monsndx panic which happened after currently moving monster expelled
        swallowed hero onto magic trap and was made tame by its effect; taming
        no longer replaces monster
 reduced message verbosity when re-entering a temple
+zapping a never seen wand while blinded won't make the wand a discovery
+zapping an unID'd wand of teleportation at self will discover it (usually)
+zapping unlocking magic at self while punished will remove attached chain
 
 
 Platform- and/or Interface-Specific Fixes
index 65d2e2d7da1dcb5e2044db5943bfe226dfff3943..f76c606bcf3d4f32229d9f4c6c380d96f7dc9753 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)zap.c      3.5     2005/11/02      */
+/*     SCCS Id: @(#)zap.c      3.5     2006/01/30      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -19,8 +19,9 @@ extern boolean notonhead;     /* for long worms */
 /* kludge to use mondied instead of killed */
 extern boolean m_using;
 
-STATIC_DCL void FDECL(polyuse, (struct obj*, int, int));
-STATIC_DCL void FDECL(create_polymon, (struct obj *, int));
+STATIC_DCL void FDECL(learnwand, (struct obj *));
+STATIC_DCL void FDECL(polyuse, (struct obj *,int,int));
+STATIC_DCL void FDECL(create_polymon, (struct obj *,int));
 STATIC_DCL boolean FDECL(zap_updown, (struct obj *));
 STATIC_DCL int FDECL(zhitm, (struct monst *,int,int,struct obj **));
 STATIC_DCL void FDECL(zhitu, (int,int,const char *,XCHAR_P,XCHAR_P));
@@ -91,6 +92,58 @@ const char * const flash_types[] = { /* also used in buzzmu(mcastu.c) */
        ""
 };
 
+/*
+ * Recognizing unseen wands by zapping:  in 3.4.3 and earlier, zapping
+ * most wand types while blind would add that type to the discoveries
+ * list even if it had never been seen (ie, picked up while blinded
+ * and shown in inventory as simply "a wand").  This behavior has been
+ * changed; now such wands won't be discovered.  But if the type is
+ * already discovered, then the individual wand whose effect was just
+ * observed will be flagged as if seen.  [You already know wands of
+ * striking; you zap "a wand" and observe striking effect (presumeably
+ * by sound or touch); it'll become shown in inventory as "a wand of
+ * striking".]
+ *
+ * Unfortunately, the new behavior isn't really correct either.  There
+ * should be an `eknown' bit for "effect known" added for wands (and
+ * for potions since quaffing one of a stack is similar) so that the
+ * particular wand which has been zapped would have its type become
+ * known (it would change from "a wand" to "a wand of striking", for
+ * example) without the type becoming discovered or other unknown wands
+ * of that type showing additional information.  When blindness ends,
+ * all objects in inventory with the eknown bit set would be discovered
+ * and other items of the same type would become known as such.
+ */
+
+/* wand discovery gets special handling when hero is blinded */
+STATIC_OVL void
+learnwand(obj)
+struct obj *obj;
+{
+    /* For a wand (or wand-like tool) zapped by the player, if the
+       effect was observable (determined by caller; usually seen, but
+       possibly heard or felt if the hero is blinded) then discover the
+       object type provided that the object itself is known (as more
+       than just "a wand").  If object type is already discovered and
+       we observed the effect, mark the individual wand as having been
+       seen.  Suppress spells (which use fake spellbook object for `obj')
+       so that casting a spell won't re-discover its forgotten book. */
+    if (obj->oclass != SPBOOK_CLASS) {
+       /* if type already discovered, treat this item has having been seen
+          even if the hero is currently blinded (skips redundant makeknown) */
+       if (objects[obj->otyp].oc_name_known) {
+           obj->dknown = 1;    /* will usually be set already */
+       /* otherwise discover it if this item itself has been or can be seen */
+       } else {
+           /* in case it was picked up while blind and then zapped without
+              examining inventory after regaining sight (bypassing xname()) */
+           if (!Blind) obj->dknown = 1;
+           /* make the discovery iff we know what we're manipulating */
+           if (obj->dknown) makeknown(obj->otyp);
+       }
+    }
+}
+
 /* Routines for IMMEDIATE wands and spells. */
 /* bhitm: monster mtmp was hit by the effect of wand or spell otmp */
 int
@@ -99,7 +152,7 @@ struct monst *mtmp;
 struct obj *otmp;
 {
        boolean wake = TRUE;    /* Most 'zaps' should wake monster */
-       boolean reveal_invis = FALSE;
+       boolean reveal_invis = FALSE, learn_it = FALSE;
        boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart;
        int dmg, otyp = otmp->otyp;
        const char *zap_type_text = "spell";
@@ -127,7 +180,7 @@ struct obj *otmp;
                        hit(zap_type_text, mtmp, exclam(dmg));
                        (void) resist(mtmp, otmp->oclass, dmg, TELL);
                } else miss(zap_type_text, mtmp);
-               makeknown(otyp);
+               learn_it = TRUE;
                break;
        case WAN_SLOW_MONSTER:
        case SPE_SLOW_MONSTER:
@@ -179,7 +232,7 @@ struct obj *otmp;
                    if (mtmp->cham == CHAM_ORDINARY && !rn2(25)) {
                        if (canseemon(mtmp)) {
                            pline("%s shudders!", Monnam(mtmp));
-                           makeknown(otyp);
+                           learn_it = TRUE;
                        }
                        /* dropped inventory shouldn't be hit by this zap */
                        for (obj = mtmp->minvent; obj; obj = obj->nobj)
@@ -190,7 +243,7 @@ struct obj *otmp;
                    } else if (newcham(mtmp, (struct permonst *)0,
                                       (otyp != POT_POLYMORPH), FALSE)) {
                        if (!Hallucination && canspotmon(mtmp))
-                           makeknown(otyp);
+                           learn_it = TRUE;
                    }
                }
                break;
@@ -212,7 +265,7 @@ struct obj *otmp;
                mon_set_minvis(mtmp);
                if (!oldinvis && knowninvisible(mtmp)) {
                    pline("%s turns transparent!", nambuf);
-                   makeknown(otyp);
+                   learn_it = TRUE;
                }
                break;
            }
@@ -225,7 +278,7 @@ struct obj *otmp;
                wake = FALSE;
                reveal_invis = TRUE;
                probe_monster(mtmp);
-               makeknown(otyp);
+               learn_it = TRUE;
                break;
        case WAN_OPENING:
        case SPE_KNOCK:
@@ -283,7 +336,7 @@ struct obj *otmp;
                break;
        case WAN_LIGHT: /* (broken wand) */
                if (flash_hits_mon(mtmp, otmp)) {
-                   makeknown(WAN_LIGHT);
+                   learn_it = TRUE;
                    reveal_invis = TRUE;
                }
                break;
@@ -293,7 +346,7 @@ struct obj *otmp;
                reveal_invis = TRUE;
                if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS))
                    slept_monst(mtmp);
-               if (!Blind) makeknown(WAN_SLEEP);
+               if (!Blind) learn_it = TRUE;
                break;
        case SPE_STONE_TO_FLESH:
                if (monsndx(mtmp->data) == PM_STONE_GOLEM) {
@@ -351,6 +404,9 @@ struct obj *otmp;
                                                        !canspotmon(mtmp))
                map_invisible(bhitpos.x, bhitpos.y);
        }
+       /* if effect was observable then discover the wand type provided
+          that the wand itself has been seen */
+       if (learn_it) learnwand(otmp);
        return 0;
 }
 
@@ -1447,8 +1503,17 @@ bhito(obj, otmp)
 struct obj *obj, *otmp;
 {
        int res = 1;    /* affected object by default */
+       boolean learn_it = FALSE, maybelearnit;
        xchar refresh_x, refresh_y;
 
+       /* fundamental: a wand effect hitting itself doesn't do anything;
+          otherwise we need to guard against accessing otmp after something
+          strange has happened to it (along the lines of polymorph or
+          stone-to-flesh [which aren't good examples since polymorph wands
+          aren't affected by polymorph zaps and stone-to-flesh isn't
+          available in wand form, but the concept still applies...]) */
+       if (obj == otmp) return 0;
+
        if (obj->bypass) {
                /* The bypass bit is currently only used as follows:
                 *
@@ -1493,8 +1558,8 @@ struct obj *obj, *otmp;
                res = 0;
        } else if (obj == uchain) {
                if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) {
+                   learn_it = TRUE;
                    unpunish();
-                   makeknown(otmp->otyp);
                } else
                    res = 0;
        } else
@@ -1514,8 +1579,7 @@ struct obj *obj, *otmp;
                if (Is_box(obj)) (void) boxlock(obj, otmp);
 
                if (obj_shudders(obj)) {
-                   if (cansee(obj->ox, obj->oy))
-                       makeknown(otmp->otyp);
+                   if (cansee(obj->ox, obj->oy)) learn_it = TRUE;
                    do_osshock(obj);
                    break;
                }
@@ -1539,23 +1603,25 @@ struct obj *obj, *otmp;
                    }
                    res = 1;
                }
-               if (res) makeknown(WAN_PROBING);
+               if (res) learn_it = TRUE;
                break;
        case WAN_STRIKING:
        case SPE_FORCE_BOLT:
-               if (obj->otyp == BOULDER)
-                       fracture_rock(obj);
-               else if (obj->otyp == STATUE)
-                       (void) break_statue(obj);
-               else {
-                       if (!context.mon_moving)
-                           (void)hero_breaks(obj, obj->ox, obj->oy, FALSE);
-                       else
-                           (void)breaks(obj, obj->ox, obj->oy);
-                       res = 0;
+               /* learn the type if you see or hear something break
+                  (the sound could be implicit) */
+               maybelearnit = cansee(obj->ox, obj->oy) || !Deaf;
+               if (obj->otyp == BOULDER) {
+                   fracture_rock(obj);
+               } else if (obj->otyp == STATUE) {
+                   (void) break_statue(obj);
+               } else {
+                   if (context.mon_moving ?
+                           !breaks(obj, obj->ox, obj->oy) :
+                           !hero_breaks(obj, obj->ox, obj->oy, FALSE))
+                       maybelearnit = FALSE;   /* nothing broke */
+                   res = 0;
                }
-               /* BUG[?]: shouldn't this depend upon you seeing it happen? */
-               makeknown(otmp->otyp);
+               if (maybelearnit) learn_it = TRUE;
                break;
        case WAN_CANCELLATION:
        case SPE_CANCELLATION:
@@ -1586,22 +1652,17 @@ struct obj *obj, *otmp;
 
                    res = !!revive(obj, TRUE);
                    if (res && Role_if(PM_HEALER)) {
-                       boolean u_noticed = FALSE;
-
                        if (Hallucination && !Deaf) {
                            You_hear("the sound of a defibrillator.");
-                           u_noticed = TRUE;
+                           learn_it = TRUE;
                        } else if (!Blind) {
                            You("observe %s %s change dramatically.",
                                s_suffix(an(mons[corpsenm].mname)),
                                nonliving(&mons[corpsenm]) ?
                                        "motility" : "health");
-                           u_noticed = TRUE;
-                       }
-                       if (u_noticed) {
-                           makeknown(otmp->otyp);
-                           exercise(A_WIS, TRUE);
+                           learn_it = TRUE;
                        }
+                       if (learn_it) exercise(A_WIS, TRUE);
                    }
                }
                break;
@@ -1613,8 +1674,7 @@ struct obj *obj, *otmp;
                        res = boxlock(obj, otmp);
                else
                        res = 0;
-               if (res /* && otmp->oclass == WAND_CLASS */)
-                       makeknown(otmp->otyp);
+               if (res) learn_it = TRUE;
                break;
        case WAN_SLOW_MONSTER:          /* no effect on objects */
        case SPE_SLOW_MONSTER:
@@ -1726,6 +1786,9 @@ smell:
                impossible("What an interesting effect (%d)", otmp->otyp);
                break;
        }
+       /* if effect was observable then discover the wand type provided
+          that the wand itself has been seen */
+       if (learn_it) learnwand(otmp);
        return res;
 }
 
@@ -1748,8 +1811,8 @@ bhitpile(obj,fhito,tx,ty)
           (below), rather than on the current object, if it happens to
           encounter a statue which mustn't become animated. */
        if (t && t->ttyp == STATUE_TRAP &&
-           activate_statue_trap(t, tx, ty, TRUE) && obj->otyp == WAN_STRIKING)
-           makeknown(obj->otyp);
+               activate_statue_trap(t, tx, ty, TRUE))
+           learnwand(obj);
     }
 
     poly_zapped = -1;
@@ -1823,9 +1886,12 @@ register struct obj *obj;
                        exercise(A_WIS, TRUE);
                        break;
        }
-       if (known && !objects[obj->otyp].oc_name_known) {
-               makeknown(obj->otyp);
+       if (known) {
+           if (!objects[obj->otyp].oc_name_known)
                more_experienced(0,10);
+           /* effect was observable; discover the wand type provided
+              that the wand itself has been seen */
+           learnwand(obj);
        }
 }
 
@@ -1897,12 +1963,13 @@ zapyourself(obj, ordinary)
 struct obj *obj;
 boolean ordinary;
 {
+       boolean learn_it = FALSE;
        int     damage = 0;
 
        switch(obj->otyp) {
                case WAN_STRIKING:
-                   makeknown(WAN_STRIKING);
                case SPE_FORCE_BOLT:
+                   learn_it = TRUE;
                    if(Antimagic) {
                        shieldeff(u.ux, u.uy);
                        pline("Boing!");
@@ -1917,7 +1984,7 @@ boolean ordinary;
                    break;
 
                case WAN_LIGHTNING:
-                   makeknown(WAN_LIGHTNING);
+                   learn_it = TRUE;
                    if (!Shock_resistance) {
                        You("shock yourself!");
                        damage = d(12,6);
@@ -1937,8 +2004,8 @@ boolean ordinary;
                    explode(u.ux, u.uy, 11, d(6,6), WAND_CLASS, EXPL_FIERY);
                    break;
                case WAN_FIRE:
-                   makeknown(WAN_FIRE);
                case FIRE_HORN:
+                   learn_it = TRUE;
                    if (Fire_resistance) {
                        shieldeff(u.ux, u.uy);
                        You_feel("rather warm.");
@@ -1955,9 +2022,9 @@ boolean ordinary;
                    break;
 
                case WAN_COLD:
-                   makeknown(WAN_COLD);
                case SPE_CONE_OF_COLD:
                case FROST_HORN:
+                   learn_it = TRUE;
                    if (Cold_resistance) {
                        shieldeff(u.ux, u.uy);
                        You_feel("a little chill.");
@@ -1970,8 +2037,8 @@ boolean ordinary;
                    break;
 
                case WAN_MAGIC_MISSILE:
-                   makeknown(WAN_MAGIC_MISSILE);
                case SPE_MAGIC_MISSILE:
+                   learn_it = TRUE;
                    if(Antimagic) {
                        shieldeff(u.ux, u.uy);
                        pline_The("missiles bounce!");
@@ -1982,11 +2049,11 @@ boolean ordinary;
                    break;
 
                case WAN_POLYMORPH:
-                   if (!Unchanging)
-                       makeknown(WAN_POLYMORPH);
                case SPE_POLYMORPH:
-                   if (!Unchanging)
-                       polyself(0);
+                   if (!Unchanging) {
+                       learn_it = TRUE;
+                       polyself(0);
+                   }
                    break;
 
                case WAN_CANCELLATION:
@@ -1995,12 +2062,12 @@ boolean ordinary;
                    break;
 
                case SPE_DRAIN_LIFE:
-                       if (!Drain_resistance) {
-                               losexp("life drainage");
-                               makeknown(obj->otyp);
-                       }
-                       damage = 0;     /* No additional damage */
-                       break;
+                   if (!Drain_resistance) {
+                       learn_it = TRUE;        /* (no effect for spells...) */
+                       losexp("life drainage");
+                   }
+                   damage = 0; /* No additional damage */
+                   break;
 
                case WAN_MAKE_INVISIBLE: {
                    /* have to test before changing HInvis but must change
@@ -2019,7 +2086,7 @@ boolean ordinary;
                        incr_itimeout(&HInvis, d(obj->spe, 250));
                    }
                    if (msg) {
-                       makeknown(WAN_MAKE_INVISIBLE);
+                       learn_it = TRUE;
                        newsym(u.ux, u.uy);
                        self_invis_message();
                    }
@@ -2028,19 +2095,19 @@ boolean ordinary;
 
                case WAN_SPEED_MONSTER:
                    if (!(HFast & INTRINSIC)) {
+                       learn_it = TRUE;
                        if (!Fast)
                            You("speed up.");
                        else
                            Your("quickness feels more natural.");
-                       makeknown(WAN_SPEED_MONSTER);
                        exercise(A_DEX, TRUE);
                    }
                    HFast |= FROMOUTSIDE;
                    break;
 
                case WAN_SLEEP:
-                   makeknown(WAN_SLEEP);
                case SPE_SLEEP:
+                   learn_it = TRUE;
                    if(Sleep_resistance) {
                        shieldeff(u.ux, u.uy);
                        You("don't feel sleepy!");
@@ -2053,14 +2120,19 @@ boolean ordinary;
                case WAN_SLOW_MONSTER:
                case SPE_SLOW_MONSTER:
                    if(HFast & (TIMEOUT | INTRINSIC)) {
+                       learn_it = TRUE;
                        u_slow_down();
-                       makeknown(obj->otyp);
                    }
                    break;
 
                case WAN_TELEPORTATION:
                case SPE_TELEPORT_AWAY:
                    tele();
+                   /* same criteria as when mounted (zap_steed) */
+                   if ((Teleport_control && !Stunned) ||
+                           !couldsee(u.ux0, u.uy0) ||
+                           distu(u.ux0, u.uy0) >= 16)
+                       learn_it = TRUE;
                    break;
 
                case WAN_DEATH:
@@ -2071,17 +2143,17 @@ boolean ordinary;
                          : "You seem no deader than before.");
                        break;
                    }
+                   learn_it = TRUE;
                    Sprintf(killer.name,"shot %sself with a death ray",uhim());
                    killer.format = NO_KILLER_PREFIX;
                    You("irradiate yourself with pure energy!");
                    You("die.");
-                   makeknown(obj->otyp);
                        /* They might survive with an amulet of life saving */
                    done(DIED);
                    break;
                case WAN_UNDEAD_TURNING:
-                   makeknown(WAN_UNDEAD_TURNING);
                case SPE_TURN_UNDEAD:
+                   learn_it = TRUE;
                    (void) unturn_dead(&youmonst);
                    if (is_undead(youmonst.data)) {
                        You_feel("frightened and %sstunned.",
@@ -2092,6 +2164,7 @@ boolean ordinary;
                    break;
                case SPE_HEALING:
                case SPE_EXTRA_HEALING:
+                   learn_it = TRUE;    /* (no effect for spells...) */
                    healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4),
                           0, FALSE, (obj->otyp == SPE_EXTRA_HEALING));
                    You_feel("%sbetter.",
@@ -2104,13 +2177,15 @@ boolean ordinary;
                case EXPENSIVE_CAMERA:
 #endif
                    damage += rnd(25);
-                   if (flashburn((long)damage)) makeknown(obj->otyp);
+                   if (flashburn((long)damage)) learn_it = TRUE;
                    damage = 0; /* reset */
                    break;
                case WAN_OPENING:
-                   if (Punished) makeknown(WAN_OPENING);
                case SPE_KNOCK:
-                   if (Punished) Your("chain quivers for a moment.");
+                   if (Punished) {
+                       learn_it = TRUE;
+                       unpunish();
+                   }
                    break;
                case WAN_DIGGING:
                case SPE_DIG:
@@ -2120,25 +2195,33 @@ boolean ordinary;
                case SPE_WIZARD_LOCK:
                    break;
                case WAN_PROBING:
-                   for (obj = invent; obj; obj = obj->nobj)
-                       obj->dknown = 1;
-                   /* note: `obj' reused; doesn't point at wand anymore */
-                   makeknown(WAN_PROBING);
+                 {
+                   struct obj *otmp;
+
+                   for (otmp = invent; otmp; otmp = otmp->nobj)
+                       otmp->dknown = 1;
+                   learn_it = TRUE;
                    ustatusline();
                    break;
+                 }
                case SPE_STONE_TO_FLESH:
-                   {
-                   struct obj *otemp, *onext;
+                 {
+                   struct obj *otmp, *onxt;
                    boolean didmerge;
 
-                   if (u.umonnum == PM_STONE_GOLEM)
+                   if (u.umonnum == PM_STONE_GOLEM) {
+                       learn_it = TRUE;
                        (void) polymon(PM_FLESH_GOLEM);
-                   if (Stoned) fix_petrification();    /* saved! */
+                   }
+                   if (Stoned) {
+                       learn_it = TRUE;
+                       fix_petrification();    /* saved! */
+                   }
                    /* but at a cost.. */
-                   for (otemp = invent; otemp; otemp = onext) {
-                       onext = otemp->nobj;
-                       (void) bhito(otemp, obj);
-                       }
+                   for (otmp = invent; otmp; otmp = onxt) {
+                       onxt = otmp->nobj;
+                       if (bhito(otmp, obj)) learn_it = TRUE;
+                   }
                    /*
                     * It is possible that we can now merge some inventory.
                     * Do a higly paranoid merge.  Restart from the beginning
@@ -2146,18 +2229,22 @@ boolean ordinary;
                     */
                    do {
                        didmerge = FALSE;
-                       for (otemp = invent; !didmerge && otemp; otemp = otemp->nobj)
-                           for (onext = otemp->nobj; onext; onext = onext->nobj)
-                               if (merged(&otemp, &onext)) {
-                                       didmerge = TRUE;
-                                       break;
-                                       }
+                       for (otmp = invent; !didmerge && otmp; otmp = otmp->nobj)
+                           for (onxt = otmp->nobj; onxt; onxt = onxt->nobj)
+                               if (merged(&otmp, &onxt)) {
+                                   didmerge = TRUE;
+                                   break;
+                               }
                    } while (didmerge);
-                   }
                    break;
-               default: impossible("object %d used?",obj->otyp);
+                 }
+               default:
+                   impossible("zapyourself: object %d used?", obj->otyp);
                    break;
        }
+       /* if effect was observable then discover the wand type provided
+          that the wand itself has been seen */
+       if (learn_it) learnwand(obj);
        return(damage);
 }
 
@@ -2194,16 +2281,18 @@ struct obj *obj;        /* wand or spell */
            */
                case WAN_PROBING:
                    probe_monster(u.usteed);
-                   makeknown(WAN_PROBING);
+                   learnwand(obj);
                    steedhit = TRUE;
                    break;
                case WAN_TELEPORTATION:
                case SPE_TELEPORT_AWAY:
                    /* you go together */
                    tele();
-                   if(Teleport_control || !couldsee(u.ux0, u.uy0) ||
-                       (distu(u.ux0, u.uy0) >= 16))
-                               makeknown(obj->otyp);
+                   /* same criteria as when unmounted (zapyourself) */
+                   if ((Teleport_control && !Stunned) ||
+                           !couldsee(u.ux0, u.uy0) ||
+                           distu(u.ux0, u.uy0) >= 16)
+                       learnwand(obj);
                    steedhit = TRUE;
                    break;
 
@@ -2512,9 +2601,9 @@ struct    obj     *obj;
                impossible("weffects: unexpected spell or wand");
            disclose = TRUE;
        }
-       if (disclose && was_unkn) {
-           makeknown(otyp);
-           more_experienced(0,10);
+       if (disclose) {
+           learnwand(obj);
+           if (was_unkn) more_experienced(0, 10);
        }
        return;
 }
@@ -2711,30 +2800,34 @@ struct obj **pobj;                      /* object tossed/used, set to NULL
                break;
            }
 
-           if (weapon == ZAPPED_WAND && find_drawbridge(&x,&y))
+           if (weapon == ZAPPED_WAND && find_drawbridge(&x,&y)) {
+               boolean learn_it = FALSE;
+
                switch (obj->otyp) {
                    case WAN_OPENING:
                    case SPE_KNOCK:
                        if (is_db_wall(bhitpos.x, bhitpos.y)) {
                            if (cansee(x,y) || cansee(bhitpos.x,bhitpos.y))
-                               makeknown(obj->otyp);
+                               learn_it = TRUE;
                            open_drawbridge(x,y);
                        }
                        break;
                    case WAN_LOCKING:
                    case SPE_WIZARD_LOCK:
-                       if ((cansee(x,y) || cansee(bhitpos.x, bhitpos.y))
-                           && levl[x][y].typ == DRAWBRIDGE_DOWN)
-                           makeknown(obj->otyp);
+                       if ((cansee(x,y) || cansee(bhitpos.x, bhitpos.y)) &&
+                               levl[x][y].typ == DRAWBRIDGE_DOWN)
+                           learn_it = TRUE;
                        close_drawbridge(x,y);
                        break;
                    case WAN_STRIKING:
                    case SPE_FORCE_BOLT:
                        if (typ != DRAWBRIDGE_UP)
                            destroy_drawbridge(x,y);
-                       makeknown(obj->otyp);
+                       learn_it = TRUE;
                        break;
                }
+               if (learn_it) learnwand(obj);
+           }
 
            mtmp = m_at(bhitpos.x, bhitpos.y);
                
@@ -2833,10 +2926,10 @@ struct obj **pobj;                      /* object tossed/used, set to NULL
                case SPE_FORCE_BOLT:
                    if (doorlock(obj, bhitpos.x, bhitpos.y)) {
                        if (cansee(bhitpos.x, bhitpos.y) ||
-                           (obj->otyp == WAN_STRIKING))
-                           makeknown(obj->otyp);
-                       if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN
-                           && *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) {
+                               (obj->otyp == WAN_STRIKING && !Deaf))
+                           learnwand(obj);
+                       if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN &&
+                               *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) {
                            shopdoor = TRUE;
                            add_damage(bhitpos.x, bhitpos.y, 400L);
                        }