--- /dev/null
+diff -ruN nethack-3.4.0.orig/include/you.h nethack-3.4.0/include/you.h
+--- nethack-3.4.0.orig/include/you.h Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/include/you.h Sat Apr 13 16:30:42 2002
+@@ -62,6 +62,7 @@
+ long unvegan; /* ... or any animal byproduct */
+ long food; /* ... or any comestible */
+ long gnostic; /* used prayer, priest, or altar */
++ long role; /* followed chivalry/bushido */
+ long weaphit; /* hit a monster with a weapon */
+ long killer; /* killed a monster yourself */
+ long literate; /* read something (other than BotD) */
+diff -ruN nethack-3.4.0.orig/src/cmd.c nethack-3.4.0/src/cmd.c
+--- nethack-3.4.0.orig/src/cmd.c Sat Apr 13 01:11:53 2002
++++ nethack-3.4.0/src/cmd.c Sat Apr 13 16:29:33 2002
+@@ -1468,6 +1468,12 @@
+ if (!u.uconduct.gnostic)
+ you_have_been("an atheist");
+
++ if ((Role_if(PM_KNIGHT) || Role_if(PM_SAMURAI)) && !u.uconduct.role) {
++ Sprintf(buf, "followed the code of %s",
++ Role_if(PM_KNIGHT) ? "chivalry" : "bushido");
++ you_have_X(buf);
++ }
++
+ if (!u.uconduct.weaphit)
+ you_have_never("hit with a wielded weapon");
+ #ifdef WIZARD
+@@ -1557,6 +1563,12 @@
+ if (!u.uconduct.gnostic)
+ dump("", " You were an atheist");
+
++ if ((Role_if(PM_KNIGHT) || Role_if(PM_SAMURAI)) && !u.uconduct.role) {
++ Sprintf(buf, "followed the code of %s",
++ Role_if(PM_KNIGHT) ? "chivalry" : "bushido");
++ dump(" You have ", buf);
++ }
++
+ if (!u.uconduct.weaphit)
+ dump("", " You never hit with a wielded weapon");
+ #ifdef WIZARD
+diff -ruN nethack-3.4.0.orig/src/dig.c nethack-3.4.0/src/dig.c
+--- nethack-3.4.0.orig/src/dig.c Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/src/dig.c Sat Apr 13 16:19:52 2002
+@@ -740,9 +740,11 @@
+ } else if (Role_if(PM_SAMURAI)) {
+ adjalign(-sgn(u.ualign.type));
+ You("disturb the honorable dead!");
++ u.uconduct.role++;
+ } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) {
+ adjalign(-sgn(u.ualign.type));
+ You("have violated the sanctity of this grave!");
++ if (Role_if(PM_KNIGHT)) u.uconduct.role++;
+ }
+
+ switch (rn2(5)) {
+diff -ruN nethack-3.4.0.orig/src/dokick.c nethack-3.4.0/src/dokick.c
+--- nethack-3.4.0.orig/src/dokick.c Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/src/dokick.c Sat Apr 13 16:19:52 2002
+@@ -68,6 +68,7 @@
+ (mon->mflee && !mon->mavenge))) {
+ You_feel("like a caitiff!");
+ adjalign(-1);
++ u.uconduct.role++;
+ }
+
+ /* squeeze some guilt feelings... */
+diff -ruN nethack-3.4.0.orig/src/eat.c nethack-3.4.0/src/eat.c
+--- nethack-3.4.0.orig/src/eat.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/eat.c Sat Apr 13 16:19:52 2002
+@@ -231,6 +231,7 @@
+ } else if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL) {
+ adjalign(-1); /* gluttony is unchivalrous */
+ You_feel("like a glutton!");
++ u.uconduct.role++;
+ }
+
+ exercise(A_CON, FALSE);
+diff -ruN nethack-3.4.0.orig/src/shk.c nethack-3.4.0/src/shk.c
+--- nethack-3.4.0.orig/src/shk.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/shk.c Sat Apr 13 16:25:24 2002
+@@ -482,9 +482,11 @@
+ eshkp->robbed += total;
+ You("stole %ld %s worth of merchandise.",
+ total, currency(total));
+- if (!Role_if(PM_ROGUE)) /* stealing is unlawful */
++ if (!Role_if(PM_ROGUE)) {
++ /* stealing is unlawful */
+ adjalign(-sgn(u.ualign.type));
+-
++ u.uconduct.role++;
++ }
+ hot_pursuit(shkp);
+ }
+
+@@ -3291,6 +3293,7 @@
+ if (Role_if(PM_KNIGHT)) {
+ You_feel("like a common thief.");
+ adjalign(-sgn(u.ualign.type));
++ u.uconduct.role++;
+ }
+ return;
+ }
+@@ -3308,6 +3311,7 @@
+ if (Role_if(PM_KNIGHT)) {
+ You_feel("like a common thief.");
+ adjalign(-sgn(u.ualign.type));
++ u.uconduct.role++;
+ }
+ } else if(!um_dist(shkp->mx, shkp->my, 5) &&
+ !shkp->msleeping && shkp->mcanmove &&
+@@ -3534,6 +3538,7 @@
+ verbalize("Oh, yes! You'll pay!");
+ hot_pursuit(shkp);
+ adjalign(-sgn(u.ualign.type));
++ u.uconduct.role++;
+ }
+ }
+ #endif /*OVLB*/
+diff -ruN nethack-3.4.0.orig/src/uhitm.c nethack-3.4.0/src/uhitm.c
+--- nethack-3.4.0.orig/src/uhitm.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/uhitm.c Sat Apr 13 16:19:52 2002
+@@ -222,6 +222,7 @@
+ u.ualign.record > -10) {
+ You("caitiff!");
+ adjalign(-1);
++ u.uconduct.role++;
+ }
+
+ /* attacking peaceful creatures is bad for the samurai's giri */
+@@ -229,6 +230,7 @@
+ u.ualign.record > -10) {
+ You("dishonorably attack the innocent!");
+ adjalign(-1);
++ u.uconduct.role++;
+ }
+
+ /* Adjust vs. (and possibly modify) monster state. */
+@@ -862,9 +864,11 @@
+ if Role_if(PM_SAMURAI) {
+ You("dishonorably use a poisoned weapon!");
+ adjalign(-sgn(u.ualign.type));
++ u.uconduct.role++;
+ } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) {
+ You_feel("like an evil coward for using a poisoned weapon.");
+ adjalign(-1);
++ if (Role_if(PM_KNIGHT)) u.uconduct.role++;
+ }
+ if (obj && !rn2(nopoison)) {
+ obj->opoisoned = FALSE;
+diff -ruN nethack-3.4.0.orig/src/vault.c nethack-3.4.0/src/vault.c
+--- nethack-3.4.0.orig/src/vault.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/vault.c Sat Apr 13 16:19:52 2002
+@@ -264,6 +264,7 @@
+ /* ignore trailing text, in case player includes character's rank */
+ strncmpi(buf, plname, (int) strlen(plname)) != 0) {
+ adjalign(-1); /* Liar! */
++ u.uconduct.role++;
+ }
+
+ if (!strcmpi(buf, "Croesus") || !strcmpi(buf, "Kroisos")
--- /dev/null
+diff -ruN nethack-3.4.0.orig/include/config.h nethack-3.4.0/include/config.h
+--- nethack-3.4.0.orig/include/config.h Sat Apr 13 18:02:50 2002
++++ nethack-3.4.0/include/config.h Sat Apr 13 18:08:01 2002
+@@ -348,6 +348,7 @@
+ * bugs left here.
+ */
+
++#define DUNGEON_GROWTH /* dungeon growth patch */
+ #define FLIPCOIN /* flipcoin patch */
+ #define STICKY_OBJECTS /* sticky objects patch */
+ #define DUMP_LOG /* dump log patch */
+diff -ruN nethack-3.4.0.orig/include/extern.h nethack-3.4.0/include/extern.h
+--- nethack-3.4.0.orig/include/extern.h Sat Apr 13 18:02:50 2002
++++ nethack-3.4.0/include/extern.h Sat Apr 13 18:07:15 2002
+@@ -665,6 +665,10 @@
+
+ /* ### hack.c ### */
+
++#ifdef DUNGEON_GROWTH
++E void FDECL(catchup_dgn_growths, (int));
++E void FDECL(dgn_growths, (BOOLEAN_P,BOOLEAN_P));
++#endif
+ E boolean FDECL(revive_nasty, (int,int,const char*));
+ E void FDECL(movobj, (struct obj *,XCHAR_P,XCHAR_P));
+ E boolean FDECL(may_dig, (XCHAR_P,XCHAR_P));
+diff -ruN nethack-3.4.0.orig/src/allmain.c nethack-3.4.0/src/allmain.c
+--- nethack-3.4.0.orig/src/allmain.c Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/src/allmain.c Sat Apr 13 18:07:15 2002
+@@ -142,7 +142,9 @@
+ if(Glib) glibr();
+ nh_timeout();
+ run_regions();
+-
++#ifdef DUNGEON_GROWTH
++ dgn_growths(TRUE, TRUE);
++#endif
+ if (u.ublesscnt) u.ublesscnt--;
+ if(flags.time && !flags.run)
+ flags.botl = 1;
+diff -ruN nethack-3.4.0.orig/src/hack.c nethack-3.4.0/src/hack.c
+--- nethack-3.4.0.orig/src/hack.c Sat Apr 13 18:02:50 2002
++++ nethack-3.4.0/src/hack.c Sat Apr 13 18:07:15 2002
+@@ -21,6 +21,332 @@
+
+ #ifdef OVL2
+
++#ifdef DUNGEON_GROWTH
++void
++rndmappos(x,y) /* guaranteed to return a valid coord */
++xchar *x;
++xchar *y;
++{
++ if (*x >= COLNO) *x = COLNO;
++ else if (*x == -1) *x = rn2(COLNO-1)+1;
++ else if (*x < 1) *x = 1;
++
++ if (*y >= ROWNO) *y = ROWNO;
++ else if (*y == -1) *y = rn2(ROWNO);
++ else if (*y < 0) *y = 0;
++}
++
++#define HERB_GROWTH_LIMIT 3 /* to limit excessive farming */
++
++static const struct herb_info {
++ int herb;
++ boolean in_water;
++} herb_info[] = {
++ { SPRIG_OF_WOLFSBANE, FALSE },
++ { CLOVE_OF_GARLIC, FALSE },
++ { CARROT, FALSE },
++ { KELP_FROND, TRUE }
++};
++
++long
++count_herbs_at(x,y, watery)
++xchar x,y;
++boolean watery;
++{
++ register int dd;
++ register long count = 0;
++
++ if (isok(x,y)) {
++ for (dd = 0; dd < SIZE(herb_info); dd++) {
++ if (watery == herb_info[dd].in_water) {
++ register struct obj *otmp = sobj_at(herb_info[dd].herb, x,y);
++ if (otmp)
++ count += otmp->quan;
++ }
++ }
++ }
++ return count;
++}
++
++/* returns TRUE if a herb can grow at (x,y) */
++boolean
++herb_can_grow_at(x,y, watery)
++xchar x,y;
++boolean watery;
++{
++ register struct rm *lev = &levl[x][y];
++ if (inside_shop(x,y)) return FALSE;
++ if (watery)
++ return (IS_POOL(lev->typ) &&
++ ((count_herbs_at(x,y, watery)) < HERB_GROWTH_LIMIT));
++ return (lev->lit && (lev->typ == ROOM || lev->typ == CORR ||
++ (IS_DOOR(lev->typ) &&
++ ((lev->doormask == D_NODOOR) ||
++ (lev->doormask == D_ISOPEN) ||
++ (lev->doormask == D_BROKEN)))) &&
++ (count_herbs_at(x,y, watery) < HERB_GROWTH_LIMIT));
++}
++
++/* grow herbs in water. return true if did something. */
++boolean
++grow_water_herbs(herb, x,y)
++int herb;
++xchar x,y;
++{
++ struct obj *otmp;
++
++ rndmappos(&x, &y);
++ otmp = sobj_at(herb, x, y);
++ if (otmp && herb_can_grow_at(x,y, TRUE)) {
++ otmp->quan++;
++ otmp->owt = weight(otmp);
++ return TRUE;
++ /* There's no need to start growing these on the neighboring
++ * mapgrids, as they move around (see water_current())
++ */
++ }
++ return FALSE;
++}
++
++/* grow herb on ground at (x,y), or maybe spread out.
++ return true if did something. */
++boolean
++grow_herbs(herb, x,y, showmsg, update)
++int herb;
++xchar x,y;
++boolean showmsg, update;
++{
++ struct obj *otmp;
++ struct rm *lev;
++
++ rndmappos(&x, &y);
++ lev = &levl[x][y];
++ otmp = sobj_at(herb, x, y);
++ if (otmp && herb_can_grow_at(x,y, FALSE)) {
++ if (otmp->quan <= rn2(HERB_GROWTH_LIMIT)) {
++ otmp->quan++;
++ otmp->owt = weight(otmp);
++ return TRUE;
++ } else {
++ int dd, dofs = rn2(8);
++ /* check surroundings, maybe grow there? */
++ for (dd = 0; dd < 8; dd++) {
++ coord pos;
++
++ dtoxy(&pos, (dd+dofs) % 8);
++ pos.x += x;
++ pos.y += y;
++ if (isok(pos.x,pos.y) && herb_can_grow_at(pos.x,pos.y, FALSE)) {
++ lev = &levl[pos.x][pos.y];
++ otmp = sobj_at(herb, pos.x, pos.y);
++ if (otmp) {
++ if (otmp->quan <= rn2(HERB_GROWTH_LIMIT)) {
++ otmp->quan++;
++ otmp->owt = weight(otmp);
++ return TRUE;
++ }
++ } else {
++ otmp = mksobj(herb, TRUE, FALSE);
++ otmp->quan = 1;
++ otmp->owt = weight(otmp);
++ place_object(otmp, pos.x, pos.y);
++ if (update) newsym(pos.x,pos.y);
++ if (cansee(pos.x,pos.y)) {
++ if (showmsg && flags.verbose) {
++ const char *what;
++ if (herb == CLOVE_OF_GARLIC)
++ what = "some garlic";
++ else
++ what = an(xname(otmp));
++ Norep("Suddenly you notice %s growing on the %s.",
++ what, surface(pos.x,pos.y));
++ }
++ }
++ return TRUE;
++ }
++ }
++ }
++ }
++ }
++ return FALSE;
++}
++
++/* moves topmost object in water at (x,y) to dir.
++ return true if did something. */
++boolean
++water_current(x,y,dir,waterforce, showmsg, update)
++xchar x,y;
++int dir;
++unsigned waterforce; /* strength of the water current */
++boolean showmsg, update;
++{
++ struct obj *otmp;
++ coord pos;
++
++ rndmappos(&x,&y);
++ dtoxy(&pos, dir);
++ pos.x += x;
++ pos.y += y;
++ if (isok(pos.x,pos.y) && IS_POOL(levl[x][y].typ) &&
++ IS_POOL(levl[pos.x][pos.y].typ)) {
++ otmp = level.objects[x][y];
++ if (otmp && otmp->where == OBJ_FLOOR) {
++ if (otmp->quan > 1)
++ otmp = splitobj(otmp, otmp->quan - 1);
++ if (otmp->owt <= waterforce) {
++ if (showmsg && Underwater &&
++ (cansee(pos.x,pos.y) || cansee(x,y))) {
++ Norep("%s floats%s in%s the murky water.",
++ An(xname(otmp)),
++ (cansee(x,y) && cansee(pos.x,pos.y)) ? "" :
++ (cansee(x,y) ? " away from you" : " towards you"),
++ flags.verbose ? " the currents of" : "");
++ }
++ obj_extract_self(otmp);
++ place_object(otmp, pos.x,pos.y);
++ stackobj(otmp);
++ if (update) {
++ newsym(x,y);
++ newsym(pos.x,pos.y);
++ }
++ return TRUE;
++ } else /* the object didn't move, put it back */
++ stackobj(otmp);
++ }
++ }
++ return FALSE;
++}
++
++/* a tree at (x,y) spontaneously drops a ripe fruit */
++boolean
++drop_ripe_treefruit(x,y,showmsg, update)
++xchar x,y;
++boolean showmsg, update;
++{
++ register struct rm *lev;
++
++ rndmappos(&x,&y);
++ lev = &levl[x][y];
++ if (IS_TREE(lev->typ) && !(lev->looted & TREE_LOOTED)) {
++ coord pos;
++ int dir, dofs = rn2(8);
++ for (dir = 0; dir < 8; dir++) {
++ dtoxy(&pos, (dir + dofs) % 8);
++ pos.x += x;
++ pos.y += y;
++ lev = &levl[pos.x][pos.y];
++ if (SPACE_POS(lev->typ) || IS_POOL(lev->typ)) {
++ struct obj *otmp;
++ otmp = rnd_treefruit_at(pos.x,pos.y);
++ if (otmp) {
++ otmp->quan = 1;
++ otmp->owt = weight(otmp);
++ obj_extract_self(otmp);
++ if (rn2(3))
++ otmp->orotten = TRUE; /* Blecch! Rotten food! */
++ if (showmsg) {
++ if ((cansee(pos.x,pos.y) || cansee(x,y))) {
++ Norep("%s falls from %s%s.",
++ cansee(pos.x,pos.y) ? An(xname(otmp)) : Something,
++ cansee(x,y) ? "the tree" : "somewhere",
++ (cansee(x,y) && IS_POOL(lev->typ)) ?
++ " into the water" : "");
++ } else if (distu(pos.x,pos.y) < 9 &&
++ otmp->otyp != EUCALYPTUS_LEAF) {
++ /* a leaf is too light to cause any sound */
++ You_hear("a %s!",
++ (IS_POOL(lev->typ) || IS_FOUNTAIN(lev->typ)) ?
++ "plop" : "splut"); /* rainforesty sounds */
++ }
++ }
++ place_object(otmp, pos.x,pos.y);
++ stackobj(otmp);
++ if (rn2(6)) levl[x][y].looted |= TREE_LOOTED;
++ if (update) newsym(pos.x,pos.y);
++ return TRUE;
++ }
++ }
++ }
++ }
++ return FALSE;
++}
++
++/* Tree at (x,y) seeds. returns TRUE if a new tree was created.
++ * Creates a kind of forest, with (hopefully) most places available.
++ */
++boolean
++seed_tree(x,y)
++xchar x,y;
++{
++ coord pos, pos2;
++ struct rm *lev = &levl[pos.x][pos.y];
++
++ rndmappos(&x,&y);
++ if (IS_TREE(levl[x][y].typ)) {
++ int dir = rn2(8);
++ dtoxy(&pos, dir);
++ pos.x += x;
++ pos.y += y;
++ if (!rn2(3)) {
++ dtoxy(&pos2, (dir+rn2(2)) % 8);
++ pos.x += pos2.x;
++ pos.y += pos2.y;
++ }
++ if (!isok(pos.x,pos.y)) return FALSE;
++ lev = &levl[pos.x][pos.y];
++ if (lev->lit && !cansee(pos.x,pos.y) && !inside_shop(pos.x,pos.y) &&
++ (lev->typ == ROOM || lev->typ == CORR) &&
++ !(u.ux == pos.x && u.uy == pos.y) && !m_at(pos.x,pos.y) &&
++ !t_at(pos.x,pos.y) && !OBJ_AT(pos.x,pos.y)) {
++ int nogrow = 0;
++ int dx,dy;
++ for (dx = pos.x-1; dx <= pos.x+1; dx++) {
++ for (dy = pos.y-1; dy <= pos.y+1; dy++) {
++ if (!isok(dx,dy) ||
++ (isok(dx,dy) && !SPACE_POS(levl[dx][dy].typ)))
++ nogrow++;
++ }
++ }
++ if (nogrow < 3) {
++ lev->typ = TREE;
++ lev->looted &= ~TREE_LOOTED;
++ block_point(pos.x,pos.y);
++ return TRUE;
++ }
++ }
++ }
++ return FALSE;
++}
++
++void
++dgn_growths(showmsg, update)
++boolean showmsg; /* show messages */
++boolean update; /* do newsym() */
++{
++ int herbnum = rn2(SIZE(herb_info));
++ (void) seed_tree(-1,-1);
++ if (herb_info[herbnum].in_water)
++ (void) grow_water_herbs(herb_info[herbnum].herb, -1,-1);
++ else
++ (void) grow_herbs(herb_info[herbnum].herb, -1,-1, showmsg, update);
++ if (!rn2(30))
++ (void) drop_ripe_treefruit(-1,-1, showmsg, update);
++ (void) water_current(-1,-1, rn2(8),
++ Is_waterlevel(&u.uz) ? 200 : 25, showmsg, update);
++}
++
++/* catch up with growths when returning to a previously visited level */
++void
++catchup_dgn_growths(mvs)
++int mvs;
++{
++ if (mvs < 0) mvs = 0;
++ else if (mvs > LARGEST_INT) mvs = LARGEST_INT;
++ while (mvs-- > 0)
++ dgn_growths(FALSE, FALSE);
++}
++#endif /* DUNGEON_GROWTH */
++
+ boolean
+ revive_nasty(x, y, msg)
+ int x,y;
+diff -ruN nethack-3.4.0.orig/src/restore.c nethack-3.4.0/src/restore.c
+--- nethack-3.4.0.orig/src/restore.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/restore.c Sat Apr 13 18:07:15 2002
+@@ -866,7 +866,9 @@
+ relink_timers(ghostly);
+ relink_light_sources(ghostly);
+ reset_oattached_mids(ghostly);
+-
++#ifdef DUNGEON_GROWTH
++ if (!ghostly) catchup_dgn_growths((monstermoves - omoves) / 5);
++#endif
+ if (ghostly)
+ clear_id_mapping();
+ }
--- /dev/null
+diff -ruN nethack-3.4.0.orig/include/config.h nethack-3.4.0/include/config.h
+--- nethack-3.4.0.orig/include/config.h Sat Apr 13 17:17:13 2002
++++ nethack-3.4.0/include/config.h Sat Apr 13 17:55:58 2002
+@@ -348,6 +348,7 @@
+ * bugs left here.
+ */
+
++#define FLIPCOIN /* flipcoin patch */
+ #define STICKY_OBJECTS /* sticky objects patch */
+ #define DUMP_LOG /* dump log patch */
+ #define DUMP_FN "/var/games/nethack/%n.dump"
+diff -ruN nethack-3.4.0.orig/src/apply.c nethack-3.4.0/src/apply.c
+--- nethack-3.4.0.orig/src/apply.c Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/src/apply.c Sat Apr 13 17:54:43 2002
+@@ -7,9 +7,17 @@
+
+ #ifdef OVLB
+
+-static const char tools[] = { TOOL_CLASS, WEAPON_CLASS, WAND_CLASS, 0 };
+-static const char tools_too[] = { ALL_CLASSES, TOOL_CLASS, POTION_CLASS,
+- WEAPON_CLASS, WAND_CLASS, GEM_CLASS, 0 };
++static const char tools[] = {
++#ifdef FLIPCOIN
++ GOLD_CLASS,
++#endif
++ TOOL_CLASS, WEAPON_CLASS, WAND_CLASS, 0 };
++static const char tools_too[] = {
++#ifdef FLIPCOIN
++ GOLD_CLASS,
++#endif
++ ALL_CLASSES, TOOL_CLASS, POTION_CLASS,
++ WEAPON_CLASS, WAND_CLASS, GEM_CLASS, 0 };
+
+ #ifdef TOURIST
+ STATIC_DCL int FDECL(use_camera, (struct obj *));
+@@ -36,6 +44,9 @@
+ STATIC_DCL int FDECL(use_pole, (struct obj *));
+ STATIC_DCL int FDECL(use_grapple, (struct obj *));
+ STATIC_DCL int FDECL(do_break_wand, (struct obj *));
++#ifdef FLIPCOIN
++STATIC_DCL int FDECL(do_flip_coin, (struct obj *));
++#endif
+ STATIC_DCL boolean FDECL(figurine_location_checks,
+ (struct obj *, coord *, BOOLEAN_P));
+ STATIC_DCL boolean NDECL(uhave_graystone);
+@@ -2621,6 +2632,51 @@
+ return FALSE;
+ }
+
++#ifdef FLIPCOIN
++STATIC_OVL int
++do_flip_coin(obj)
++struct obj *obj;
++{
++#ifndef GOLDOBJ
++ u.ugold += obj->quan;
++ dealloc_obj(obj);
++#endif
++
++ if (nohands(youmonst.data)) {
++ pline("And how would you flip the coin without hands?");
++ return 0;
++ } else
++ if (!freehand()) {
++ You("need at least one free %s.", body_part(HAND));
++ return 0;
++ }
++
++ You("flip %s coin.",
++#ifndef GOLDOBJ
++ (u.ugold > 1)
++#else
++ (obj->quan > 1)
++#endif
++ ? "a" : "the");
++ if (!Fumbling && !Glib && !Blind &&
++ ((ACURR(A_DEX) + Luck) > 0) && rn2(ACURR(A_DEX) + Luck)) {
++ pline("%s.", rn2(2) ? "Heads" : "Tails");
++ } else {
++ struct obj *gold;
++ You("try to catch the coin but it slips from your %s.",
++ makeplural(body_part(HAND)));
++#ifndef GOLDOBJ
++ gold = mkgoldobj(1);
++#else
++ if (obj->quan > 1) gold = splitobj(obj, 1L);
++ else gold = obj;
++#endif
++ dropx(gold);
++ }
++ return 1;
++}
++#endif
++
+ int
+ doapply()
+ {
+@@ -2634,6 +2690,10 @@
+
+ if (obj->oclass == WAND_CLASS)
+ return do_break_wand(obj);
++#ifdef FLIPCOIN
++ else if (obj->oclass == GOLD_CLASS)
++ return do_flip_coin(obj);
++#endif
+
+ switch(obj->otyp){
+ case BLINDFOLD:
--- /dev/null
+diff -ruN nethack-3.4.0.orig/src/mon.c nethack-3.4.0/src/mon.c
+--- nethack-3.4.0.orig/src/mon.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/mon.c Sat Apr 13 16:46:02 2002
+@@ -1578,6 +1578,7 @@
+ int how;
+ {
+ boolean be_sad = FALSE; /* true if unseen pet is killed */
++ boolean kenny = (!strcmp(m_monnam(mdef), "Kenny"));
+
+ if ((mdef->wormno ? worm_known(mdef) : cansee(mdef->mx, mdef->my))
+ && fltxt)
+@@ -1595,8 +1596,15 @@
+ else
+ mondied(mdef);
+
+- if (be_sad && mdef->mhp <= 0)
+- You("have a sad feeling for a moment, then it passes.");
++ if (be_sad && mdef->mhp <= 0) {
++ if (kenny || (Hallucination && !rn2(2))) {
++ verbalize("Oh my god, they killed Kenny!");
++ verbalize("You bastards!");
++ } else {
++ You("have a %s feeling for a moment, then it passes.",
++ (Hallucination ? "plaid" : "sad"));
++ }
++ }
+ }
+
+ void
--- /dev/null
+diff -ruN nethack-3.4.0.orig/include/extern.h nethack-3.4.0/include/extern.h
+--- nethack-3.4.0.orig/include/extern.h Sat Apr 13 17:30:36 2002
++++ nethack-3.4.0/include/extern.h Sat Apr 13 17:48:09 2002
+@@ -210,6 +210,7 @@
+ E const char *FDECL(level_distance, (d_level *));
+ E void FDECL(use_crystal_ball, (struct obj *));
+ E void NDECL(do_mapping);
++E int NDECL(dolistmons);
+ E void NDECL(do_vicinity_map);
+ E void FDECL(cvt_sdoor_to_door, (struct rm *));
+ #ifdef USE_TRAMPOLI
+diff -ruN nethack-3.4.0.orig/src/cmd.c nethack-3.4.0/src/cmd.c
+--- nethack-3.4.0.orig/src/cmd.c Sat Apr 13 17:31:50 2002
++++ nethack-3.4.0/src/cmd.c Sat Apr 13 17:48:47 2002
+@@ -98,6 +98,7 @@
+ extern int NDECL(dozap); /**/
+ extern int NDECL(doorganize); /**/
+ extern int NDECL(dowashhands); /**/
++extern int NDECL(dolistmons); /**/
+ #ifdef STICKY_OBJECTS
+ extern int NDECL(dosticky); /**/
+ #endif /* STICKY_OBJECTS */
+@@ -1782,6 +1783,7 @@
+ {"force", "force a lock", doforce, FALSE},
+ {"invoke", "invoke an object's powers", doinvoke, TRUE},
+ {"jump", "jump to a location", dojump, FALSE},
++ {"listmons", "list monsters you can see or detect", dolistmons, TRUE},
+ {"loot", "loot a box on the floor", doloot, FALSE},
+ {"monster", "use a monster's special ability", domonability, TRUE},
+ {"name", "name an item or type of object", ddocall, TRUE},
+diff -ruN nethack-3.4.0.orig/src/detect.c nethack-3.4.0/src/detect.c
+--- nethack-3.4.0.orig/src/detect.c Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/src/detect.c Sat Apr 13 17:48:09 2002
+@@ -20,6 +20,163 @@
+ STATIC_PTR void FDECL(findone,(int,int,genericptr_t));
+ STATIC_PTR void FDECL(openone,(int,int,genericptr_t));
+
++/* stuff for #listmons -command begins */
++
++#define LM_PLINELIM 4 /* # of uniquely identifiable monsters needed for
++ showing in a separate window. */
++
++struct _listmons {
++ struct _listmons *next;
++ xchar prefix; /* 0 = no prefix, 1 = an(), 2 = the() */
++ int nummons;
++ char *name;
++};
++
++static struct _listmons *mlist = (struct _listmons *)0;
++static long listmons_diffmons = 0, listmons_allmons = 0;
++static boolean listmons_see = TRUE;
++
++void
++add_str_listmons(str, prefix)
++char *str;
++xchar prefix;
++{
++ struct _listmons *tmp;
++
++ listmons_allmons++;
++
++ for (tmp = mlist; tmp; tmp = tmp->next) {
++ if (!strcmp(tmp->name, str)) {
++ tmp->nummons++;
++ return;
++ }
++ }
++ tmp = (struct _listmons *) alloc(sizeof(struct _listmons));
++ tmp->next = mlist;
++ tmp->nummons = 1;
++ tmp->prefix = prefix;
++ tmp->name = (char *) alloc(strlen(str)+1);
++ (void) memcpy((genericptr_t)tmp->name, (genericptr_t)str, strlen(str)+1);
++ mlist = tmp;
++ listmons_diffmons++;
++}
++
++void
++free_listmons()
++{
++ struct _listmons *tmp = mlist;
++
++ while (tmp) {
++ struct _listmons *tmp2 = tmp->next;
++ free(tmp->name);
++ free(tmp);
++ tmp = tmp2;
++ }
++
++ mlist = (struct _listmons *)0;
++ listmons_diffmons = listmons_allmons = 0;
++ listmons_see = TRUE;
++}
++
++void
++show_listmons()
++{
++ struct _listmons *tmp;
++ char buf[BUFSZ];
++
++ if (listmons_allmons < 1) {
++ if (!canseeself()) {
++ You("can't even see yourself.");
++ } else You("don't see any monsters.");
++ return;
++ }
++
++ if (listmons_diffmons >= LM_PLINELIM) {
++ /* show the monsters in a window */
++ winid win = create_nhwindow(NHW_MENU);
++ Sprintf(buf, "You %s %li creatures:", listmons_see ? "see" : "sense",
++ listmons_allmons);
++ putstr(win, 0, buf);
++ putstr(win, 0, "");
++ for (tmp = mlist; tmp; tmp = tmp->next) {
++ if (tmp->nummons > 1) {
++ Sprintf(buf, "%i %s", tmp->nummons, makeplural(tmp->name));
++ putstr(win, 0, buf);
++ } else {
++ switch (tmp->prefix) {
++ case 2: putstr(win, 0, the(tmp->name)); break;
++ case 1: putstr(win, 0, an(tmp->name)); break;
++ default: putstr(win, 0, tmp->name); break;
++ }
++ }
++ }
++ display_nhwindow(win, FALSE);
++ destroy_nhwindow(win);
++ } else {
++ /* just pline() the monsters */
++ long allmon = listmons_allmons;
++ Sprintf(buf, "You %s%s ",
++ (listmons_allmons == 1) ? "can only " : "",
++ listmons_see ? "see" : "sense");
++ for (tmp = mlist; tmp; tmp = tmp->next) {
++ allmon -= tmp->nummons;
++ if (tmp != mlist)
++ Sprintf(eos(buf),"%s", (allmon) ? ", " : " and ");
++ if (tmp->nummons > 1) {
++ Sprintf(eos(buf), "%i %s", tmp->nummons, makeplural(tmp->name));
++ } else {
++ switch (tmp->prefix) {
++ case 2: Sprintf(eos(buf), "%s", the(tmp->name)); break;
++ case 1: Sprintf(eos(buf), "%s", an(tmp->name)); break;
++ default: Sprintf(eos(buf), "%s", tmp->name); break;
++ }
++ }
++ }
++ pline("%s.", buf);
++ }
++}
++
++int
++dolistmons()
++{
++ struct monst *mtmp;
++ char buf[BUFSZ];
++
++ /* It would be too easy to see the funny hallucinatory monsters
++ * for example in the bigroom, and this function doesn't give
++ * any real information while you're hallucinating anyway.
++ */
++ if (Hallucination) {
++ You("can't bear to look at all those evil monsters!");
++ return 0;
++ }
++
++ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
++ register int sensed = (canspotmon(mtmp) &&
++ ((mtmp->m_ap_type == M_AP_NOTHING) || sensemon(mtmp)));
++ if (sensed) {
++ boolean is_uniq = !!(mons[mtmp->mnum].geno & G_UNIQ);
++ if (mtmp->mtame) {
++ Sprintf(buf, "tame ");
++ } else
++ if (mtmp->mpeaceful) {
++ Sprintf(buf, "peaceful ");
++ } else buf[0] = '\0';
++ Sprintf(eos(buf), "%s", l_monnam(mtmp));
++
++ if (!canseemon(mtmp)) listmons_see = FALSE;
++
++ add_str_listmons(&buf[0],
++ is_uniq ? 2 :
++ (mtmp->isshk || mtmp->mnamelth) ? 0 : 1);
++ }
++ }
++ show_listmons();
++ free_listmons();
++ return 0;
++}
++/* stuff for #listmons -command ends */
++
+ /* Recursively search obj for an object in class oclass and return 1st found */
+ struct obj *
+ o_in(obj, oclass)
--- /dev/null
+diff -ruN nethack-3.4.0.orig/util/makedefs.c nethack-3.4.0/util/makedefs.c
+--- nethack-3.4.0.orig/util/makedefs.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/util/makedefs.c Sat Apr 13 18:18:41 2002
+@@ -592,12 +592,17 @@
+ }
+
+ static const char *build_opts[] = {
++#ifdef FLIPCOIN
++ "ability to flip coin",
++#endif
+ #ifdef AMIGA_WBENCH
+ "Amiga WorkBench support",
+ #endif
+ #ifdef ANSI_DEFAULT
+ "ANSI default terminal",
+ #endif
++ "behind boulder patch",
++ "chivalry/bushido challenge",
+ #ifdef TEXTCOLOR
+ "color",
+ #endif
+@@ -613,6 +618,9 @@
+ #ifdef WIZARD
+ "debug mode",
+ #endif
++#ifdef DUMP_LOG
++ "dumping character to file",
++#endif
+ #ifdef ELBERETH
+ "Elbereth",
+ #endif
+@@ -622,21 +630,30 @@
+ #ifdef MFLOPPY
+ "floppy drive support",
+ #endif
++#ifdef DUNGEON_GROWTH
++ "growth in dungeon",
++#endif
+ #ifdef GOLDOBJ
+ "gold object in inventories",
+ #endif
+ #ifdef INSURANCE
+ "insurance files for recovering from crashes",
+ #endif
++ "Kenny patch",
++#ifdef STEED
++ "key for riding",
++#endif
+ #ifdef KOPS
+ "Keystone Kops",
+ #endif
++ "listing seen monsters",
+ #ifdef LOGFILE
+ "log file",
+ #endif
+ #ifdef MAIL
+ "mail daemon",
+ #endif
++ "#monster messages",
+ #ifdef GNUDOS
+ "MSDOS protected mode",
+ #endif
+@@ -694,9 +711,14 @@
+ #ifdef SHELL
+ "shell command",
+ #endif
++ "showing number of created monsters",
+ #ifdef SINKS
+ "sinks",
+ #endif
++#ifdef STICKY_OBJECTS
++ "sticky objects",
++#endif
++ "steed fix",
+ #ifdef SUSPEND
+ "suspend command",
+ #endif
+@@ -722,6 +744,7 @@
+ #ifdef WALLIFIED_MAZE
+ "walled mazes",
+ #endif
++ "washing hands",
+ #ifdef ZEROCOMP
+ "zero-compressed save files",
+ #endif
--- /dev/null
+diff -ruN nethack-3.4.0.orig/dat/cmdhelp nethack-3.4.0/dat/cmdhelp
+--- nethack-3.4.0.orig/dat/cmdhelp Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/dat/cmdhelp Sat Apr 13 18:01:28 2002
+@@ -114,6 +114,7 @@
+ M-p Pray to the gods for help
+ M-q Quit
+ M-r Rub a lamp
++M-R Ride (or stop riding) a monster
+ M-s Sit down
+ M-t Turn undead
+ M-u Untrap something (trap, door, or chest)
+diff -ruN nethack-3.4.0.orig/dat/hh nethack-3.4.0/dat/hh
+--- nethack-3.4.0.orig/dat/hh Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/dat/hh Sat Apr 13 18:01:28 2002
+@@ -99,6 +99,7 @@
+ M-p pray pray to the gods for help
+ M-q quit stop playing
+ M-r rub rub a lamp or a stone
++M-R ride ride (or stop riding) a monster
+ M-s sit sit down
+ M-t turn turn undead
+ M-u untrap untrap something
+diff -ruN nethack-3.4.0.orig/doc/Guidebook.mn nethack-3.4.0/doc/Guidebook.mn
+--- nethack-3.4.0.orig/doc/Guidebook.mn Sat Apr 13 17:17:13 2002
++++ nethack-3.4.0/doc/Guidebook.mn Sat Apr 13 18:01:28 2002
+@@ -752,6 +752,8 @@
+ #quit
+ .lp M-r
+ #rub
++.lp M-R
++#ride
+ .lp M-s
+ #sit
+ .lp M-t
+diff -ruN nethack-3.4.0.orig/doc/Guidebook.tex nethack-3.4.0/doc/Guidebook.tex
+--- nethack-3.4.0.orig/doc/Guidebook.tex Sat Apr 13 17:17:13 2002
++++ nethack-3.4.0/doc/Guidebook.tex Sat Apr 13 18:01:28 2002
+@@ -1010,6 +1010,9 @@
+ \item[\tb{M-r}]
+ {\tt\#rub}
+ %.lp
++\item[\tb{M-R}]
++{\tt\#ride}
++%.lp
+ \item[\tb{M-s}]
+ {\tt\#sit}
+ %.lp
+diff -ruN nethack-3.4.0.orig/src/cmd.c nethack-3.4.0/src/cmd.c
+--- nethack-3.4.0.orig/src/cmd.c Sat Apr 13 17:48:47 2002
++++ nethack-3.4.0/src/cmd.c Sat Apr 13 18:01:28 2002
+@@ -1719,6 +1719,9 @@
+ {'r', FALSE, doread},
+ {'R', FALSE, doremring},
+ {M('r'), FALSE, dorub},
++#ifdef STEED
++ {M('R'), TRUE, doride},
++#endif
+ {'s', TRUE, dosearch, "searching"},
+ {'S', TRUE, dosave},
+ {M('s'), FALSE, dosit},
--- /dev/null
+diff -ruN nethack-3.4.0.orig/src/hack.c nethack-3.4.0/src/hack.c
+--- nethack-3.4.0.orig/src/hack.c Sat Apr 13 17:17:13 2002
++++ nethack-3.4.0/src/hack.c Sat Apr 13 17:19:02 2002
+@@ -589,10 +589,9 @@
+ u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+ (char *)0, SUPPRESS_SADDLE, FALSE));
+ else {
+-#else
++#endif
+ pline("Ouch! You bump into a door.");
+ exercise(A_DEX, FALSE);
+-#endif
+ #ifdef STEED
+ }
+ #endif
--- /dev/null
+diff -ruN nethack-3.4.0.orig/include/config.h nethack-3.4.0/include/config.h
+--- nethack-3.4.0.orig/include/config.h Sat Apr 13 16:32:15 2002
++++ nethack-3.4.0/include/config.h Sat Apr 13 17:15:58 2002
+@@ -348,6 +348,7 @@
+ * bugs left here.
+ */
+
++#define STICKY_OBJECTS /* sticky objects patch */
+ #define DUMP_LOG /* dump log patch */
+ #define DUMP_FN "/var/games/nethack/%n.dump"
+ #define SHOW_BORN /* show born patch */
+diff -ruN nethack-3.4.0.orig/include/extern.h nethack-3.4.0/include/extern.h
+--- nethack-3.4.0.orig/include/extern.h Sat Apr 13 16:32:15 2002
++++ nethack-3.4.0/include/extern.h Sat Apr 13 17:10:02 2002
+@@ -797,6 +797,9 @@
+ E void NDECL(free_invbuf);
+ E void NDECL(reassign);
+ E int NDECL(doorganize);
++#ifdef STICKY_OBJECTS
++E int NDECL(dosticky);
++#endif /* STICKY_OBJECTS */
+ E int FDECL(count_unpaid, (struct obj *));
+ E int FDECL(count_buc, (struct obj *,int));
+ E void FDECL(carry_obj_effects, (struct obj *));
+diff -ruN nethack-3.4.0.orig/include/obj.h nethack-3.4.0/include/obj.h
+--- nethack-3.4.0.orig/include/obj.h Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/include/obj.h Sat Apr 13 17:10:02 2002
+@@ -86,7 +86,13 @@
+
+ Bitfield(in_use,1); /* for magic items before useup items */
+ Bitfield(bypass,1); /* mark this as an object to be skipped by bhito() */
++#ifndef STICKY_OBJECTS
+ /* 6 free bits */
++#else
++ Bitfield(sticky,1); /* "sticky" inventory slot */
++ /* 5 free bits */
++#endif /* STICKY_OBJECTS */
++
+
+ int corpsenm; /* type of corpse is mons[corpsenm] */
+ #define leashmon corpsenm /* gets m_id of attached pet */
+diff -ruN nethack-3.4.0.orig/src/bones.c nethack-3.4.0/src/bones.c
+--- nethack-3.4.0.orig/src/bones.c Thu Apr 11 17:58:27 2002
++++ nethack-3.4.0/src/bones.c Sat Apr 13 17:10:02 2002
+@@ -81,6 +81,9 @@
+ otmp->dknown = otmp->bknown = 0;
+ otmp->rknown = 0;
+ otmp->invlet = 0;
++#ifdef STICKY_OBJECTS
++ otmp->sticky = 0;
++#endif /* STICKY_OBJECTS */
+ otmp->no_charge = 0;
+
+ if (otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe);
+diff -ruN nethack-3.4.0.orig/src/cmd.c nethack-3.4.0/src/cmd.c
+--- nethack-3.4.0.orig/src/cmd.c Sat Apr 13 16:32:15 2002
++++ nethack-3.4.0/src/cmd.c Sat Apr 13 17:10:02 2002
+@@ -97,6 +97,9 @@
+ extern int NDECL(dowieldquiver); /**/
+ extern int NDECL(dozap); /**/
+ extern int NDECL(doorganize); /**/
++#ifdef STICKY_OBJECTS
++extern int NDECL(dosticky); /**/
++#endif /* STICKY_OBJECTS */
+ #endif /* DUMB */
+
+ #ifdef OVL1
+@@ -1732,6 +1735,9 @@
+ {'x', FALSE, doswapweapon},
+ {'X', TRUE, enter_explore_mode},
+ /* 'y', 'Y' : go nw */
++#ifdef STICKY_OBJECTS
++ {M('y'), TRUE, dosticky},
++#endif /* STICKY_OBJECTS */
+ {'z', FALSE, dozap},
+ {'Z', TRUE, docast},
+ {'<', FALSE, doup},
+@@ -1786,6 +1792,9 @@
+ #endif
+ {"rub", "rub a lamp or a stone", dorub, FALSE},
+ {"sit", "sit down", dosit, FALSE},
++#ifdef STICKY_OBJECTS
++ {"sticky", "set 'sticky' inventory slots", dosticky, TRUE},
++#endif /* STICKY_OBJECTS */
+ {"turn", "turn undead", doturn, TRUE},
+ {"twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE},
+ {"untrap", "untrap something", dountrap, FALSE},
+diff -ruN nethack-3.4.0.orig/src/invent.c nethack-3.4.0/src/invent.c
+--- nethack-3.4.0.orig/src/invent.c Sat Apr 13 16:32:15 2002
++++ nethack-3.4.0/src/invent.c Sat Apr 13 17:14:34 2002
+@@ -36,6 +36,9 @@
+
+ #ifdef OVLB
+
++#ifdef STICKY_OBJECTS
++boolean sticky_inv_hack = 0; /* blech ick yuck... */
++#endif /* STICKY_OBJECTS */
+ static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */
+
+ #ifdef WIZARD
+@@ -164,6 +167,9 @@
+ struct obj **potmp, **pobj;
+ {
+ register struct obj *otmp = *potmp, *obj = *pobj;
++#ifdef STICKY_OBJECTS
++ boolean sticky_hack = 0;
++#endif /* STICKY_OBJECTS */
+
+ if(mergable(otmp, obj)) {
+ /* Approximate age: we do it this way because if we were to
+@@ -228,7 +234,19 @@
+ }
+ #endif /*0*/
+
++#ifdef STICKY_OBJECTS
++ if (flags.invlet_constant && obj->invlet &&
++ obj->sticky && !otmp->sticky) {
++ otmp->invlet = obj->invlet;
++ otmp->sticky = 1;
++ sticky_hack = 1;
++ }
++#endif /* STICKY_OBJECTS */
+ obfree(obj,otmp); /* free(obj), bill->otmp */
++#ifdef STICKY_OBJECTS
++ /* if we're not merging in inventory, this will be a nop */
++ if (sticky_hack) reorder_invent();
++#endif /* STICKY_OBJECTS */
+ return(1);
+ }
+ return 0;
+@@ -308,6 +326,10 @@
+ struct obj *obj;
+ {
+ struct obj *otmp, *prev;
++#ifdef STICKY_OBJECTS
++ struct obj *otmp2;
++ char save_invlet = '\0';
++#endif /* STICKY_OBJECTS */
+
+ if (obj->where != OBJ_FREE)
+ panic("addinv: obj not free");
+@@ -320,6 +342,16 @@
+ return obj;
+ #endif
+
++#ifdef STICKY_OBJECTS
++ /* if object is sticky, find the object we'll be displacing */
++ if (flags.invlet_constant && obj->invlet && obj->sticky) {
++ save_invlet = obj->invlet;
++ for (otmp2 = invent; otmp2; otmp2 = otmp2->nobj)
++ if (otmp2->invlet == save_invlet)
++ break;
++ }
++#endif /* STICKY_OBJECTS */
++
+ /* merge if possible; find end of chain in the process */
+ for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj)
+ if (merged(&otmp, &obj)) {
+@@ -328,7 +360,15 @@
+ }
+ /* didn't merge, so insert into chain */
+ if (flags.invlet_constant || !prev) {
+- if (flags.invlet_constant) assigninvlet(obj);
++ if (flags.invlet_constant) {
++ assigninvlet(obj);
++#ifdef STICKY_OBJECTS
++ if (save_invlet) {
++ if (otmp2) otmp2->invlet = obj->invlet;
++ obj->invlet = save_invlet;
++ }
++#endif /* STICKY_OBJECTS */
++ }
+ obj->nobj = invent; /* insert at beginning */
+ invent = obj;
+ if (flags.invlet_constant) reorder_invent();
+@@ -1530,9 +1570,16 @@
+ #endif
+ } else {
+ /* ordinary inventory display or pickup message */
++#ifndef STICKY_OBJECTS
+ Sprintf(li, "%c - %s%s",
+ (use_invlet ? obj->invlet : let),
+ (txt ? txt : doname(obj)), (dot ? "." : ""));
++#else
++ Sprintf(li, "%c %c %s%s",
++ (use_invlet ? obj->invlet : let),
++ (use_invlet && obj->sticky ? '=' : '-'),
++ (txt ? txt : doname(obj)), (dot ? "." : ""));
++#endif /* STICKY_OBJECTS */
+ }
+ if (savequan) obj->quan = savequan;
+
+@@ -1695,9 +1742,19 @@
+ classcount++;
+ }
+ any.a_char = ilet;
++#ifdef STICKY_OBJECTS
++ /*
++ * Yes, this is evil and disgusting. If you
++ * have a better way of doing this, jump on it.
++ */
++ sticky_inv_hack = otmp->sticky;
++#endif /* STICKY_OBJECTS */
+ add_menu(win, obj_to_glyph(otmp),
+ &any, ilet, 0, ATR_NONE, doname(otmp),
+ MENU_UNSELECTED);
++#ifdef STICKY_OBJECTS
++ sticky_inv_hack = 0;
++#endif /* STICKY_OBJECTS */
+ #ifdef DUMP_LOG
+ if (want_dump) {
+ char letbuf[7];
+@@ -2656,6 +2713,22 @@
+ update_inventory();
+ return(0);
+ }
++#ifdef STICKY_OBJECTS
++int
++dosticky() /* toggle "sticky" inventory slot */
++{
++ struct obj *obj;
++ char allowall[2];
++
++ /* get a pointer to the object the user wants to modify */
++ allowall[0] = ALL_CLASSES; allowall[1] = '\0';
++ if (!(obj = getobj(allowall,"mark"))) return 0;
++
++ obj->sticky = !obj->sticky;
++ prinv(obj->sticky ? "Marking:" : "Unmarking:", obj, 0L);
++ return(0);
++}
++#endif /* STICKY_OBJECTS */
+
+ /* common to display_minventory and display_cinventory */
+ STATIC_OVL void
--- /dev/null
+diff -ruN nethack-3.4.0.orig/include/extern.h nethack-3.4.0/include/extern.h
+--- nethack-3.4.0.orig/include/extern.h Sat Apr 13 17:17:13 2002
++++ nethack-3.4.0/include/extern.h Sat Apr 13 17:30:36 2002
+@@ -677,6 +677,7 @@
+ E char *FDECL(in_rooms, (XCHAR_P,XCHAR_P,int));
+ E void FDECL(check_special_room, (BOOLEAN_P));
+ E int NDECL(dopickup);
++E int NDECL(dowashhands);
+ E void NDECL(lookaround);
+ E int NDECL(monster_nearby);
+ E void FDECL(nomul, (int));
+diff -ruN nethack-3.4.0.orig/src/cmd.c nethack-3.4.0/src/cmd.c
+--- nethack-3.4.0.orig/src/cmd.c Sat Apr 13 17:17:13 2002
++++ nethack-3.4.0/src/cmd.c Sat Apr 13 17:31:50 2002
+@@ -97,6 +97,7 @@
+ extern int NDECL(dowieldquiver); /**/
+ extern int NDECL(dozap); /**/
+ extern int NDECL(doorganize); /**/
++extern int NDECL(dowashhands); /**/
+ #ifdef STICKY_OBJECTS
+ extern int NDECL(dosticky); /**/
+ #endif /* STICKY_OBJECTS */
+@@ -1800,6 +1801,7 @@
+ {"untrap", "untrap something", dountrap, FALSE},
+ {"version", "list compile time options for this version of NetHack",
+ doextversion, TRUE},
++ {"wash", "wash your hands", dowashhands, FALSE},
+ {"wipe", "wipe off your face", dowipe, FALSE},
+ {"?", "get this list of extended commands", doextlist, TRUE},
+ #if defined(WIZARD)
+diff -ruN nethack-3.4.0.orig/src/do_wear.c nethack-3.4.0/src/do_wear.c
+--- nethack-3.4.0.orig/src/do_wear.c Thu Apr 11 17:58:28 2002
++++ nethack-3.4.0/src/do_wear.c Sat Apr 13 17:30:36 2002
+@@ -1599,6 +1599,11 @@
+ if (otmp->otyp != LOADSTONE || !otmp->cursed)
+ dropx(otmp);
+ }
++ if (Glib && Underwater) {
++ Glib = 0;
++ Your("%s are much cleaner now.",
++ makeplural(body_part(HAND)));
++ }
+ }
+
+ struct obj *
+diff -ruN nethack-3.4.0.orig/src/hack.c nethack-3.4.0/src/hack.c
+--- nethack-3.4.0.orig/src/hack.c Sat Apr 13 17:19:02 2002
++++ nethack-3.4.0/src/hack.c Sat Apr 13 17:30:36 2002
+@@ -443,6 +443,66 @@
+ newsym(ox, oy);
+ }
+
++int
++dowashhands()
++{
++ register struct rm *lev = &levl[u.ux][u.uy];
++
++ if (check_capacity((char *)0)) return (0);
++ if (nohands(youmonst.data)) {
++ You("have no hands.");
++ return 0;
++ }
++ if (Underwater) {
++ pline("What, are you keeping your %s %s?",
++ makeplural(body_part(HAND)),
++ (Is_waterlevel(&u.uz) ? "out of the water" : "above the water level"));
++ return (0);
++ }
++ if (IS_POOL(lev->typ) || IS_FOUNTAIN(lev->typ)
++#ifdef SINKS
++ || IS_SINK(lev->typ)
++#endif
++ ) {
++ if (Levitation && !Lev_at_will) {
++ You_cant("reach the water.");
++ return (0);
++ }
++ if (uwep && uwep->cursed || uswapwep && uswapwep->cursed) {
++ You_cant("do a proper scrubbing without both hands free.");
++ return (0);
++ }
++ if (uarmg) {
++ /* Glib state is not maintained separately for gloves and hands */
++ You("clean your gloves in the water.");
++ } else
++ if (noncorporeal(youmonst.data) || unsolid(youmonst.data)) {
++ pline_The("water flows through your %s.",makeplural(body_part(HAND)));
++ } else {
++ You("%s your %s%s.",
++ (Role_if(PM_HEALER) ? "scrub" : "wash"),
++ makeplural(body_part(HAND)),
++ (Glib ? " clean" : ", but they don't get any cleaner"));
++#ifdef TOURIST
++ /* Tourist is the nearest thing to a hitchhiker? */
++ if (!carrying(TOWEL) && Role_if(PM_TOURIST))
++ pline("Do you know where your towel is?");
++#endif
++ }
++ Glib = 0;
++ /* Monsters with flames don't have hands, otherwise could deal
++ * few points of damage: "The water extinguishes [some of] your flames!"
++ * (What about salamanders?)
++ */
++ return (1);
++ } else {
++ You_cant("%s any water %s.",
++ (Blind ? "feel" : "see"),
++ (Is_waterlevel(&u.uz) ? "close enough" : "here"));
++ return (0);
++ }
++}
++
+ #ifdef SINKS
+ static NEARDATA const char fell_on_sink[] = "fell onto a sink";
+