OSDN Git Service

update year to 2022
[jnethack/source.git] / src / lock.c
index 41f56aa..b7b5bad 100644 (file)
@@ -1,25 +1,28 @@
-/* NetHack 3.6 lock.c  $NHDT-Date: 1446955300 2015/11/08 04:01:40 $  $NHDT-Branch: master $:$NHDT-Revision: 1.67 $ */
+/* NetHack 3.6 lock.c  $NHDT-Date: 1548978605 2019/01/31 23:50:05 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.84 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Robert Patrick Rankin, 2011. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2022            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 
-STATIC_PTR int NDECL(picklock);
-STATIC_PTR int NDECL(forcelock);
-
 /* at most one of `door' and `box' should be non-null at any given time */
 STATIC_VAR NEARDATA struct xlock_s {
     struct rm *door;
     struct obj *box;
     int picktyp, /* key|pick|card for unlock, sharp vs blunt for #force */
         chance, usedtime;
+    boolean magic_key;
 } xlock;
 
+/* occupation callbacks */
+STATIC_PTR int NDECL(picklock);
+STATIC_PTR int NDECL(forcelock);
+
 STATIC_DCL const char *NDECL(lock_action);
 STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P));
 STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *));
@@ -51,7 +54,7 @@ lock_action()
 {
     /* "unlocking"+2 == "locking" */
     static const char *actions[] = {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         "unlocking the door",   /* [0] */
         "unlocking the chest",  /* [1] */
         "unlocking the box",    /* [2] */
@@ -95,7 +98,8 @@ STATIC_PTR int
 picklock(VOID_ARGS)
 {
     if (xlock.box) {
-        if ((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) {
+        if (xlock.box->where != OBJ_FLOOR
+            || xlock.box->ox != u.ux || xlock.box->oy != u.uy) {
             return ((xlock.usedtime = 0)); /* you or it moved */
         }
     } else { /* door */
@@ -136,6 +140,57 @@ picklock(VOID_ARGS)
     if (rn2(100) >= xlock.chance)
         return 1; /* still busy */
 
+    /* using the Master Key of Thievery finds traps if its bless/curse
+       state is adequate (non-cursed for rogues, blessed for others;
+       checked when setting up 'xlock') */
+    if ((!xlock.door ? (int) xlock.box->otrapped
+                     : (xlock.door->doormask & D_TRAPPED) != 0)
+        && xlock.magic_key) {
+        xlock.chance += 20; /* less effort needed next time */
+        /* unfortunately we don't have a 'tknown' flag to record
+           "known to be trapped" so declining to disarm and then
+           retrying lock manipulation will find it all over again */
+/*JP
+        if (yn("You find a trap!  Do you want to try to disarm it?") == 'y') {
+*/
+        if (yn("ã©\82ð\8c©\82Â\82¯\82½\81I\8aO\82µ\82Ü\82·\82©\81H") == 'y') {
+            const char *what;
+            boolean alreadyunlocked;
+
+            /* disarming while using magic key always succeeds */
+            if (xlock.door) {
+                xlock.door->doormask &= ~D_TRAPPED;
+/*JP
+                what = "door";
+*/
+                what = "\94à";
+                alreadyunlocked = !(xlock.door->doormask & D_LOCKED);
+            } else {
+                xlock.box->otrapped = 0;
+/*JP
+                what = (xlock.box->otyp == CHEST) ? "chest" : "box";
+*/
+                what = (xlock.box->otyp == CHEST) ? "\95ó\94 " : "\94 ";
+                alreadyunlocked = !xlock.box->olocked;
+            }
+#if 0 /*JP:T*/
+            You("succeed in disarming the trap.  The %s is still %slocked.",
+                what, alreadyunlocked ? "un" : "");
+#else
+            You("ã©\82ð\8aO\82µ\82½\81D%s\82Í\8c®\82ª%s\82Ü\82Ü\82¾\81D",
+                what, alreadyunlocked ? "\8aJ\82¢\82½" : "\82©\82©\82Á\82½");
+#endif
+            exercise(A_WIS, TRUE);
+        } else {
+/*JP
+            You("stop %s.", lock_action());
+*/
+            You("%s\82Ì\82ð\82â\82ß\82½\81D", lock_action());
+            exercise(A_WIS, FALSE);
+        }
+        return ((xlock.usedtime = 0));
+    }
+
 /*JP
     You("succeed in %s.", lock_action());
 */
@@ -149,7 +204,7 @@ picklock(VOID_ARGS)
             xlock.door->doormask = D_NODOOR;
             unblock_point(u.ux + u.dx, u.uy + u.dy);
             if (*in_rooms(u.ux + u.dx, u.uy + u.dy, SHOPBASE))
-                add_damage(u.ux + u.dx, u.uy + u.dy, 0L);
+                add_damage(u.ux + u.dx, u.uy + u.dy, SHOP_DOOR_COST);
             newsym(u.ux + u.dx, u.uy + u.dy);
         } else if (xlock.door->doormask & D_LOCKED)
             xlock.door->doormask = D_CLOSED;
@@ -198,12 +253,13 @@ boolean destroyit;
             if (!rn2(3) || otmp->oclass == POTION_CLASS) {
                 chest_shatter_msg(otmp);
                 if (costly)
-                    loss +=
-                        stolen_value(otmp, u.ux, u.uy, peaceful_shk, TRUE);
+                    loss += stolen_value(otmp, u.ux, u.uy, peaceful_shk, TRUE);
                 if (otmp->quan == 1L) {
                     obfree(otmp, (struct obj *) 0);
                     continue;
                 }
+                /* this works because we're sure to have at least 1 left;
+                   otherwise it would fail since otmp is not in inventory */
                 useup(otmp);
             }
             if (box->otyp == ICE_BOX && otmp->otyp == CORPSE) {
@@ -247,7 +303,7 @@ forcelock(VOID_ARGS)
             /* for a +0 weapon, probability that it survives an unsuccessful
              * attempt to force the lock is (.992)^50 = .67
              */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%sour %s broke!", (uwep->quan > 1L) ? "One of y" : "Y",
                   xname(uwep));
 #else
@@ -271,18 +327,44 @@ forcelock(VOID_ARGS)
     You("succeed in forcing the lock.");
 */
     pline("\8c®\82ð\82±\82\8aJ\82¯\82½\81D");
+    exercise(xlock.picktyp ? A_DEX : A_STR, TRUE);
+    /* breakchestlock() might destroy xlock.box; if so, xlock context will
+       be cleared (delobj -> obfree -> maybe_reset_pick); but it might not,
+       so explicitly clear that manually */
     breakchestlock(xlock.box, (boolean) (!xlock.picktyp && !rn2(3)));
+    reset_pick(); /* lock-picking context is no longer valid */
 
-    exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
-    return ((xlock.usedtime = 0));
+    return 0;
 }
 
 void
 reset_pick()
 {
     xlock.usedtime = xlock.chance = xlock.picktyp = 0;
-    xlock.door = 0;
-    xlock.box = 0;
+    xlock.magic_key = FALSE;
+    xlock.door = (struct rm *) 0;
+    xlock.box = (struct obj *) 0;
+}
+
+/* level change or object deletion; context may no longer be valid */
+void
+maybe_reset_pick(container)
+struct obj *container; /* passed from obfree() */
+{
+    /*
+     * If a specific container, only clear context if it is for that
+     * particular container (which is being deleted).  Other stuff on
+     * the current dungeon level remains valid.
+     * However if 'container' is Null, clear context if not carrying
+     * xlock.box (which might be Null if context is for a door).
+     * Used for changing levels, where a floor container or a door is
+     * being left behind and won't be valid on the new level but a
+     * carried container will still be.  There might not be any context,
+     * in which case redundantly clearing it is harmless.
+     */
+    if (container ? (container == xlock.box)
+                  : (!xlock.box || !carried(xlock.box)))
+        reset_pick();
 }
 
 /* for doapply(); if player gives a direction or resumes an interrupted
@@ -318,6 +400,7 @@ struct obj *pick;
             const char *what = (picktyp == LOCK_PICK) ? "pick" : "key";
 */
             const char *what = (picktyp == LOCK_PICK) ? "\8c®\8aJ\82¯\8aí\8bï" : "\8c®";
+
             if (picktyp == CREDIT_CARD)
 /*JP
                 what = "card";
@@ -343,6 +426,7 @@ struct obj *pick;
             You("resume your attempt at %s.", action);
 */
             pline("%s\82Ì\82ð\8dÄ\8aJ\82µ\82½\81D", action);
+            xlock.magic_key = is_magic_key(&youmonst, pick);
             set_occupation(picklock, action, 0);
             return PICKLOCK_DID_SOMETHING;
         }
@@ -355,13 +439,19 @@ struct obj *pick;
         You("%s\82ð\82Â\82©\82Þ\82±\82Æ\82ª\82Å\82«\82È\82¢\81I\8eè\82ª\82È\82¢\82ñ\82¾\82à\82Ì\81I", xname(pick));
         return PICKLOCK_DID_NOTHING;
     } else if (u.uswallow) {
+#if 0 /*JP:T*/
         You_cant("%sunlock %s.", (picktyp == CREDIT_CARD) ? "" : "lock or ",
                  mon_nam(u.ustuck));
+#else
+        You_cant("%s\82ð%s\82È\82¢\81D", mon_nam(u.ustuck),
+                 (picktyp == CREDIT_CARD) ? "\8aJ\82¯\82ç\82ê" : "\8aJ\82¯\95Â\82ß\82Å\82«");
+#endif
         return PICKLOCK_DID_NOTHING;
     }
 
-    if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD
-         && picktyp != SKELETON_KEY)) {
+    if (picktyp != LOCK_PICK
+        && picktyp != CREDIT_CARD
+        && picktyp != SKELETON_KEY) {
         impossible("picking lock with object %d?", picktyp);
         return PICKLOCK_DID_NOTHING;
     }
@@ -376,11 +466,13 @@ struct obj *pick;
     if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */
         const char *verb;
         char qsfx[QBUFSZ];
+#if 0 /*JP*/
         boolean it;
+#endif
         int count;
 
         if (u.dz < 0) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             There("isn't any sort of lock up %s.",
                   Levitation ? "here" : "there");
 #else
@@ -396,9 +488,9 @@ struct obj *pick;
             return PICKLOCK_LEARNED_SOMETHING;
         } else if (is_pool(u.ux, u.uy) && !Underwater) {
 /*JP
-            pline_The("water has no lock.");
+            pline_The("%s has no lock.", hliquid("water"));
 */
-            pline("\90\85\82É\8fù\91O\82Í\82È\82¢\81D");
+            pline_The("%s\82É\8fù\91O\82Í\82È\82¢\81D", hliquid("\90\85"));
             return PICKLOCK_LEARNED_SOMETHING;
         }
 
@@ -414,7 +506,9 @@ struct obj *pick;
                     You("\82±\82±\82©\82ç%s\82É\93Í\82©\82È\82¢\81D", the(xname(otmp)));
                     return PICKLOCK_LEARNED_SOMETHING;
                 }
+#if 0 /*JP*/
                 it = 0;
+#endif
                 if (otmp->obroken)
 /*JP
                     verb = "fix";
@@ -424,12 +518,12 @@ struct obj *pick;
 /*JP
                     verb = "lock", it = 1;
 */
-                    verb = "\8c®\82ð\82©\82¯\82é", it = 1;
+                    verb = "\8c®\82ð\82©\82¯\82é";
                 else if (picktyp != LOCK_PICK)
 /*JP
                     verb = "unlock", it = 1;
 */
-                    verb = "\8c®\82ð\82Í\82¸\82·", it = 1;
+                    verb = "\8c®\82ð\82Í\82¸\82·";
                 else
 /*JP
                     verb = "pick";
@@ -441,7 +535,7 @@ struct obj *pick;
                 Sprintf(qsfx, " here; %s %s?", verb, it ? "it" : "its lock");
 */
                 Sprintf(qsfx, "\82ª\82 \82é\81D%s\81H", verb);
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 (void) safe_qbuf(qbuf, "There is ", qsfx, otmp, doname,
                                  ansimpleoname, "a box");
 #else
@@ -464,7 +558,7 @@ struct obj *pick;
                     return PICKLOCK_LEARNED_SOMETHING;
                 } else if (picktyp == CREDIT_CARD && !otmp->olocked) {
                     /* credit cards are only good for unlocking */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     You_cant("do that with %s.",
                              an(simple_typename(picktyp)));
 #else
@@ -489,7 +583,6 @@ struct obj *pick;
                 if (otmp->cursed)
                     ch /= 2;
 
-                xlock.picktyp = picktyp;
                 xlock.box = otmp;
                 xlock.door = 0;
                 break;
@@ -515,8 +608,8 @@ struct obj *pick;
 
         door = &levl[cc.x][cc.y];
         mtmp = m_at(cc.x, cc.y);
-        if (mtmp && canseemon(mtmp) && mtmp->m_ap_type != M_AP_FURNITURE
-            && mtmp->m_ap_type != M_AP_OBJECT) {
+        if (mtmp && canseemon(mtmp) && M_AP_TYPE(mtmp) != M_AP_FURNITURE
+            && M_AP_TYPE(mtmp) != M_AP_OBJECT) {
             if (picktyp == CREDIT_CARD
                 && (mtmp->isshk || mtmp->data == &mons[PM_ORACLE]))
 /*JP
@@ -524,7 +617,7 @@ struct obj *pick;
 */
                 verbalize("\82¢\82Â\82à\83j\83R\83j\83R\8c»\8bà\95¥\82¢\81D");
             else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("I don't think %s would appreciate that.",
                       mon_nam(mtmp));
 #else
@@ -534,7 +627,7 @@ struct obj *pick;
         } else if (mtmp && is_door_mappear(mtmp)) {
             /* "The door actually was a <mimic>!" */
             stumble_onto_mimic(mtmp);
-            /* mimic might keep the key (50% chance, 10% for PYEC) */
+            /* mimic might keep the key (50% chance, 10% for PYEC or MKoT) */
             maybe_absorb_item(mtmp, pick, 50, 10);
             return PICKLOCK_LEARNED_SOMETHING;
         }
@@ -580,7 +673,7 @@ struct obj *pick;
                 return PICKLOCK_LEARNED_SOMETHING;
             }
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             Sprintf(qbuf, "%s it?",
                     (door->doormask & D_LOCKED) ? "Unlock" : "Lock");
 #else
@@ -612,6 +705,7 @@ struct obj *pick;
     context.move = 0;
     xlock.chance = ch;
     xlock.picktyp = picktyp;
+    xlock.magic_key = is_magic_key(&youmonst, pick);
     xlock.usedtime = 0;
     set_occupation(picklock, lock_action(), 0);
     return PICKLOCK_DID_SOMETHING;
@@ -626,7 +720,10 @@ doforce()
     char qbuf[QBUFSZ];
 
     if (u.uswallow) {
+/*JP
         You_cant("force anything from inside here.");
+*/
+        You_cant("\93à\91¤\82©\82ç\82±\82\8aJ\82¯\82é\82±\82Æ\82Í\82Å\82«\82È\82¢\81D");
         return 0;
     }
     if (!uwep /* proper type test */
@@ -635,7 +732,7 @@ doforce()
                   || objects[uwep->otyp].oc_skill == P_FLAIL
                   || objects[uwep->otyp].oc_skill > P_LANCE)
                : uwep->oclass != ROCK_CLASS)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You_cant("force anything %s weapon.",
                  !uwep ? "when not wielding a"
                        : (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep))
@@ -673,21 +770,27 @@ doforce()
     for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere)
         if (Is_box(otmp)) {
             if (otmp->obroken || !otmp->olocked) {
-#if 0 /*JP*/
-                There("is %s here, but its lock is already %s.", doname(otmp),
-                      otmp->obroken ? "broken" : "unlocked");
+                /* force doname() to omit known "broken" or "unlocked"
+                   prefix so that the message isn't worded redundantly;
+                   since we're about to set lknown, there's no need to
+                   remember and then reset its current value */
+                otmp->lknown = 0;
+#if 0 /*JP:T*/
+                There("is %s here, but its lock is already %s.",
+                      doname(otmp), otmp->obroken ? "broken" : "unlocked");
 #else
-                pline("\82±\82±\82É\82Í%s\82ª\82 \82é\81C\82µ\82©\82µ\82»\82Ì\8c®\82Í\82à\82¤%s\81D", doname(otmp),
+                pline("\82±\82±\82É\82Í%s\82ª\82 \82é\81D\82µ\82©\82µ\82»\82Ì\8c®\82Í\82à\82¤%s\81D",
+                      doname(otmp),
                       otmp->obroken ? "\89ó\82ê\82Ä\82¢\82é" : "\82Í\82¸\82³\82ê\82Ä\82¢\82é");
 #endif
                 otmp->lknown = 1;
                 continue;
             }
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             (void) safe_qbuf(qbuf, "There is ", " here; force its lock?",
                              otmp, doname, ansimpleoname, "a box");
 #else
-            (void) safe_qbuf(qbuf, "\82±\82±\82É\82Í", "\82ª\82 \82é\81C\8c®\82ð\82±\82\8aJ\82¯\82Ü\82·\82©\81H",
+            (void) safe_qbuf(qbuf, "\82±\82±\82É\82Í", "\82ª\82 \82é\81D\8c®\82ð\82±\82\8aJ\82¯\82Ü\82·\82©\81H",
                              otmp, doname, ansimpleoname, "\94 ");
 #endif
             otmp->lknown = 1;
@@ -711,6 +814,7 @@ doforce()
             xlock.box = otmp;
             xlock.chance = objects[uwep->otyp].oc_wldam * 2;
             xlock.picktyp = picktyp;
+            xlock.magic_key = FALSE;
             xlock.usedtime = 0;
             break;
         }
@@ -781,8 +885,9 @@ int x, y;
     } else if (!get_adjacent_loc((char *) 0, (char *) 0, u.ux, u.uy, &cc))
         return 0;
 
+    /* open at yourself/up/down */
     if ((cc.x == u.ux) && (cc.y == u.uy))
-        return 0;
+        return doloot();
 
     if (stumble_on_door_mimic(cc.x, cc.y))
         return 1;
@@ -816,6 +921,13 @@ int x, y;
             pline_The("drawbridge is already open.");
 */
             pline_The("\92µ\82Ë\8b´\82Í\82à\82¤\8aJ\82¢\82Ä\82¢\82é\81D");
+        else if (container_at(cc.x, cc.y, TRUE))
+#if 0 /*JP:T*/
+            pline("%s like something lootable over there.",
+                  Blind ? "Feels" : "Seems");
+#else
+            pline("\82±\82±\82É\82Í\89½\82©\93ü\82ê\95¨\82ª\82 \82é\82æ\82¤\82¾\81D");
+#endif
         else
 /*JP
             You("%s no door there.", Blind ? "feel" : "see");
@@ -881,7 +993,7 @@ int x, y;
             b_trapped("\94à", FINGER);
             door->doormask = D_NODOOR;
             if (*in_rooms(cc.x, cc.y, SHOPBASE))
-                add_damage(cc.x, cc.y, 0L);
+                add_damage(cc.x, cc.y, SHOP_DOOR_COST);
         } else
             door->doormask = D_ISOPEN;
         feel_newsym(cc.x, cc.y); /* the hero knows she opened it */
@@ -904,13 +1016,13 @@ boolean quietly;
 {
     register struct monst *mtmp = m_at(x, y);
 
-    if (mtmp && mtmp->m_ap_type != M_AP_FURNITURE) {
-        if (mtmp->m_ap_type == M_AP_OBJECT)
+    if (mtmp && M_AP_TYPE(mtmp) != M_AP_FURNITURE) {
+        if (M_AP_TYPE(mtmp) == M_AP_OBJECT)
             goto objhere;
         if (!quietly) {
             if ((mtmp->mx != x) || (mtmp->my != y)) {
                 /* worm tail */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s%s blocks the way!",
                       !canspotmon(mtmp) ? Something : s_suffix(Monnam(mtmp)),
                       !canspotmon(mtmp) ? "" : " tail");
@@ -920,7 +1032,7 @@ boolean quietly;
                       !canspotmon(mtmp) ? "" : "\82Ì\90K\94ö");
 #endif
             } else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s blocks the way!",
                       !canspotmon(mtmp) ? "Some creature" : Monnam(mtmp));
 #else
@@ -934,7 +1046,7 @@ boolean quietly;
         return TRUE;
     }
     if (OBJ_AT(x, y)) {
   objhere:
+ objhere:
         if (!quietly)
 /*JP
             pline("%s's in the way.", Something);
@@ -1018,7 +1130,7 @@ doclose()
 */
             pline("\92µ\82Ë\8b´\82ð\95Â\82ß\82é\96¾\94\92\82È\95û\96@\82Í\82È\82¢\81D");
         else {
       nodoor:
+ nodoor:
 /*JP
             You("%s no door there.", Blind ? "feel" : "see");
 */
@@ -1201,7 +1313,7 @@ int x, y;
                 return FALSE;
             }
             block_point(x, y);
-            door->typ = SDOOR;
+            door->typ = SDOOR, door->doormask = D_NODOOR;
             if (vis)
 /*JP
                 pline_The("doorway vanishes!");
@@ -1216,7 +1328,7 @@ int x, y;
         /* & trap doors, but is it ever OK for anything else? */
         if (t_at(x, y)) {
             /* maketrap() clears doormask, so it should be NODOOR */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%s springs up in the doorway, but %s.", dustcloud,
                   quickly_dissipates);
 #else
@@ -1231,7 +1343,7 @@ int x, y;
 /*JP
             msg = "The door locks!";
 */
-                msg = "\94à\82É\8c®\82ª\82©\82©\82Á\82½\81I";
+            msg = "\94à\82É\8c®\82ª\82©\82©\82Á\82½\81I";
             break;
         case D_ISOPEN:
 /*JP
@@ -1250,7 +1362,7 @@ int x, y;
 /*JP
                "A cloud of dust springs up and assembles itself into a door!";
 */
-                "\82Ù\82±\82è\82ª\82½\82¿\82±\82ß\81C\8fW\82Ü\82Á\82Ä\94à\82É\82È\82Á\82½\81I";
+               "\82Ù\82±\82è\82ª\82½\82¿\82±\82ß\81C\8fW\82Ü\82Á\82Ä\94à\82É\82È\82Á\82½\81I";
             break;
         default:
             res = FALSE;
@@ -1347,7 +1459,7 @@ struct obj *otmp;
     long save_Blinded;
 
     if (otmp->oclass == POTION_CLASS) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename()));
 #else
         if (Blind)