]> granicus.if.org Git - nethack/commitdiff
locking/unlocking vs traps (trunk only)
authornethack.rankin <nethack.rankin>
Sun, 1 Oct 2006 05:24:28 +0000 (05:24 +0000)
committernethack.rankin <nethack.rankin>
Sun, 1 Oct 2006 05:24:28 +0000 (05:24 +0000)
     About six weeks back, <email deleted> suggested that
bear traps should deal out damage and be escapable via opening magic.
This doesn't do anything about the first part, but it does allow opening
magic (wand of opening, spell of knock, blessed Bell of Opening) to get
the hero out of bear traps and webs if zapped either at self or downwards.
Zaps across the floor which hit monsters will free them from such traps,
with a chance that releasing a hostile monster will pacify it (using
existing #untrap code).  Conversely, if you are at a web or bear trap
location but not currently trapped, closing magic (wand of locking, spell
of wizard lock) will cause the trap to activate; you may or may not become
trapped.  Likewise for zaps at monsters who are at such locations, which
is treated as an attack.

     Opening magic which hits the hero or a monster located at a trap door
or falling rock trap spot will cause the trap to activate; as above, it's
an attack for the monster case.  At the moment, zapping opening magic
downwards at the hero's location (but not zapping at self or at monsters)
will also cause holes, pits, and spiked pits to activate.  (Zapping down
triggers falling rock traps and zapping up doesn't; that'll need to be
changed.)  Zapping opening down while mounted will untrap, if stuck in a
web or bear trap, and will trap, for the falling cases, in precedence over
releasing the saddle and forcibly dismounting.  The latter still occurs
when there is no applicable trap present though.

     Zapping locking magic downwards at a hole location will convert the
hole into a trap door.  Zapping breaking magic (wand of striking, spell of
force bolt) down at a trap door location will convert the trap door into a
hole.  (Neither conversion currently alters the made-by-you flag for the
trap.  However, the rationalization that distinctive style is what makes
made-by-you recognizable suggests that conversion should clear the flag.)
Lastly, the old behavior (which pre-dated bare holes) of destroying trap
doors when zapping down at them with locking magic has been removed--it
didn't seem to fit very well with the new cases.  I'm starting to have
second thoughts about that but am going to commit this before discovery of
some more niggling details drags it out for another six weeks.

doc/fixes35.0
include/extern.h
src/detect.c
src/trap.c
src/zap.c

index 6fe0f6d4298883619b813e6ffc91e0036656150f..7b885cf8fbc8453817903378c2876cd8d1ac7dd2 100644 (file)
@@ -236,6 +236,9 @@ obsolete config file keywords: GRAPHICS, OBJECTS, TRAPS, EFFECTS
 deprecated options: IBMGraphics, DECGraphics, boulder
 new options: symset, roguesymset for choosing a symbol set from the symbols file
 new config file keyword: SYMBOLS for overriding character symbol values by name
+opening magic frees from bear traps and webs, activates trap doors
+closing magic activates bear traps and webs
+locking converts a hole into a trap door; striking does the opposite
 
 
 Platform- and/or Interface-Specific New Features
index 118ef4ba2871a99d3627ec5758e2008367878c61..f1897036767adc83560900e354abadd46495f82f 100644 (file)
@@ -2204,6 +2204,9 @@ E void FDECL(drain_en, (int));
 E int NDECL(dountrap);
 E void FDECL(cnv_trap_obj, (int,int,struct trap *,BOOLEAN_P));
 E int FDECL(untrap, (BOOLEAN_P));
+E boolean FDECL(openholdingtrap, (struct monst *,boolean *));
+E boolean FDECL(closeholdingtrap, (struct monst *,boolean *));
+E boolean FDECL(openfallingtrap, (struct monst *,BOOLEAN_P,boolean *));
 E boolean FDECL(chest_trap, (struct obj *,int,BOOLEAN_P));
 E void FDECL(deltrap, (struct trap *));
 E boolean FDECL(delfloortrap, (struct trap *));
index 5320610a6dccb9dfd0f290d36b377d99fc601adb..d24344f954036f0c9cc7c3d67df9c080d0e66f07 100644 (file)
@@ -1112,13 +1112,14 @@ genericptr_t num;
 {
        register struct trap *ttmp;
        register struct obj *otmp;
+       int *num_p = (int *)num;
 
        if(OBJ_AT(zx, zy)) {
                for(otmp = level.objects[zx][zy];
                                otmp; otmp = otmp->nexthere) {
                    if(Is_box(otmp) && otmp->olocked) {
                        otmp->olocked = 0;
-                       (*(int*)num)++;
+                       (*num_p)++;
                    }
                }
                /* let it fall to the next cases. could be on trap. */
@@ -1139,22 +1140,29 @@ genericptr_t num;
                    levl[zx][zy].doormask = D_ISOPEN;
                unblock_point(zx, zy);
                newsym(zx, zy);
-               (*(int*)num)++;
+               (*num_p)++;
        } else if(levl[zx][zy].typ == SCORR) {
                levl[zx][zy].typ = CORR;
                unblock_point(zx, zy);
                newsym(zx, zy);
-               (*(int*)num)++;
+               (*num_p)++;
        } else if ((ttmp = t_at(zx, zy)) != 0) {
+               struct monst *mon;
+               boolean dummy;  /* unneeded "you notice it arg" */
+
                if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
                    ttmp->tseen = 1;
                    newsym(zx,zy);
-                   (*(int*)num)++;
+                   (*num_p)++;
                }
+               mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
+               if (openholdingtrap(mon, &dummy) ||
+                       openfallingtrap(mon, TRUE, &dummy))
+                   (*num_p)++;
        } else if (find_drawbridge(&zx, &zy)) {
                /* make sure it isn't an open drawbridge */
                open_drawbridge(zx, zy);
-               (*(int*)num)++;
+               (*num_p)++;
        }
 }
 
index 6f60278ca9c66ac8cca3d136be5c2589e5e9eda2..06a9c9d2f3e8e2e72b92c748aee0e4b0eb1d98ae 100644 (file)
@@ -34,6 +34,9 @@ STATIC_OVL boolean FDECL(keep_saddle_with_steedcorpse,
                        (unsigned, struct obj *, struct obj *));
 #endif
 
+/* mintrap() should take a flags argument, but for time being we use this */
+STATIC_VAR int force_mintrap = 0;
+
 STATIC_VAR const char * const a_your[2] = { "a", "your" };
 STATIC_VAR const char * const A_Your[2] = { "A", "Your" };
 STATIC_VAR const char tower_of_flame[] = "tower of flame";
@@ -1066,18 +1069,20 @@ glovecheck:             (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
                if (webmsgok) {
                    char verbbuf[BUFSZ];
 
+                   if (forcetrap) {
+                       Strcpy(verbbuf, "are caught by");
 #ifdef STEED
-                   if (u.usteed)
-                       Sprintf(verbbuf, "lead %s",
+                   } else if (u.usteed) {
+                       Sprintf(verbbuf, "lead %s into",
                                x_monnam(u.usteed, steed_article,
                                         "poor", SUPPRESS_SADDLE, FALSE));
-                   else
 #endif
-                       
-                   Sprintf(verbbuf, "%s", Levitation ? (const char *)"float" :
-                               locomotion(youmonst.data, "stumble"));
-                   You("%s into %s spider web!",
-                       verbbuf, a_your[trap->madeby_u]);
+                   } else {
+                       Sprintf(verbbuf, "%s into",
+                               Levitation ? (const char *)"float" :
+                                 locomotion(youmonst.data, "stumble"));
+                   }
+                   You("%s %s spider web!", verbbuf, a_your[trap->madeby_u]);
                }
                u.utraptype = TT_WEB;
 
@@ -1833,7 +1838,8 @@ register struct monst *mtmp;
        } else {
            register int tt = trap->ttyp;
            boolean in_sight, tear_web, see_it,
-                   inescapable = ((tt == HOLE || tt == PIT) &&
+                   inescapable = force_mintrap ||
+                               ((tt == HOLE || tt == PIT) &&
                                   In_sokoban(&u.uz) && !trap->madeby_u);
            const char *fallverb;
 
@@ -1940,6 +1946,12 @@ register struct monst *mtmp;
                                   && !Deaf)
                                    You_hear("the roaring of an angry bear!");
                            }
+                       } else if (force_mintrap) {
+                           if (in_sight) {
+                               pline("%s evades %s bear trap!",
+                                     Monnam(mtmp), a_your[trap->madeby_u]);
+                               seetrap(trap);
+                           }
                        }
                        break;
 
@@ -2079,6 +2091,15 @@ glovecheck:                  target = which_armor(mtmp, W_ARMG);
                        if (is_flyer(mptr) || is_floater(mptr) ||
                                (mtmp->wormno && count_wsegs(mtmp) > 5) ||
                                is_clinger(mptr)) {
+                           if (force_mintrap && !In_sokoban(&u.uz)) {
+                               /* openfallingtrap; not inescapable here */
+                               if (in_sight) {
+                                   seetrap(trap);
+                                   pline("%s doesn't fall into the pit.",
+                                         Monnam(mtmp));
+                               }
+                               break;  /* inescapable = FALSE; */
+                           }
                            if (!inescapable) break;    /* avoids trap */
                            fallverb = "is dragged";    /* sokoban pit */
                        }
@@ -2109,6 +2130,21 @@ glovecheck:                  target = which_armor(mtmp, W_ARMG);
                                mptr == &mons[PM_WUMPUS] ||
                                (mtmp->wormno && count_wsegs(mtmp) > 5) ||
                                mptr->msize >= MZ_HUGE) {
+                           if (force_mintrap && !In_sokoban(&u.uz)) {
+                               /* openfallingtrap; not inescapable here */
+                               if (in_sight) {
+                                   seetrap(trap);
+                                   if (tt == TRAPDOOR)
+                                       pline(
+                           "A trap door opens, but %s doesn't fall through.",
+                                             mon_nam(mtmp));
+                                   else /* (tt == HOLE) */
+                                       pline(
+                                         "%s doesn't fall through the hole.",
+                                             Monnam(mtmp));
+                               }
+                               break;  /* inescapable = FALSE; */
+                           }
                            if (inescapable) {  /* sokoban hole */
                                if (in_sight) {
                                    pline("%s seems to be yanked down!",
@@ -2207,6 +2243,12 @@ glovecheck:                  target = which_armor(mtmp, W_ARMG);
                                      Monnam(mtmp), a_your[trap->madeby_u]);
                            deltrap(trap);
                            newsym(mtmp->mx, mtmp->my);
+                       } else if (force_mintrap && !mtmp->mtrapped) {
+                           if (in_sight) {
+                               pline("%s avoids %s spider web!",
+                                     Monnam(mtmp), a_your[trap->madeby_u]);
+                               seetrap(trap);
+                           }
                        }
                        break;
 
@@ -3817,6 +3859,148 @@ boolean force;
        }
 }
 
+/* for magic unlocking; returns true if targetted monster (which might
+   be hero) gets untrapped; the trap remains intact */
+boolean
+openholdingtrap(mon, noticed)
+struct monst *mon;
+boolean *noticed;      /* set to true iff hero notices the effect; */
+{                      /* otherwise left with its previous value intact */
+    struct trap *t;
+    char buf[BUFSZ];
+    const char *trapdescr, *which;
+    boolean ishero = (mon == &youmonst);
+
+#ifdef STEED
+    if (mon == u.usteed) ishero = TRUE;
+#endif
+    t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
+    /* if no trap here or it's not a holding trap, we're done */
+    if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE;
+
+    trapdescr = defsyms[trap_to_defsym(t->ttyp)].explanation;
+    which = t->tseen ? the_your[t->madeby_u] :
+               index(vowels, *trapdescr) ? "an" : "a";
+
+    if (ishero) {
+       if (!u.utrap) return FALSE;
+       u.utrap = 0;    /* released regardless of type */
+       *noticed = TRUE;
+       /* give message only if trap was the expected type */
+       if (u.utraptype == TT_BEARTRAP || u.utraptype == TT_WEB) {
+#ifdef STEED
+           if (u.usteed)
+               Sprintf(buf, "%s is", noit_Monnam(u.usteed));
+           else
+#endif
+               Strcpy(buf, "You are");
+           pline("%s released from %s %s.", buf, which, trapdescr);
+       }
+    } else {
+       if (!mon->mtrapped) return FALSE;
+       mon->mtrapped = 0;
+       if (canspotmon(mon)) {
+           *noticed = TRUE;
+           pline("%s is released from %s %s.",
+                 Monnam(mon), which, trapdescr);
+       } else if (cansee(t->tx, t->ty) && t->tseen) {
+           *noticed = TRUE;
+           if (t->ttyp == WEB)
+               pline("%s is released from %s %s.",
+                     Something, which, trapdescr);
+           else /* BEAR_TRAP */
+               pline("%s %s opens.", upstart(strcpy(buf, which)), trapdescr);
+       }
+       /* might pacify monster if adjacent */
+       if (rn2(2) && distu(mon->mx, mon->my) <= 2) reward_untrap(t, mon);
+    }
+    return TRUE;
+}
+
+/* for magic locking; returns true if targetted monster (which might
+   be hero) gets hit by a trap (might avoid actually becoming trapped) */
+boolean
+closeholdingtrap(mon, noticed)
+struct monst *mon;
+boolean *noticed;      /* set to true iff hero notices the effect; */
+{                      /* otherwise left with its previous value intact */
+    struct trap *t;
+    unsigned dotrapflags;
+    boolean ishero = (mon == &youmonst), result;
+
+#ifdef STEED
+    if (mon == u.usteed) ishero = TRUE;
+#endif
+    t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
+    /* if no trap here or it's not a holding trap, we're done */
+    if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE;
+
+    if (ishero) {
+       if (u.utrap) return FALSE;      /* already trapped */
+       *noticed = TRUE;
+       dotrapflags = FORCETRAP;
+#ifdef STEED
+       /* dotrap calls mintrap when mounted hero encounters a web */
+       if (u.usteed) dotrapflags |= NOWEBMSG;
+#endif
+       ++force_mintrap;
+       dotrap(t, dotrapflags);
+       --force_mintrap;
+       result = (u.utrap != 0);
+    } else {
+       if (mon->mtrapped) return FALSE;        /* already trapped */
+       /* you notice it if you see the trap close/tremble/whatever
+          or if you sense the monster who becomes trapped */
+       *noticed = cansee(t->tx, t->ty) || canspotmon(mon);
+       ++force_mintrap;
+       result = (mintrap(mon) != 0);
+       --force_mintrap;
+    }
+    return result;
+}
+
+/* for magic unlocking; returns true if targetted monster (which might
+   be hero) gets hit by a trap (target might avoid its effect) */
+boolean
+openfallingtrap(mon, trapdoor_only, noticed)
+struct monst *mon;
+boolean trapdoor_only;
+boolean *noticed;      /* set to true iff hero notices the effect; */
+{                      /* otherwise left with its previous value intact */
+    struct trap *t;
+    boolean ishero = (mon == &youmonst), result;
+
+#ifdef STEED
+    if (mon == u.usteed) ishero = TRUE;
+#endif
+    t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
+    /* if no trap here or it's not a falling trap, we're done
+       (note: falling rock traps have a trapdoor in the ceiling) */
+    if (!t || ((t->ttyp != TRAPDOOR && t->ttyp != ROCKTRAP) &&
+           (trapdoor_only ||
+               (t->ttyp != HOLE && t->ttyp != PIT && t->ttyp != SPIKED_PIT))))
+       return FALSE;
+
+    if (ishero) {
+       if (u.utrap) return FALSE;      /* already trapped */
+       *noticed = TRUE;
+       dotrap(t, FORCETRAP);
+       result = (u.utrap != 0);
+    } else {
+       if (mon->mtrapped) return FALSE;        /* already trapped */
+       /* you notice it if you see the trap close/tremble/whatever
+          or if you sense the monster who becomes trapped */
+       *noticed = cansee(t->tx, t->ty) || canspotmon(mon);
+       /* monster will be angered; mintrap doesn't handle that */
+       wakeup(mon);
+       ++force_mintrap;
+       result = (mintrap(mon) != 0);
+       --force_mintrap;
+       /* mon might now be on the migrating monsters list */
+    }
+    return TRUE;
+}
+
 /* only called when the player is doing something to the chest directly */
 boolean
 chest_trap(obj, bodypart, disarm)
index 76a400316354cc8b1a3b56feb75697c99b4f2605..db7cc3f43483c2181241068f20348f18a61c6517 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -270,10 +270,9 @@ struct obj *otmp;
                }
                break;
            }
-       case WAN_NOTHING:
        case WAN_LOCKING:
        case SPE_WIZARD_LOCK:
-               wake = FALSE;
+               wake = closeholdingtrap(mtmp, &learn_it);
                break;
        case WAN_PROBING:
                wake = FALSE;
@@ -290,8 +289,16 @@ struct obj *otmp;
                                else pline("%s opens its mouth!", Monnam(mtmp));
                        }
                        expels(mtmp, mtmp->data, TRUE);
+               /* zap which hits steed will only release saddle if it
+                  doesn't hit a holding or falling trap; playability
+                  here overrides the more logical target ordering */
+               } else if (openholdingtrap(mtmp, &learn_it)) {
+                       break;
+               } else if (openfallingtrap(mtmp, TRUE, &learn_it)) {
+                       /* mtmp might now be on the migrating monsters list */
+                       break;
 #ifdef STEED
-               } else if (!!(obj = which_armor(mtmp, W_SADDLE))) {
+               } else if ((obj = which_armor(mtmp, W_SADDLE)) != 0) {
                        mtmp->misc_worn_check &= ~obj->owornmask;
                        update_mon_intrinsics(mtmp, obj, FALSE, FALSE);
                        obj->owornmask = 0L;
@@ -384,6 +391,9 @@ struct obj *otmp;
                    }
                }
                break;
+       case WAN_NOTHING:
+               wake = FALSE;
+               break;
        default:
                impossible("What an interesting effect (%d)", otyp);
                break;
@@ -2198,13 +2208,22 @@ boolean ordinary;
                        learn_it = TRUE;
                        unpunish();
                    }
+                   if (u.utrap) {      /* escape web or bear trap */
+                       (void) openholdingtrap(&youmonst, &learn_it);
+                   } else {    /* trigger previously escaped trapdoor */
+                       (void) openfallingtrap(&youmonst, TRUE, &learn_it);
+                   }
+                   break;
+               case WAN_LOCKING:
+               case SPE_WIZARD_LOCK:
+                   if (!u.utrap) {
+                       (void) closeholdingtrap(&youmonst, &learn_it);
+                   }
                    break;
                case WAN_DIGGING:
                case SPE_DIG:
                case SPE_DETECT_UNSEEN:
                case WAN_NOTHING:
-               case WAN_LOCKING:
-               case SPE_WIZARD_LOCK:
                    break;
                case WAN_PROBING:
                  {
@@ -2445,6 +2464,13 @@ struct obj *obj; /* wand or spell */
                pline_The("stairs seem to ripple momentarily.");
                disclose = TRUE;
            }
+           /* down will release you from bear trap or web */
+           if (u.dz > 0 && u.utrap) {
+               (void) openholdingtrap(&youmonst, &disclose);
+           /* down will trigger trapdoor, hole, or [spiked-] pit */
+           } else if (u.dz > 0 && !u.utrap) {
+               (void) openfallingtrap(&youmonst, FALSE, &disclose);
+           }
            break;
        case WAN_STRIKING:
        case SPE_FORCE_BOLT:
@@ -2453,8 +2479,8 @@ struct obj *obj;  /* wand or spell */
        case WAN_LOCKING:
        case SPE_WIZARD_LOCK:
            /* down at open bridge or up or down at open portcullis */
-           if ((levl[x][y].typ == DRAWBRIDGE_DOWN) ? (u.dz > 0) :
-                       (is_drawbridge_wall(x,y) && !is_db_wall(x,y)) &&
+           if (((levl[x][y].typ == DRAWBRIDGE_DOWN) ? (u.dz > 0) :
+                       (is_drawbridge_wall(x,y) && !is_db_wall(x,y))) &&
                    find_drawbridge(&xx, &yy)) {
                if (!striking)
                    close_drawbridge(xx, yy);
@@ -2476,21 +2502,38 @@ struct obj *obj;        /* wand or spell */
                    stackobj(otmp);
                }
                newsym(x, y);
-           } else if (!striking && ttmp && ttmp->ttyp == TRAPDOOR && u.dz > 0) {
-               if (!Blind) {
-                       if (ttmp->tseen) {
-                               pline("A trap door beneath you closes up then vanishes.");
-                               disclose = TRUE;
-                       } else {
-                               You_see("a swirl of %s beneath you.",
-                                       is_ice(x,y) ? "frost" : "dust");
-                       }
-               } else {
-                       You_hear("a twang followed by a thud.");
+           } else if (u.dz > 0 && ttmp) {
+               if (!striking && closeholdingtrap(&youmonst, &disclose)) {
+                   ;   /* now stuck in web or bear trap */
+               } else if (striking && ttmp->ttyp == TRAPDOOR) {
+                   /* striking transforms trapdoor into hole */
+                   if (Blind && !ttmp->tseen) {
+                       pline("%s beneath you shatters.", Something);
+                   } else if (!ttmp->tseen) {  /* => !Blind */
+                       pline("There's a trapdoor beneath you; it shatters.");
+                   } else {
+                       pline("The trapdoor beneath you shatters.");
+                       disclose = TRUE;
+                   }
+                   ttmp->ttyp = HOLE;
+                   ttmp->tseen = 1;
+                   newsym(x, y);
+                   /* might fall down hole */
+                   dotrap(ttmp, 0);
+               } else if (!striking && ttmp->ttyp == HOLE) {
+                   /* locking transforms hole into trapdoor */
+                   ttmp->ttyp = TRAPDOOR;
+                   if (Blind || !ttmp->tseen) {
+                       pline("Some %s swirls beneath you.",
+                             is_ice(x,y) ? "frost" : "dust");
+                   } else {
+                       ttmp->tseen = 1;
+                       newsym(x, y);
+                       pline("A trapdoor appears beneath you.");
+                       disclose = TRUE;
+                   }
+                   /* hadn't fallen down hole; won't fall now */
                }
-               deltrap(ttmp);
-               ttmp = (struct trap *)0;
-               newsym(x, y);
            }
            break;
        case SPE_STONE_TO_FLESH: