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
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 ### */
* 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"
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 */
};
#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 */
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));
/* 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. */
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();
}
}
}
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");
/* 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. */
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;
}
}
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;
}
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;
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 */
/* 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. */
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));
#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;
&& 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);
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)))));
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;
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.");
}
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;
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) &&
/* 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. */
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;
|| (!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 ||
&& 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;
}
&& 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);
}
/* 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. */
&& !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))
{
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;
{
register int zx, zy;
- if (In_sokoban(&u.uz))
+ if (Sokoban)
return;
known = TRUE;
#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++) {
#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;
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;
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);
}
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
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
}
} 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)
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
* 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) &&
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 " : "");
}
break;
}
- if (!In_sokoban(&u.uz)) {
+ if (!Sokoban) {
char verbbuf[BUFSZ];
#ifdef STEED
if (u.usteed) {
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
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);
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);
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.
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;
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" :
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);
}
}
}
+/* 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*/
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;
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 */
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;