]> granicus.if.org Git - nethack/commitdiff
still more Master Key of Thievery
authorPatR <rankin@nethack.org>
Sun, 8 Oct 2017 10:29:16 +0000 (03:29 -0700)
committerPatR <rankin@nethack.org>
Sun, 8 Oct 2017 10:29:16 +0000 (03:29 -0700)
Make #untrap while carrying the non-cursed (for rogues) or blessed
(for non-rogues) Key work the same as #invoke has been doing (without
regard to its bless/curse state):  when used on trapped door or chest,
that trap will always be found and disarming it will always succeed.

It should work when carried by monsters too:  if they try to open a
trapped door while carrying the Key (must be blessed since they're
not rogues) the trap will be automatically disarmed.  (Caveat:  that
hasn't been adequately tested.)

TODO (maybe...):  change the #invoke property to detect unseen/secret
door detection instead of #untrap.  The latter isn't completely
redundant; it works when the Key is cursed.  But quest artifacts
strongly resist becoming cursed so that isn't a particularly useful
distinction.

Also, trap hints when wielding the Key without gloves didn't notice
adjacent door and chest traps.  Now it does.  And the behavior is
slightly different:  known traps covered by objects or monsters are
treated like unknown traps as far as the hot/cold hints go.

include/artilist.h
include/extern.h
src/artifact.c
src/lock.c
src/monmove.c
src/trap.c

index e06e99a7fd6269a9e7fb7f8e5a3762e3c8051436..d5992bcef4429909882bdcda6d9e07b329912d42 100644 (file)
@@ -208,6 +208,9 @@ A("The Palantir of Westernesse",        CRYSTAL_BALL,
       PHYS(5, 0), NO_DFNS, NO_CARY, CREATE_AMMO, A_CHAOTIC, PM_RANGER, NON_PM,
       4000L, NO_COLOR),
 
+    /* MKoT has an additional carry property if the Key is not cursed (for
+       rogues) or blessed (for non-rogues):  #untrap of doors and chests
+       will always find any traps and disarming those will always succeed */
     A("The Master Key of Thievery", SKELETON_KEY,
       (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_SPEAK),
       (SPFX_WARN | SPFX_TCTRL | SPFX_HPHDAM), 0, NO_ATTK, NO_DFNS, NO_CARY,
index ca78d340f3b1ba7d005030e38bd4252e4fa6dfcb..cc2bf44dcc8976dfc80f0040c12d302ce0495fe6 100644 (file)
@@ -78,8 +78,8 @@ E int FDECL(spec_dbon, (struct obj *, struct monst *, int));
 E void FDECL(discover_artifact, (XCHAR_P));
 E boolean FDECL(undiscovered_artifact, (XCHAR_P));
 E int FDECL(disp_artifact_discoveries, (winid));
-E boolean FDECL(artifact_hit,
-                (struct monst *, struct monst *, struct obj *, int *, int));
+E boolean FDECL(artifact_hit, (struct monst *, struct monst *, struct obj *,
+                               int *, int));
 E int NDECL(doinvoke);
 E boolean FDECL(finesse_ahriman, (struct obj *));
 E void FDECL(arti_speak, (struct obj *));
@@ -93,6 +93,8 @@ E void FDECL(Sting_effects, (int));
 E int FDECL(retouch_object, (struct obj **, BOOLEAN_P));
 E void FDECL(retouch_equipment, (int));
 E void NDECL(mkot_trap_warn);
+E boolean FDECL(is_magic_key, (struct monst *, struct obj *));
+E struct obj *FDECL(has_magic_key, (struct monst *));
 
 /* ### attrib.c ### */
 
index 4fdfc828aa66aa3b325b4e6f9301ec0e3ebae3e2..25fc171185a32687f7e416b7a571f0782954dc6c 100644 (file)
@@ -2055,36 +2055,102 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */
 static int mkot_trap_warn_count = 0;
 
 STATIC_OVL int
-count_surround_traps(x,y)
-int x,y;
+count_surround_traps(x, y)
+int x, y;
 {
-    int dx, dy, ret = 0;
+    struct rm *levp;
+    struct obj *otmp;
     struct trap *ttmp;
-
-    for (dx = x-1; dx < x+2; dx++)
-        for (dy = y-1; dy < y+2; dy++)
-            if (isok(dx,dy) && (ttmp = t_at(dx,dy))
-                && !ttmp->tseen)
-                ret++;
+    int dx, dy, glyph, ret = 0;
+
+    for (dx = x - 1; dx < x + 2; ++dx)
+        for (dy = y - 1; dy < y + 2; ++dy) {
+            if (!isok(dx, dy))
+                continue;
+            /* If a trap is shown here, don't count it; the hero
+             * should be expecting it.  But if there is a trap here
+             * that's not shown, either undiscovered or covered by
+             * something, do count it.
+             */
+            glyph = glyph_at(dx, dy);
+            if (glyph_is_trap(glyph))
+                continue;
+            if ((ttmp = t_at(dx, dy)) != 0) {
+                ++ret;
+                continue;
+            }
+            levp = &levl[dx][dy];
+            if (IS_DOOR(levp->typ) && (levp->doormask & D_TRAPPED) != 0) {
+                ++ret;
+                continue;
+            }
+            for (otmp = level.objects[dx][dy]; otmp; otmp = otmp->nexthere)
+                if (Is_container(otmp) && otmp->otrapped) {
+                    ++ret; /* we're counting locations, so just */
+                    break; /* count the first one in a pile     */
+                }
+        }
+    /*
+     * [Shouldn't we also check inventory for a trapped container?
+     * Even if its trap has already been found, there's no 'tknown'
+     * flag to help hero remember that so we have nothing comparable
+     * to a shown glyph to justify skipping it.]
+     */
     return ret;
 }
 
+/* sense adjacent traps if wielding MKoT without wearing gloves */
 void
 mkot_trap_warn()
 {
-    if (!uarmg && uwep
-        && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) {
-        const char *const heat[7] = { "cold", "slightly warm", "warm",
-                                      "very warm", "hot", "very hot",
-                                      "like fire" };
-        int ntraps = count_surround_traps(u.ux, u.uy);
-
-        if (ntraps != mkot_trap_warn_count)
-            pline_The("key feels %s%c", heat[(ntraps > 6) ? 6 : ntraps],
-                      ntraps > 3 ? '!' : '.');
+    static const char *const heat[7] = {
+        "cool", "slightly warm", "warm", "very warm",
+        "hot", "very hot", "like fire"
+    };
+
+    if (!uarmg && uwep && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) {
+        int idx, ntraps = count_surround_traps(u.ux, u.uy);
+
+        if (ntraps != mkot_trap_warn_count) {
+            idx = min(ntraps, SIZE(heat) - 1);
+            pline_The("Key feels %s%c", heat[idx], (ntraps > 3) ? '!' : '.');
+        }
         mkot_trap_warn_count = ntraps;
     } else
         mkot_trap_warn_count = 0;
 }
 
+/* Master Key is magic key if its bless/curse state meets our criteria:
+   not cursed for rogues or blessed for non-rogues */
+boolean
+is_magic_key(mon, obj)
+struct monst *mon; /* if null, non-rogue is assumed */
+struct obj *obj;
+{
+    if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY)
+         && ((mon == &youmonst) ? Role_if(PM_ROGUE)
+                                : (mon && mon->data == &mons[PM_ROGUE])))
+        ? !obj->cursed : obj->blessed)
+        return TRUE;
+    return FALSE;
+}
+
+/* figure out whether 'mon' (usually youmonst) is carrying the magic key */
+struct obj *
+has_magic_key(mon)
+struct monst *mon; /* if null, hero assumed */
+{
+    struct obj *o;
+    short key = artilist[ART_MASTER_KEY_OF_THIEVERY].otyp;
+
+    if (!mon)
+        mon = &youmonst;
+    for (o = ((mon == &youmonst) ? invent : mon->minvent); o;
+         o = nxtobj(o, key, FALSE)) {
+        if (is_magic_key(mon, o))
+            return o;
+    }
+    return (struct obj *) 0;
+}
+
 /*artifact.c*/
index 19dfabe461a372e8e1de6d23b7e2ceaca0d10841..426e8a35f866fa2e2add6bdfedfd34780f47209b 100644 (file)
@@ -18,7 +18,6 @@ STATIC_PTR int NDECL(picklock);
 STATIC_PTR int NDECL(forcelock);
 
 STATIC_DCL const char *NDECL(lock_action);
-STATIC_DCL boolean FDECL(is_magic_key, (struct monst *, struct obj *));
 STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P));
 STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *));
 
@@ -275,21 +274,6 @@ maybe_reset_pick()
         reset_pick();
 }
 
-/* Master Key is magic key if its bless/curse state meets our criteria:
-   not cursed for rogues or blessed for non-rogues */
-STATIC_OVL boolean
-is_magic_key(mon, obj)
-struct monst *mon; /* if null, non-rogue is assumed */
-struct obj *obj;
-{
-    if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY)
-         && ((mon == &youmonst) ? Role_if(PM_ROGUE)
-                                : (mon && mon->data == &mons[PM_ROGUE])))
-        ? !obj->cursed : obj->blessed)
-        return TRUE;
-    return FALSE;
-}
-
 /* for doapply(); if player gives a direction or resumes an interrupted
    previous attempt then it costs hero a move even if nothing ultimately
    happens; when told "can't do that" before being asked for direction
index ab17709f42776a05655804ab16d07b20ef02ca2f..26d7e2144edcc9723f1388d3b329af8b3f33804b 100644 (file)
@@ -1246,6 +1246,16 @@ postmov:
                 boolean btrapped = (here->doormask & D_TRAPPED) != 0,
                         observeit = canseeit && canspotmon(mtmp);
 
+                /* if mon has MKoT, disarm door trap; no message given */
+                if (btrapped && has_magic_key(mtmp)) {
+                    /* BUG: this lets a vampire or blob or a doorbuster
+                       holding the Key disarm the trap even though it isn't
+                       using that Key when squeezing under or smashing the
+                       door.  Not significant enough to worry about; perhaps
+                       the Key's magic is more powerful for monsters? */
+                    here->doormask &= ~D_TRAPPED;
+                    btrapped = FALSE;
+                }
                 if ((here->doormask & (D_LOCKED | D_CLOSED)) != 0
                     && (amorphous(ptr)
                         || (can_fog(mtmp)
index 53493d4262812e6e87145f2d9418b6a369c24b4e..401d5a7fcdead70c0a82396d0b989b622ddf7b68 100644 (file)
@@ -4256,6 +4256,11 @@ boolean force;
         pline_The("perils lurking there are beyond your grasp.");
         return 0;
     }
+    /* 'force' is true for #invoke; make it be true for #untrap if
+       carrying MKoT */
+    if (!force && has_magic_key(&youmonst))
+        force = TRUE;
+
     ttmp = t_at(x, y);
     if (ttmp && !ttmp->tseen)
         ttmp = 0;