]> granicus.if.org Git - nethack/commitdiff
sokoban completion (trunk only)
authornethack.rankin <nethack.rankin>
Tue, 30 Aug 2011 22:13:27 +0000 (22:13 +0000)
committernethack.rankin <nethack.rankin>
Tue, 30 Aug 2011 22:13:27 +0000 (22:13 +0000)
     Something I've had in mind for a long time and finally gotten around
to implementing:  when you fill in the last pit or hole of a sokoban level,
it's considered to be completed so luck penalties for unsokobanish things
(breaking a boulder, dropping everything and squeezing onto a boulder's
spot, reading a scroll of earth) stop being assessed and most Sokoban-
specific movement restrictions (against pushing boulders diagonally,
squeezing diagonally between boulders, floating over a pit or hole without
falling in, digging of new holes by monsters) are lifted.  Teleporting,
level teleporting, and phasing through walls are still prohibited when in
the sokoban branch of the dungeon.  (Keeping the non-phasing one in place
prevents taking a shortcut to the final prize in order to bypass the
treasure zoo monsters.)

     This adds level.flags.sokoban_rules, defines Sokoban macro to access
it, and replaces most In_sokoban(&u.uz) tests to check it instead.  It
gets set when a sokoban level is pre-mapped at the end of level creation,
and if it is set then whenever a trap is deleted, the flag gets cleared
if there are no more pits or holes present on the level.

18 files changed:
doc/fixes35.0
include/extern.h
include/patchlevel.h
include/rm.h
src/apply.c
src/ball.c
src/cmd.c
src/detect.c
src/dothrow.c
src/hack.c
src/mklev.c
src/mon.c
src/monmove.c
src/muse.c
src/pickup.c
src/read.c
src/trap.c
src/zap.c

index a38bbbeaa0cd5a97e2a147ac5d3fa4b0ebe11501..771d4d4706fa25dbc20dc013a83316f9a0c14041 100644 (file)
@@ -520,6 +520,8 @@ adopt/adapt/improve the Paranoid_Quit patch; default is paranoid_confirm:pray
        paranoid_confirm:pray    y to confirm #pray; supersedes prayconfirm
        paranoid_confirm:Remove  always pick from inventory for 'R' and 'T'
 flexibility for specifying "detect <foo>" vs "<foo> detection" when wishing
+when a sokoban puzzle has been completed (last pit or hole filled in),
+       stop assessing luck penalties and lift most movement restrictions
 
 
 Platform- and/or Interface-Specific New Features
index c99cc0714d000ec5a8a26bf5f5f72acf1cbf0226..d029c70d6f2fdb9e05150aade77d5deffd1c7a82 100644 (file)
@@ -2318,6 +2318,7 @@ E void NDECL(force_launch_placement);
 E boolean FDECL(uteetering_at_seen_pit, (struct trap *));
 E boolean NDECL(lava_effects);
 E void NDECL(sink_into_lava);
+E void NDECL(sokoban_guilt);
 
 /* ### u_init.c ### */
 
index 9d2c149fbdfa1a96c26ae2b6ca8eaa61515fad5f..dc3ddb29499b491a597dcafb09bb8401c190d0c8 100644 (file)
@@ -13,7 +13,7 @@
  * Incrementing EDITLEVEL can be used to force invalidation of old bones
  * and save files.
  */
-#define EDITLEVEL      46
+#define EDITLEVEL      47
 
 #define COPYRIGHT_BANNER_A \
 "NetHack, Copyright 1985-2011"
index c3a9e4eb880de4b55cb7bc1e4ee057c8b0fc93dc..2dc83368b74f0a4fba6f260d0a7680dd9726ee34 100644 (file)
@@ -512,8 +512,9 @@ struct levelflags {
        Bitfield(hero_memory,1);        /* hero has memory */
        Bitfield(shortsighted,1);       /* monsters are shortsighted */
        Bitfield(graveyard,1);          /* has_morgue, but remains set */
-       Bitfield(is_maze_lev,1);
+       Bitfield(sokoban_rules,1);      /* fill pits and holes w/ boulders */
 
+       Bitfield(is_maze_lev,1);
        Bitfield(is_cavernous_lev,1);
        Bitfield(arboreal, 1);          /* Trees replace rock */
 };
@@ -578,4 +579,7 @@ extern dlevel_t level;      /* structure describing the current level */
 #define m_buried_at(x,y)       (MON_BURIED_AT(x,y) ? level.monsters[x][y] : \
                                                       (struct monst *)0)
 
+/* restricted movement, potential luck penalties */
+#define Sokoban level.flags.sokoban_rules
+
 #endif /* RM_H */
index 979a63860d333b8f5720d8ffd6405bdc95bca141..5c3eee86ed700181f275758996f4497d74cd1db9 100644 (file)
@@ -1526,12 +1526,8 @@ int magic; /* 0=Physical, otherwise skill level */
            if (range < temp)
                range = temp;
            (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t)&range);
-
-           /* A little Sokoban guilt... */
-           if (In_sokoban(&u.uz))
-               change_luck(-1);
-
            teleds(cc.x, cc.y, TRUE);
+           sokoban_guilt();
            nomul(-1);
            nomovemsg = "";
            morehungry(rnd(25));
index 66b28f2a57be09c59995943f5db6989635c1b830..84972fd4b57f5ed637da0e872d2bb10d113f5e0c 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 ball.c  $Date$  $Revision$ */
-/*     SCCS Id: @(#)ball.c     3.5     2007/03/24      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -740,8 +739,7 @@ xchar x, y;
        newsym(u.ux0,u.uy0);            /* clean up old position */
        if (u.ux0 != u.ux || u.uy0 != u.uy) {
            spoteffects(TRUE);
-           if (In_sokoban(&u.uz))
-               change_luck(-1);        /* Sokoban guilt */
+           sokoban_guilt();
        }
     }
 }
index 320456938803a8a9a27ebbef30b4c8f7c3b64a0a..c1ffd7be6905863c5d5735ddc294f89f798ada6c 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -892,6 +892,7 @@ wiz_map_terrain(VOID_ARGS)
        if (level.flags.is_maze_lev)    Strcat(dsc, " maze");
        if (level.flags.is_cavernous_lev) Strcat(dsc, " cave");
        if (level.flags.arboreal)       Strcat(dsc, " tree");
+       if (Sokoban)                    Strcat(dsc, " sokoban-rules");
        /* non-flag info; probably should include dungeon branching
           checks (extra stairs and magic portals) here */
        if (Invocation_lev(&u.uz)) Strcat(dsc, " invoke");
index 038914c687f32f6e5e9ca4e7ec45466138b372ca..bb77ec0e3f52a46badd0b4aebf9d89d082de50dd 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 detect.c        $Date$  $Revision$ */
-/*     SCCS Id: @(#)detect.c   3.5     2007/11/05      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -1363,15 +1362,17 @@ sokoban_detect()
                levl[x][y].seenv = SVALL;
                levl[x][y].waslit = TRUE;
                map_background(x, y, 1);
-               for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
-                   if (obj->otyp == BOULDER)
-                       map_object(obj, 1);
+               if ((obj = sobj_at(BOULDER, x, y)) != 0)
+                   map_object(obj, 1);
            }
 
        /* Map the traps */
        for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
            ttmp->tseen = 1;
            map_trap(ttmp, 1);
+           /* set sokoban_rules when there is at least one pit or hole */
+           if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
+               Sokoban = 1;
        }
 }
 
index cc94a5c2b90bfabea69590bc13703201423e8f4d..7f6c63c997db53c0ce9742d6dea5cb5143f6eb67 100644 (file)
@@ -596,7 +596,7 @@ hurtle_step(arg, x, y)
     if ((u.ux - x) && (u.uy - y) &&
        bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) {
        /* Move at a diagonal. */
-       if (In_sokoban(&u.uz)) {
+       if (Sokoban) {
            You("come to an abrupt halt!");
            return FALSE;
        }
@@ -624,7 +624,7 @@ hurtle_step(arg, x, y)
                dotrap(ttmp,0);
        } else if ((ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT ||
                    ttmp->ttyp == HOLE || ttmp->ttyp == TRAPDOOR) &&
-                  In_sokoban(&u.uz)) {
+                  Sokoban) {
                /* Air currents overcome the recoil */
                dotrap(ttmp,0);
                *range = 0;
@@ -711,8 +711,7 @@ hurtle(dx, dy, range, verbose)
        You("%s in the opposite direction.", range > 1 ? "hurtle" : "float");
     /* if we're in the midst of shooting multiple projectiles, stop */
     endmultishot(TRUE);
-    if (In_sokoban(&u.uz))
-       change_luck(-1);        /* Sokoban guilt */
+    sokoban_guilt();
     uc.x = u.ux;
     uc.y = u.uy;
     /* this setting of cc is only correct if dx and dy are [-1,0,1] only */
index d86a39333d2974d21195f99130376d8fe60a248c..77820e514f89a6f77ff8689387dca6fc526371e7 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 hack.c  $Date$  $Revision$ */
-/*     SCCS Id: @(#)hack.c     3.5     2008/01/22      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -135,7 +134,7 @@ moverock()
            mtmp = m_at(rx, ry);
 
                /* KMH -- Sokoban doesn't let you push boulders diagonally */
-           if (In_sokoban(&u.uz) && u.dx && u.dy) {
+           if (Sokoban && u.dx && u.dy) {
                if (Blind) feel_location(sx,sy);
                pline("%s won't roll diagonally on this %s.",
                      The(xname(otmp)), surface(sx, sy));
@@ -319,17 +318,15 @@ moverock()
 #ifdef STEED
                if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) {
                    You("aren't skilled enough to %s %s from %s.",
-                       (flags.pickup && !In_sokoban(&u.uz))
-                           ? "pick up" : "push aside",
+                       (flags.pickup && !Sokoban) ? "pick up" : "push aside",
                        the(xname(otmp)), y_monnam(u.usteed));
                } else
 #endif
                {
                    pline("However, you can easily %s.",
-                       (flags.pickup && !In_sokoban(&u.uz))
-                           ? "pick it up" : "push it aside");
-                   if (In_sokoban(&u.uz))
-                       change_luck(-1);        /* Sokoban guilt */
+                         (flags.pickup && !Sokoban) ?
+                               "pick it up" : "push it aside");
+                   sokoban_guilt();
                    break;
                }
                break;
@@ -344,8 +341,7 @@ moverock()
                                     && IS_ROCK(levl[sx][u.uy].typ))))
                || verysmall(youmonst.data))) {
                pline("However, you can squeeze yourself into a small opening.");
-               if (In_sokoban(&u.uz))
-                   change_luck(-1);    /* Sokoban guilt */
+               sokoban_guilt();
                break;
            } else
                return (-1);
@@ -571,7 +567,7 @@ bad_rock(mdat,x,y)
 struct permonst *mdat;
 register xchar x,y;
 {
-       return((boolean) ((In_sokoban(&u.uz) && sobj_at(BOULDER,x,y)) ||
+       return((boolean) ((Sokoban && sobj_at(BOULDER,x,y)) ||
               (IS_ROCK(levl[x][y].typ)
                    && (!tunnels(mdat) || needspick(mdat) || !may_dig(x,y))
                    && !(passes_walls(mdat) && may_passwall(x,y)))));
@@ -598,7 +594,7 @@ struct monst *mon;
     if (amt > 600) return 2;
 
     /* Sokoban restriction applies to hero only */
-    if (mon == &youmonst && In_sokoban(&u.uz)) return 3;
+    if (mon == &youmonst && Sokoban) return 3;
 
     /* can squeeze through */
     return 0;
@@ -647,6 +643,7 @@ int mode;
            if (mode == DO_MOVE) {
                if (Is_stronghold(&u.uz) && is_db_wall(x,y))
                    pline_The("drawbridge is up!");
+               /* sokoban restriction stays even after puzzle is solved */
                if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz))
                    pline_The("Sokoban walls resist your ability.");
            }
@@ -737,13 +734,13 @@ int mode;
        return FALSE;
     }
 
-    if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) {
+    if (sobj_at(BOULDER,x,y) && (Sokoban || !Passes_walls)) {
        if (!(Blind || Hallucination) && (context.run >= 2) && mode != TEST_TRAV)
            return FALSE;
        if (mode == DO_MOVE) {
            /* tunneling monsters will chew before pushing */
            if (tunnels(youmonst.data) && !needspick(youmonst.data) &&
-               !In_sokoban(&u.uz)) {
+                   !Sokoban) {
                if (still_chewing(x,y)) return FALSE;
            } else
                if (moverock() < 0) return FALSE;
@@ -751,7 +748,7 @@ int mode;
            struct obj* obj;
 
            /* don't pick two boulders in a row, unless there's a way thru */
-           if (sobj_at(BOULDER,ux,uy) && !In_sokoban(&u.uz)) {
+           if (sobj_at(BOULDER,ux,uy) && !Sokoban) {
                if (!Passes_walls &&
                    !(tunnels(youmonst.data) && !needspick(youmonst.data)) &&
                    !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK) &&
index f510b97a8ca5df6cf38e2717fcbbde5544e1f5aa..813e7fc89740dd73aaff4de51660b37c8a57b01a 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 mklev.c $Date$  $Revision$ */
-/*     SCCS Id: @(#)mklev.c    3.5     2009/02/21      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -590,9 +589,10 @@ clear_level_structures()
        level.flags.nommap = 0;
        level.flags.hero_memory = 1;
        level.flags.shortsighted = 0;
-       level.flags.arboreal = 0;
+       level.flags.sokoban_rules = 0;
        level.flags.is_maze_lev = 0;
        level.flags.is_cavernous_lev = 0;
+       level.flags.arboreal = 0;
 
        nroom = 0;
        rooms[0].hx = -1;
index 12a6ebaed8238f5b7b0fa91f15adac65d4de0a4b..e36482dd1a7ea1f07371d8647307d1985c761459 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -1161,7 +1161,7 @@ impossible("A monster looked at a very strange trap of type %d.", ttmp->ttyp);
                                      || (!is_flyer(mdat)
                                    && !is_floater(mdat)
                                    && !is_clinger(mdat))
-                                     || In_sokoban(&u.uz))
+                                     || Sokoban)
                                && (ttmp->ttyp != SLP_GAS_TRAP ||
                                    !resists_sleep(mon))
                                && (ttmp->ttyp != BEAR_TRAP ||
index 45aa8443f3d98230aee9770a9b07cb277ea0cc7b..531d8a789e089d41ae33f1b3d08e86947b0a764a 100644 (file)
@@ -832,7 +832,7 @@ not_special:
                        && pctload < 75);
                likeobjs = (likes_objs(ptr) && pctload < 75);
                likemagic = (likes_magic(ptr) && pctload < 85);
-               likerock = (throws_rocks(ptr) && pctload < 50 && !In_sokoban(&u.uz));
+               likerock = (throws_rocks(ptr) && pctload < 50 && !Sokoban);
                conceals = hides_under(ptr);
                setlikes = TRUE;
            }
@@ -1247,8 +1247,7 @@ postmov:
                                  && pctload < 75);
                    likeobjs = (likes_objs(ptr) && pctload < 75);
                    likemagic = (likes_magic(ptr) && pctload < 85);
-                   likerock = (throws_rocks(ptr) && pctload < 50 &&
-                               !In_sokoban(&u.uz));
+                   likerock = (throws_rocks(ptr) && pctload < 50 && !Sokoban);
                    conceals = hides_under(ptr);
                }
 
index 3619d1f0109362215e096a47e511b43c95e5f9c5..ff6c20a75431c854ac938024ba8aa08e1d5f8d7f 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 muse.c  $Date$  $Revision$ */
-/*     SCCS Id: @(#)muse.c     3.5     2007/04/14      */
 /*     Copyright (C) 1990 by Ken Arromdee                         */
 /* NetHack may be freely redistributed.  See license for details.  */
 
@@ -448,7 +447,7 @@ struct monst *mtmp;
                    && !mtmp->isshk && !mtmp->isgd && !mtmp->ispriest
                    && !is_floater(mtmp->data)
                    /* monsters digging in Sokoban can ruin things */
-                   && !In_sokoban(&u.uz)
+                   && !Sokoban
                    /* digging wouldn't be effective; assume they know that */
                    && !(levl[x][y].wall_info & W_NONDIGGABLE)
                    && !(Is_botlevel(&u.uz) || In_endgame(&u.uz))
index 0871b8ff915a4686d1a8fa55e51ebea989645ee4..bcf9f4c52b51bf985e533bc59afaa7470d53c9a3 100644 (file)
@@ -1225,7 +1225,7 @@ boolean telekinesis;
 {
     int result, old_wt, new_wt, prev_encumbr, next_encumbr;
 
-    if (obj->otyp == BOULDER && In_sokoban(&u.uz)) {
+    if (obj->otyp == BOULDER && Sokoban) {
        You("cannot get your %s around this %s.",
                        body_part(HAND), xname(obj));
        return -1;
index b139e82660a07bfa83bf57e433fc7c2e5aa3150b..70ede87185261690f3b369095508c40db2c7277c 100644 (file)
@@ -563,7 +563,7 @@ forget_map(howmuch)
 {
        register int zx, zy;
 
-       if (In_sokoban(&u.uz))
+       if (Sokoban)
            return;
 
        known = TRUE;
@@ -1329,17 +1329,16 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
 #ifdef REINCARNATION
                !Is_rogue_level(&u.uz) && 
 #endif
-                (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) {
-               register int x, y;
+                (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) {
+               register int x, y;
 
-               /* Identify the scroll */
-               pline_The("%s rumbles %s you!", ceiling(u.ux,u.uy),
-                         sblessed ? "around" : "above");
-               known = 1;
-               if (In_sokoban(&u.uz))
-                   change_luck(-1);    /* Sokoban guilt */
+               /* Identify the scroll */
+               pline_The("%s rumbles %s you!", ceiling(u.ux,u.uy),
+                         sblessed ? "around" : "above");
+               known = 1;
+               sokoban_guilt();
 
-               /* Loop through the surrounding squares */
+               /* Loop through the surrounding squares */
                if (!scursed) for (x = u.ux-1; x <= u.ux+1; x++) {
                    for (y = u.uy-1; y <= u.uy+1; y++) {
 
index 44123680a08114d54f491c3e75604e2c5d43aad4..67371fcd618f2fbfdbe81e78b6f37d178dd556d6 100644 (file)
@@ -30,10 +30,11 @@ STATIC_DCL void FDECL(join_adjacent_pits, (struct trap *));
 #endif
 STATIC_DCL void FDECL(clear_conjoined_pits, (struct trap *));
 #ifdef STEED
-STATIC_OVL int FDECL(steedintrap, (struct trap *, struct obj *));
-STATIC_OVL boolean FDECL(keep_saddle_with_steedcorpse,
+STATIC_DCL int FDECL(steedintrap, (struct trap *, struct obj *));
+STATIC_DCL boolean FDECL(keep_saddle_with_steedcorpse,
                        (unsigned, struct obj *, struct obj *));
 #endif
+STATIC_DCL void NDECL(maybe_finish_sokoban);
 
 /* mintrap() should take a flags argument, but for time being we use this */
 STATIC_VAR int force_mintrap = 0;
@@ -328,8 +329,7 @@ register int x, y, typ;
                unearth_objs(x, y);
                break;
        }
-       if (ttmp->ttyp == HOLE) ttmp->tseen = 1;  /* You can't hide a hole */
-       else ttmp->tseen = 0;
+       ttmp->tseen = (ttmp->ttyp == HOLE);     /* hide non-holes */
        ttmp->once = 0;
        ttmp->madeby_u = 0;
        ttmp->dst.dnum = -1;
@@ -337,6 +337,11 @@ register int x, y, typ;
        if (!oldplace) {
            ttmp->ntrap = ftrap;
            ftrap = ttmp;
+       } else {
+           /* oldplace;
+              it shouldn't be possible to override a sokoban pit or hole
+              with some other trap, but we'll check just to be safe */
+           if (Sokoban) maybe_finish_sokoban();
        }
        return(ttmp);
 }
@@ -350,8 +355,9 @@ boolean td; /* td == TRUE : trap door or hole */
        const char *dont_fall = 0;
        int newlevel, bottom;
 
-       /* KMH -- You can't escape the Sokoban level traps */
-       if(Blind && Levitation && !In_sokoban(&u.uz)) return;
+       /* we'll fall even while levitating in Sokoban; otherwise, if we
+          won't fall and won't be told that we aren't falling, give up now */
+       if (Blind && Levitation && !Sokoban) return;
 
        bottom = dunlevs_in_dungeon(&u.uz);
        /* when in the upper half of the quest, don't fall past the
@@ -370,8 +376,9 @@ boolean td; /* td == TRUE : trap door or hole */
 
        if(td) {
            struct trap *t = t_at(u.ux,u.uy);
+
            feeltrap(t);
-           if (!In_sokoban(&u.uz)) {
+           if (!Sokoban) {
                if (t->ttyp == TRAPDOOR)
                        pline("A trap door opens up under you!");
                else 
@@ -379,7 +386,7 @@ boolean td; /* td == TRUE : trap door or hole */
            }
        } else pline_The("%s opens up under you!", surface(u.ux,u.uy));
 
-       if (In_sokoban(&u.uz) && Can_fall_thru(&u.uz))
+       if (Sokoban && Can_fall_thru(&u.uz))
            ;   /* KMH -- You can't escape the Sokoban level traps */
        else if(Levitation || u.ustuck
           || (!Can_fall_thru(&u.uz) && !levl[u.ux][u.uy].candig)
@@ -694,7 +701,7 @@ unsigned trflags;
        nomul(0);
 
        /* KMH -- You can't escape the Sokoban level traps */
-       if (In_sokoban(&u.uz) &&
+       if (Sokoban &&
                        (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE ||
                        ttype == TRAPDOOR)) {
            /* The "air currents" message is still appropriate -- even when
@@ -703,8 +710,8 @@ unsigned trflags;
             * check, clinging to the ceiling, etc.
             */
            pline("Air currents pull you down into %s %s!",
-               a_your[trap->madeby_u],
-               defsyms[trap_to_defsym(ttype)].explanation);
+                 a_your[trap->madeby_u],
+                 defsyms[trap_to_defsym(ttype)].explanation);
            /* then proceed to normal trap effect */
        } else if (already_seen && !forcetrap) {
            if ((Levitation || Flying) &&
@@ -984,9 +991,9 @@ glovecheck:         (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
            case PIT:
            case SPIKED_PIT:
                /* KMH -- You can't escape the Sokoban level traps */
-               if (!In_sokoban(&u.uz) && (Levitation || Flying)) break;
+               if (!Sokoban && (Levitation || Flying)) break;
                feeltrap(trap);
-               if (!In_sokoban(&u.uz) && is_clinger(youmonst.data)) {
+               if (!Sokoban && is_clinger(youmonst.data)) {
                    if(trap->tseen) {
                        You_see("%s %spit below you.", a_your[trap->madeby_u],
                            ttype == SPIKED_PIT ? "spiked " : "");
@@ -998,7 +1005,7 @@ glovecheck:                (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
                    }
                    break;
                }
-               if (!In_sokoban(&u.uz)) {
+               if (!Sokoban) {
                    char verbbuf[BUFSZ];
 #ifdef STEED
                    if (u.usteed) {
@@ -1949,7 +1956,7 @@ register struct monst *mtmp;
            boolean in_sight, tear_web, see_it,
                    inescapable = force_mintrap ||
                                ((tt == HOLE || tt == PIT) &&
-                                  In_sokoban(&u.uz) && !trap->madeby_u);
+                                Sokoban && !trap->madeby_u);
            const char *fallverb;
 
 #ifdef STEED
@@ -2211,7 +2218,7 @@ 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)) {
+                           if (force_mintrap && !Sokoban) {
                                /* openfallingtrap; not inescapable here */
                                if (in_sight) {
                                    seetrap(trap);
@@ -2251,7 +2258,7 @@ 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)) {
+                           if (force_mintrap && !Sokoban) {
                                /* openfallingtrap; not inescapable here */
                                if (in_sight) {
                                    seetrap(trap);
@@ -2718,7 +2725,7 @@ long hmask, emask;     /* might cancel timeout */
                if (!(emask & W_SADDLE))
 #endif
                {
-                   if (In_sokoban(&u.uz) && trap) {
+                   if (Sokoban && trap) {
                        /* Justification elsewhere for Sokoban traps
                         * is based on air currents. This is
                         * consistent with that.
@@ -2793,7 +2800,7 @@ climb_pit()
            display_nhwindow(WIN_MESSAGE, FALSE);
            clear_nhwindow(WIN_MESSAGE);
            You("free your %s.", body_part(LEG));
-       } else if (Flying && !In_sokoban(&u.uz)) {
+       } else if (Flying && !Sokoban) {
            /* eg fell in pit, poly'd to a flying monster */
            You("fly from the pit.");
            u.utrap = 0;
@@ -2801,7 +2808,7 @@ climb_pit()
            vision_full_recalc = 1;     /* vision limits change */
        } else if (!(--u.utrap)) {
            You("%s to the edge of the pit.",
-               (In_sokoban(&u.uz) && Levitation) ?
+               (Sokoban && Levitation) ?
                "struggle against the air currents and float" :
 #ifdef STEED
                u.usteed ? "ride" :
@@ -4437,6 +4444,8 @@ register struct trap *trap;
                for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ;
                ttmp->ntrap = trap->ntrap;
        }
+       if (Sokoban && (trap->ttyp == PIT || trap->ttyp == HOLE))
+               maybe_finish_sokoban();
        dealloc_trap(trap);
 }
 
@@ -4802,4 +4811,46 @@ sink_into_lava()
     }
 }
 
+/* called when something has been done (breaking a boulder, for instance)
+   which entails a luck penalty if performed on a sokoban level */
+void
+sokoban_guilt()
+{
+    if (Sokoban) {
+       change_luck(-1);
+       /* TODO: issue some feedback so that player can learn that whatever
+          he/she just did is a naughty thing to do in sokoban and should
+          probably be avoided in future....
+          Caveat: doing this might introduce message sequencing issues,
+          depending upon feedback during the various actions which trigger
+          Sokoban luck penalties. */
+    }
+}
+
+/* called when a trap has been deleted or had its ttyp replaced */
+STATIC_OVL void
+maybe_finish_sokoban()
+{
+    struct trap *t;
+
+    if (Sokoban && !in_mklev) {
+       /* scan all remaining traps, ignoring any created by the hero;
+          if this level has no more pits or holes, the current sokoban
+          puzzle has been solved */
+       for (t = ftrap; t; t = t->ntrap) {
+           if (t->madeby_u) continue;
+           if (t->ttyp == PIT || t->ttyp == HOLE) break;
+       }
+       if (!t) {
+           /* we've passed the last trap without finding a pit or hole;
+              clear the sokoban_rules flag so that luck penalties for
+              things like breaking boulders or jumping will no longer
+              be given, and restrictions on diagonal moves are lifted */
+           Sokoban = 0;        /* clear level.flags.sokoban_rules */
+           /* TODO: give some feedback about solving the sokoban puzzle
+              (perhaps say "congratulations" in Japanese?) */
+       }
+    }
+}
+
 /*trap.c*/
index 4e641cbae3051ca174c6774ade7ee08bfc2f9aba..ac22d7781eb0b83b245ee032d99ce46b0c2e42fe 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -1307,8 +1307,8 @@ poly_obj(obj, id)
        boolean can_merge = (id == STRANGE_OBJECT);
        int obj_location = obj->where;
 
-       if (obj->otyp == BOULDER && In_sokoban(&u.uz))
-           change_luck(-1);    /* Sokoban guilt */
+       if (obj->otyp == BOULDER)
+           sokoban_guilt();
        if (id == STRANGE_OBJECT) { /* preserve symbol */
            int try_limit = 3;
            unsigned magic_obj = objects[obj->otyp].oc_magic;
@@ -3152,7 +3152,7 @@ struct obj **pobj;                        /* object tossed/used, set to NULL
                            pline("%s jerks to an abrupt halt.",
                                  The(distant_name(obj, xname))); /* lame */
                        range = 0;
-                   } else if (In_sokoban(&u.uz) && (t = t_at(x, y)) != 0 &&
+                   } else if (Sokoban && (t = t_at(x, y)) != 0 &&
                               (t->ttyp == PIT || t->ttyp == SPIKED_PIT ||
                                t->ttyp == HOLE || t->ttyp == TRAPDOOR)) {
                        /* hero falls into the trap, so ball stops */
@@ -4270,10 +4270,8 @@ register struct obj *obj;                   /* no texts here! */
                breakobj(obj, x, y, TRUE, FALSE); /* charges for shop goods */
            }
        }
-
-       /* A little Sokoban guilt... */
-       if (by_you && obj->otyp == BOULDER && In_sokoban(&u.uz))
-           change_luck(-1);
+       if (by_you && obj->otyp == BOULDER)
+           sokoban_guilt();
 
        obj->otyp = ROCK;
        obj->oclass = GEM_CLASS;