diff -Naurd ../nethack-3.3.1/include/amiconf.h ./include/amiconf.h
--- ../nethack-3.3.1/include/amiconf.h Wed Jan 19 15:40:33 2000
+++ ./include/amiconf.h Fri Mar 22 14:40:55 2002
@@ -1,4 +1,4 @@
-/*	SCCS Id: @(#)amiconf.h	3.3	2000/01/12	*/
+/*	SCCS Id: @(#)amiconf.h	3.4	2000/01/12	*/
 /* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1990, 1991, 1992, 1993. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -30,13 +30,23 @@
 # define DCC30_BUG	/* A bitfield bug (from dog.c, others) in DICE 3.0. */
 #endif
 
+#ifndef __GNUC__
 typedef long off_t;
+#endif
 
 #define MICRO		/* must be defined to allow some inclusions */
 
+#define NOCWD_ASSUMPTIONS	/* Allow paths to be specified for HACKDIR,
+				   LEVELDIR, SAVEDIR, BONESDIR, DATADIR,
+				   SCOREDIR, LOCKDIR, and CONFIGDIR. */
+
 /* data librarian defs */
-#define DLBFILE		"NetHack:nhdat"		/* main library */
-#define DLBFILE2	"NetHack:nhsdat"	/* sound library */
+#ifndef NOCWD_ASSUMPTIONS
+# define DLBFILE	"NetHack:nhdat"		/* main library */
+# define DLBFILE2	"NetHack:nhsdat"	/* sound library */
+#else
+# define DLBFILE	"nhdat"			/* main library */
+# define DLBFILE2	"nhsdat"		/* sound library */
 #define FILENAME_CMP	stricmp			/* case insensitive */
 
 #ifndef __SASC_60
@@ -86,7 +96,8 @@
 extern void FDECL(ami_mkargline, (int *, char **[]));
 extern void ami_wininit_data(void);
 
-extern boolean FromWBench;	/* how were we run? */
+#define FromWBench 0 /* A hint for compiler ... */
+/* extern boolean FromWBench;	/* how were we run? */
 extern int ami_argc;
 extern char **ami_argv;
 
@@ -101,7 +112,7 @@
 #define remove(x)	unlink(x)
 
 /* DICE wants rewind() to return void.	We want it to return int. */
-#ifdef _DCC
+#if defined(_DCC) || defined(__GNUC__)
 # define rewind(f)	fseek(f, 0, 0)
 #endif
 
diff -Naurd ../nethack-3.3.1/include/artifact.h ./include/artifact.h
--- ../nethack-3.3.1/include/artifact.h Thu Dec 30 14:40:50 1999
+++ ./include/artifact.h Fri Mar 22 14:40:55 2002
@@ -47,6 +47,7 @@
 	aligntyp    alignment;	/* alignment of bequeathing gods */
 	short	    role;	/* character role associated with */
 	short	    race;	/* character race associated with */
+	long        cost;	/* price when sold to hero (default 100 x base cost) */
 };
 
 /* invoked properties with special powers */
diff -Naurd ../nethack-3.3.1/include/artilist.h ./include/artilist.h
--- ../nethack-3.3.1/include/artilist.h Sat Jul 22 19:31:21 2000
+++ ./include/artilist.h Fri Mar 22 14:40:55 2002
@@ -1,18 +1,18 @@
-/*	SCCS Id: @(#)artilist.h 3.3	2000/07/22	*/
+/*	SCCS Id: @(#)artilist.h 3.4	2001/11/17	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #ifdef MAKEDEFS_C
 /* in makedefs.c, all we care about is the list of names */
 
-#define A(nam,typ,s1,s2,mt,atk,dfn,cry,inv,al,cl,rac) nam
+#define A(nam,typ,s1,s2,mt,atk,dfn,cry,inv,al,cl,rac,cost) nam
 
 static const char *artifact_names[] = {
 #else
 /* in artifact.c, set up the actual artifact list structure */
 
-#define A(nam,typ,s1,s2,mt,atk,dfn,cry,inv,al,cl, rac) \
- { typ, nam, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac }
+#define A(nam,typ,s1,s2,mt,atk,dfn,cry,inv,al,cl,rac,cost) \
+ { typ, nam, s1, s2, mt, atk, dfn, cry, inv, al, cl, rac, cost }
 
 #define     NO_ATTK	{0,0,0,0}		/* no attack */
 #define     NO_DFNS	{0,0,0,0}		/* no defense */
@@ -29,41 +29,48 @@
 STATIC_OVL NEARDATA struct artifact artilist[] = {
 #endif	/* MAKEDEFS_C */
 
+/* Artifact cost rationale:
+ * 1.  The more useful the artifact, the better its cost.
+ * 2.  Quest artifacts are highly valued.
+ * 3.  Chaotic artifacts are inflated due to scarcity (and balance).
+ */
+
+
 /*  dummy element #0, so that all interesting indices are non-zero */
 A("",				STRANGE_OBJECT,
-	0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM ),
+	0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 0L ),
 
 A("Excalibur",			LONG_SWORD,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_SEEK|SPFX_DEFN|SPFX_INTEL|SPFX_SEARCH),0,0,
-	PHYS(5,10),	DRLI(0,0),	NO_CARY,	0, A_LAWFUL, PM_KNIGHT, NON_PM ),
+	PHYS(5,10),	DRLI(0,0),	NO_CARY,	0, A_LAWFUL, PM_KNIGHT, NON_PM, 4000L ),
 /*
  *	Stormbringer only has a 2 because it can drain a level,
  *	providing 8 more.
  */
 A("Stormbringer",		RUNESWORD,
 	(SPFX_RESTR|SPFX_ATTK|SPFX_DEFN|SPFX_INTEL|SPFX_DRLI), 0, 0,
-	DRLI(5,2),	DRLI(0,0),	NO_CARY,	0, A_CHAOTIC, NON_PM, NON_PM ),
+	DRLI(5,2),	DRLI(0,0),	NO_CARY,	0, A_CHAOTIC, NON_PM, NON_PM, 8000L ),
 /*
  *	Mjollnir will return to the hand of the wielder when thrown
  *	if the wielder is a Valkyrie wearing Gauntlets of Power.
  */
 A("Mjollnir",			WAR_HAMMER,		/* Mjo:llnir */
 	(SPFX_RESTR|SPFX_ATTK),  0, 0,
-	ELEC(5,24),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, PM_VALKYRIE, NON_PM ),
+	ELEC(5,24),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, PM_VALKYRIE, NON_PM, 4000L ),
 
 A("Cleaver",			BATTLE_AXE,
 	SPFX_RESTR, 0, 0,
-	PHYS(3,6),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, PM_BARBARIAN, NON_PM ),
+	PHYS(3,6),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, PM_BARBARIAN, NON_PM, 1500L ),
 
 A("Grimtooth",			ORCISH_DAGGER,
 	SPFX_RESTR, 0, 0,
-	PHYS(2,6),	NO_DFNS,	NO_CARY,	0, A_CHAOTIC, NON_PM, PM_ORC ),
+	PHYS(2,6),	NO_DFNS,	NO_CARY,	0, A_CHAOTIC, NON_PM, PM_ORC, 300L ),
 /*
  *	Orcrist and Sting have same alignment as elves.
  */
 A("Orcrist",			ELVEN_BROADSWORD,
 	SPFX_DFLAG2, 0, M2_ORC,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_CHAOTIC, NON_PM, PM_ELF ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_CHAOTIC, NON_PM, PM_ELF, 2000L ),
 
 /*
  *	The combination of SPFX_WARN and M2_something on an artifact
@@ -73,50 +80,50 @@
  */
 A("Sting",			ELVEN_DAGGER,
 	(SPFX_WARN|SPFX_DFLAG2), 0, M2_ORC,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_CHAOTIC, NON_PM, PM_ELF ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_CHAOTIC, NON_PM, PM_ELF, 800L ),
 /*
  *	Magicbane is a bit different!  Its magic fanfare
  *	unbalances victims in addition to doing some damage.
  */
 A("Magicbane",			ATHAME,
 	(SPFX_RESTR|SPFX_ATTK|SPFX_DEFN), 0, 0,
-	STUN(3,4),	DFNS(AD_MAGM),	NO_CARY,	0, A_NEUTRAL, PM_WIZARD, NON_PM ),
+	STUN(3,4),	DFNS(AD_MAGM),	NO_CARY,	0, A_NEUTRAL, PM_WIZARD, NON_PM, 3500L ),
 
 A("Frost Brand",		LONG_SWORD,
 	(SPFX_RESTR|SPFX_ATTK|SPFX_DEFN), 0, 0,
-	COLD(5,0),	COLD(0,0),	NO_CARY,	0, A_NONE, NON_PM, NON_PM ),
+	COLD(5,0),	COLD(0,0),	NO_CARY,	0, A_NONE, NON_PM, NON_PM, 3000L ),
 
 A("Fire Brand",			LONG_SWORD,
 	(SPFX_RESTR|SPFX_ATTK|SPFX_DEFN), 0, 0,
-	FIRE(5,0),	FIRE(0,0),	NO_CARY,	0, A_NONE, NON_PM, NON_PM ),
+	FIRE(5,0),	FIRE(0,0),	NO_CARY,	0, A_NONE, NON_PM, NON_PM, 3000L ),
 
 A("Dragonbane",			BROADSWORD,
 	(SPFX_RESTR|SPFX_DCLAS), 0, S_DRAGON,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM, 500L ),
 
 A("Demonbane",			LONG_SWORD,
 	(SPFX_RESTR|SPFX_DFLAG2), 0, M2_DEMON,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_LAWFUL, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_LAWFUL, NON_PM, NON_PM, 2500L ),
 
 A("Werebane",			SILVER_SABER,
 	(SPFX_RESTR|SPFX_DFLAG2), 0, M2_WERE,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM, 1500L ),
 
 A("Grayswandir",		SILVER_SABER,
 	(SPFX_RESTR|SPFX_HALRES), 0, 0,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_LAWFUL, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_LAWFUL, NON_PM, NON_PM, 8000L ),
 
 A("Giantslayer",		LONG_SWORD,
 	(SPFX_RESTR|SPFX_DFLAG2), 0, M2_GIANT,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, NON_PM, NON_PM, 200L ),
 
 A("Ogresmasher",		WAR_HAMMER,
 	(SPFX_RESTR|SPFX_DCLAS), 0, S_OGRE,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM, 200L ),
 
 A("Trollsbane",			MORNING_STAR,
 	(SPFX_RESTR|SPFX_DCLAS), 0, S_TROLL,
-	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM ),
+	PHYS(5,0),	NO_DFNS,	NO_CARY,	0, A_NONE, NON_PM, NON_PM, 200L ),
 /*
  *	Two problems:  1) doesn't let trolls regenerate heads,
  *	2) doesn't give unusual message for 2-headed monsters (but
@@ -124,7 +131,7 @@
  */
 A("Vorpal Blade",		LONG_SWORD,
 	(SPFX_RESTR|SPFX_BEHEAD), 0, 0,
-	PHYS(5,1),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, NON_PM, NON_PM ),
+	PHYS(5,1),	NO_DFNS,	NO_CARY,	0, A_NEUTRAL, NON_PM, NON_PM, 4000L ),
 /*
  *	Ah, never shall I forget the cry,
  *		or the shriek that shrieked he,
@@ -135,11 +142,11 @@
  */
 A("Snickersnee",		KATANA,
 	SPFX_RESTR, 0, 0,
-	PHYS(0,8),	NO_DFNS,	NO_CARY,	0, A_LAWFUL, PM_SAMURAI, NON_PM ),
+	PHYS(0,8),	NO_DFNS,	NO_CARY,	0, A_LAWFUL, PM_SAMURAI, NON_PM, 1200L ),
 
 A("Sunsword",			LONG_SWORD,
 	(SPFX_RESTR|SPFX_DFLAG2), 0, M2_UNDEAD,
-	PHYS(5,0),	DFNS(AD_BLND),	NO_CARY,	0, A_LAWFUL, NON_PM, NON_PM ),
+	PHYS(5,0),	DFNS(AD_BLND),	NO_CARY,	0, A_LAWFUL, NON_PM, NON_PM, 1500L ),
 
 /*
  *	The artifacts for the quest dungeon, all self-willed.
@@ -148,85 +155,86 @@
 A("The Orb of Detection",	CRYSTAL_BALL,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL), (SPFX_ESP|SPFX_HSPDAM), 0,
 	NO_ATTK,	NO_DFNS,	CARY(AD_MAGM),
-	INVIS,		A_LAWFUL, PM_ARCHEOLOGIST, NON_PM ),
+	INVIS,		A_LAWFUL, PM_ARCHEOLOGIST, NON_PM, 2500L ),
 
 A("The Heart of Ahriman",	LUCKSTONE,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL), SPFX_STLTH, 0,
-	NO_ATTK,	NO_DFNS,	NO_CARY,
-	LEVITATION,	A_NEUTRAL, PM_BARBARIAN, NON_PM ),
+	/* this stone does double damage if used as a projectile weapon */
+	PHYS(5,0),	NO_DFNS,	NO_CARY,
+	LEVITATION,	A_NEUTRAL, PM_BARBARIAN, NON_PM, 2500L ),
 
 A("The Sceptre of Might",	MACE,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_DALIGN), 0, 0,
 	PHYS(5,0),	NO_DFNS,	CARY(AD_MAGM),
-	CONFLICT,	A_LAWFUL, PM_CAVEMAN, NON_PM ),
+	CONFLICT,	A_LAWFUL, PM_CAVEMAN, NON_PM, 2500L ),
 
 #if 0	/* OBSOLETE */
 A("The Palantir of Westernesse",	CRYSTAL_BALL,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL),
 		(SPFX_ESP|SPFX_REGEN|SPFX_HSPDAM), 0,
 	NO_ATTK,	NO_DFNS,	NO_CARY,
-	TAMING,		A_CHAOTIC, NON_PM , PM_ELF),
+	TAMING,		A_CHAOTIC, NON_PM , PM_ELF, 8000L ),
 #endif
 
 A("The Staff of Aesculapius",	QUARTERSTAFF,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_ATTK|SPFX_INTEL|SPFX_DRLI|SPFX_REGEN), 0,0,
 	DRLI(0,0),	DRLI(0,0),	NO_CARY,
-	HEALING,	A_NEUTRAL, PM_HEALER, NON_PM ),
+	HEALING,	A_NEUTRAL, PM_HEALER, NON_PM, 5000L ),
 
 A("The Magic Mirror of Merlin", MIRROR,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_SPEAK), SPFX_ESP, 0,
 	NO_ATTK,	NO_DFNS,	CARY(AD_MAGM),
-	0,		A_LAWFUL, PM_KNIGHT, NON_PM ),
+	0,		A_LAWFUL, PM_KNIGHT, NON_PM, 1500L ),
 
 A("The Eyes of the Overworld",	LENSES,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_XRAY), 0, 0,
 	NO_ATTK,	NO_DFNS,	CARY(AD_MAGM),
-	ENLIGHTENING,	A_NEUTRAL,	 PM_MONK, NON_PM ),
+	ENLIGHTENING,	A_NEUTRAL,	 PM_MONK, NON_PM, 2500L ),
 
 A("The Mitre of Holiness",	HELM_OF_BRILLIANCE,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_DFLAG2|SPFX_INTEL), 0, M2_UNDEAD,
 	NO_ATTK,	NO_DFNS,	CARY(AD_FIRE),
-	ENERGY_BOOST,	A_LAWFUL, PM_PRIEST, NON_PM ),
+	ENERGY_BOOST,	A_LAWFUL, PM_PRIEST, NON_PM, 2000L ),
 
 A("The Longbow of Diana", BOW,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_REFLECT), SPFX_ESP, 0,
 	PHYS(5,0),	NO_DFNS,	NO_CARY,
-	CREATE_AMMO, A_CHAOTIC, PM_RANGER, NON_PM ),
+	CREATE_AMMO, A_CHAOTIC, PM_RANGER, NON_PM, 4000L ),
 
 A("The Master Key of Thievery", SKELETON_KEY,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_SPEAK),
 		(SPFX_WARN|SPFX_TCTRL|SPFX_HPHDAM), 0,
 	NO_ATTK,	NO_DFNS,	NO_CARY,
-	UNTRAP,		A_CHAOTIC, PM_ROGUE, NON_PM ),
+	UNTRAP,		A_CHAOTIC, PM_ROGUE, NON_PM, 3500L ),
 
 A("The Tsurugi of Muramasa",	TSURUGI,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_BEHEAD|SPFX_LUCK), 0, 0,
 	PHYS(0,8),	NO_DFNS,	NO_CARY,
-	0,		A_LAWFUL, PM_SAMURAI, NON_PM ),
+	0,		A_LAWFUL, PM_SAMURAI, NON_PM, 4500L ),
 
 #ifdef TOURIST
 A("The Platinum Yendorian Express Card", CREDIT_CARD,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_DEFN),
 		(SPFX_ESP|SPFX_HSPDAM), 0,
 	NO_ATTK,	NO_DFNS,	CARY(AD_MAGM),
-	CHARGE_OBJ,	A_NEUTRAL, PM_TOURIST, NON_PM ),
+	CHARGE_OBJ,	A_NEUTRAL, PM_TOURIST, NON_PM, 7000L ),
 #endif
 
 A("The Orb of Fate",		CRYSTAL_BALL,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL|SPFX_LUCK),
 		(SPFX_WARN|SPFX_HSPDAM|SPFX_HPHDAM), 0,
 	NO_ATTK,	NO_DFNS,	NO_CARY,
-	LEV_TELE,	A_NEUTRAL, PM_VALKYRIE, NON_PM ),
+	LEV_TELE,	A_NEUTRAL, PM_VALKYRIE, NON_PM, 3500L ),
 
 A("The Eye of the Aethiopica",	AMULET_OF_ESP,
 	(SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL), (SPFX_EREGEN|SPFX_HSPDAM), 0,
 	NO_ATTK,	NO_DFNS,	CARY(AD_MAGM),
-	CREATE_PORTAL,	A_NEUTRAL, PM_WIZARD, NON_PM ),
+	CREATE_PORTAL,	A_NEUTRAL, PM_WIZARD, NON_PM, 4000L ),
 
 /*
  *  terminator; otyp must be zero
  */
-A(0, 0, 0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM )
+A(0, 0, 0, 0, 0, NO_ATTK, NO_DFNS, NO_CARY, 0, A_NONE, NON_PM, NON_PM, 0L )
 
 };	/* artilist[] (or artifact_names[]) */
 
diff -Naurd ../nethack-3.3.1/include/config.h ./include/config.h
--- ../nethack-3.3.1/include/config.h Sat Jul 22 02:13:51 2000
+++ ./include/config.h Fri Mar 22 14:40:55 2002
@@ -82,6 +83,7 @@
 #endif
 
 #ifdef QT_GRAPHICS
+# define USER_SOUNDS		/* Use sounds */
 # define USE_XPM		/* Use XPM format for images (required) */
 # define GRAPHIC_TOMBSTONE	/* Use graphical tombstone (rip.ppm) */
 # ifndef DEFAULT_WINDOW_SYS
diff -Naurd ../nethack-3.3.1/include/config1.h ./include/config1.h
--- ../nethack-3.3.1/include/config1.h Mon Dec 6 21:22:04 1999
+++ ./include/config1.h Fri Mar 22 14:40:55 2002
@@ -116,6 +121,7 @@
 # define STRNCMPI
 # define USE_STDARG
 # define NEED_VARARGS
+
 #endif
 
 
diff -Naurd ../nethack-3.3.1/include/coord.h ./include/coord.h
--- ../nethack-3.3.1/include/coord.h Wed Jul 7 05:16:31 1999
+++ ./include/coord.h Fri Mar 22 14:40:55 2002
@@ -1,11 +1,11 @@
-/*	SCCS Id: @(#)coord.h	3.3	90/02/22	*/
+/*	SCCS Id: @(#)coord.h	3.4	1990/02/22	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #ifndef COORD_H
 #define COORD_H
 
-typedef struct {
+typedef struct nhcoord {
 	xchar x,y;
 } coord;
 
diff -Naurd ../nethack-3.3.1/include/decl.h ./include/decl.h
--- ../nethack-3.3.1/include/decl.h Sat Jul 22 01:58:59 2000
+++ ./include/decl.h Fri Mar 22 14:40:56 2002
@@ -27,7 +27,9 @@
 E NEARDATA int bases[MAXOCLASSES];
 
 E NEARDATA int multi;
+#if 0
 E NEARDATA int warnlevel;
+#endif
 E NEARDATA int nroom;
 E NEARDATA int nsubroom;
 E NEARDATA int occtime;
@@ -162,6 +164,9 @@
 E const char ynaqchars[];
 E const char ynNaqchars[];
 E NEARDATA long yn_number;
+
+E const char disclosure_options[];
+
 E NEARDATA int smeq[];
 E NEARDATA int doorindex;
 E NEARDATA char *save_cm;
@@ -188,11 +193,14 @@
 
 E NEARDATA schar tbx, tby;		/* set in mthrowu.c */
 
+E NEARDATA struct multishot { int n, i; short o; boolean s; } m_shot;
+
 E NEARDATA struct dig_info {		/* apply.c, hack.c */
 	int	effort;
 	d_level level;
 	coord	pos;
-	boolean down, chew;
+	long lastdigtime;
+	boolean down, chew, warned, quiet;
 } digging;
 
 E NEARDATA long moves, monstermoves;
@@ -236,10 +244,6 @@
 E NEARDATA struct obj *billobjs;
 E NEARDATA struct obj zeroobj;		/* init'd and defined in decl.c */
 
-E const char *he[3];
-E const char *him[3];
-E const char *his[3];
-
 #include "you.h"
 E NEARDATA struct you u;
 
@@ -274,12 +278,15 @@
 #define purple		c_color_names.c_purple
 #define White		c_color_names.c_white
 
+/* The names of the colors used for gems, etc. */
+E const char *c_obj_colors[];
+
 E struct c_common_strings {
     const char	*const c_nothing_happens, *const c_thats_enough_tries,
 		*const c_silly_thing_to, *const c_shudder_for_moment,
 		*const c_something, *const c_Something,
 		*const c_You_can_move_again,
-		*const c_Never_mind;
+		*const c_Never_mind, *c_vision_clears;
 } c_common_strings;
 #define nothing_happens    c_common_strings.c_nothing_happens
 #define thats_enough_tries c_common_strings.c_thats_enough_tries
@@ -289,6 +296,7 @@
 #define Something	   c_common_strings.c_Something
 #define You_can_move_again c_common_strings.c_You_can_move_again
 #define Never_mind	   c_common_strings.c_Never_mind
+#define vision_clears	   c_common_strings.c_vision_clears
 
 /* material strings */
 E const char *materialnm[];
diff -Naurd ../nethack-3.3.1/include/display.h ./include/display.h
--- ../nethack-3.3.1/include/display.h Thu Aug 3 20:39:09 2000
+++ ./include/display.h Fri Mar 22 14:40:55 2002
@@ -245,6 +245,11 @@
  *		is the dungeon features and other miscellaneous things.
  *		Count: MAXPCHARS
  *
+ * explosions	A set of nine for each of the following seven explosion types:
+ *                   dark, noxious, muddy, wet, magical, fiery, frosty.
+ *              The nine positions represent those surrounding the hero.
+ *		Count: MAXEXPCHARS * EXPL_MAX (EXPL_MAX is defined in hack.h)
+ *
  * zap beam	A set of four (there are four directions) for each beam type.
  *		The beam type is shifted over 2 positions and the direction
  *		is stored in the lower 2 bits.	Count: NUM_ZAP << 2
@@ -268,7 +273,8 @@
 #define GLYPH_RIDDEN_OFF	(NUMMONS	+ GLYPH_BODY_OFF)
 #define GLYPH_OBJ_OFF		(NUMMONS	+ GLYPH_RIDDEN_OFF)
 #define GLYPH_CMAP_OFF		(NUM_OBJECTS	+ GLYPH_OBJ_OFF)
-#define GLYPH_ZAP_OFF		(MAXPCHARS	+ GLYPH_CMAP_OFF)
+#define GLYPH_EXPLODE_OFF	((MAXPCHARS - MAXEXPCHARS) + GLYPH_CMAP_OFF)
+#define GLYPH_ZAP_OFF		((MAXEXPCHARS * EXPL_MAX) + GLYPH_EXPLODE_OFF)
 #define GLYPH_SWALLOW_OFF	((NUM_ZAP << 2) + GLYPH_ZAP_OFF)
 #define GLYPH_WARNING_OFF	((NUMMONS << 3) + GLYPH_SWALLOW_OFF)
 #define MAX_GLYPH		(WARNCOUNT      + GLYPH_WARNING_OFF)
@@ -295,6 +301,9 @@
 	    (int) (obj)->otyp + GLYPH_OBJ_OFF))
 
 #define cmap_to_glyph(cmap_idx) ((int) (cmap_idx)   + GLYPH_CMAP_OFF)
+#define explosion_to_glyph(expltype,idx)	\
+		((((expltype) * MAXEXPCHARS) + ((idx) - S_explode1)) + GLYPH_EXPLODE_OFF)
+
 #define trap_to_glyph(trap)	\
 			cmap_to_glyph(trap_to_defsym(what_trap((trap)->ttyp)))
 
diff -Naurd ../nethack-3.3.1/include/extern.h ./include/extern.h
--- ../nethack-3.3.1/include/extern.h Wed Aug 9 16:46:17 2000
+++ ./include/extern.h Fri Mar 22 14:40:56 2002
@@ -34,7 +34,7 @@
 E int FDECL(jump, (int));
 E int NDECL(number_leashed);
 E void FDECL(o_unleash, (struct obj *));
-E void FDECL(m_unleash, (struct monst *));
+E void FDECL(m_unleash, (struct monst *,BOOLEAN_P));
 E void NDECL(unleash_all);
 E boolean NDECL(next_to_u);
 E struct obj *FDECL(get_mleash, (struct monst *));
@@ -43,10 +43,12 @@
 E boolean FDECL(um_dist, (XCHAR_P,XCHAR_P,XCHAR_P));
 E boolean FDECL(snuff_candle, (struct obj *));
 E boolean FDECL(snuff_lit, (struct obj *));
+E boolean FDECL(catch_lit, (struct obj *));
 E void FDECL(use_unicorn_horn, (struct obj *));
 E boolean FDECL(tinnable, (struct obj *));
 E void NDECL(reset_trapset);
 E void FDECL(fig_transform, (genericptr_t, long));
+E int FDECL(unfixable_trouble_count,(BOOLEAN_P));
 
 /* ### artifact.c ### */
 
@@ -74,7 +76,10 @@
 				struct obj *,int *,int));
 E int NDECL(doinvoke);
 E void FDECL(arti_speak, (struct obj *));
+E boolean FDECL(artifact_light, (struct obj *));
 E long FDECL(spec_m2, (struct obj *));
+E boolean FDECL(artifact_has_invprop, (struct obj *,UCHAR_P));
+E long FDECL(arti_cost, (struct obj *));
 
 /* ### attrib.c ### */
 
@@ -166,7 +171,7 @@
 E int FDECL(getdir, (const char *));
 E void NDECL(confdir);
 E int FDECL(isok, (int,int));
-E int FDECL(click_to_cmd, (int,int,int));
+E const char *FDECL(click_to_cmd, (int,int,int));
 E char NDECL(readchar);
 #ifdef WIZARD
 E void NDECL(sanity_check);
@@ -192,6 +197,7 @@
 /* ### detect.c ### */
 
 E struct obj *FDECL(o_in, (struct obj*,CHAR_P));
+E struct obj *FDECL(o_material, (struct obj*,unsigned));
 E int FDECL(gold_detect, (struct obj *));
 E int FDECL(food_detect, (struct obj *));
 E int FDECL(object_detect, (struct obj *,int));
@@ -215,6 +221,7 @@
 
 /* ### dig.c ### */
 
+E boolean NDECL(is_digging);
 #ifdef USE_TRAMPOLI
 E int NDECL(dig);
 #endif
@@ -223,7 +230,9 @@
 E void FDECL(digactualhole, (int,int,struct monst *,int));
 E boolean FDECL(dighole, (BOOLEAN_P));
 E int FDECL(use_pick_axe, (struct obj *));
+E int FDECL(use_pick_axe2, (struct obj *));
 E boolean FDECL(mdig_tunnel, (struct monst *));
+E void FDECL(watch_dig, (struct monst *,XCHAR_P,XCHAR_P,BOOLEAN_P));
 E void NDECL(zap_dig);
 E struct obj *FDECL(bury_an_obj, (struct obj *));
 E void FDECL(bury_objs, (int,int));
@@ -287,6 +296,7 @@
 E boolean FDECL(canletgo, (struct obj *,const char *));
 E void FDECL(dropx, (struct obj *));
 E void FDECL(dropy, (struct obj *));
+E void FDECL(obj_no_longer_held, (struct obj *));
 E int NDECL(doddrop);
 E int NDECL(dodown);
 E int NDECL(doup);
@@ -324,15 +334,15 @@
 E char *FDECL(Adjmonnam, (struct monst *,const char *));
 E char *FDECL(Amonnam, (struct monst *));
 E char *FDECL(a_monnam, (struct monst *));
+E char *FDECL(distant_monnam, (struct monst *,int,char *));
 E const char *NDECL(rndmonnam);
 E const char *FDECL(hcolor, (const char *));
-E char *FDECL(self_pronoun, (const char *,const char *));
 #ifdef REINCARNATION
 E const char *NDECL(roguename);
 #endif
 E struct obj *FDECL(realloc_obj,
 		(struct obj *, int, genericptr_t, int, const char *));
-E char *FDECL(coyotename, (char *));
+E char *FDECL(coyotename, (struct monst *,char *));
 
 /* ### do_wear.c ### */
 
@@ -417,7 +427,8 @@
 E int NDECL(dofire);
 E void FDECL(hitfloor, (struct obj *));
 E void FDECL(hurtle, (int,int,int,BOOLEAN_P));
-E void FDECL(throwit, (struct obj *,long));
+E void FDECL(mhurtle, (struct monst *,int,int,int));
+E void FDECL(throwit, (struct obj *,long,BOOLEAN_P));
 E int FDECL(omon_adj, (struct monst *,struct obj *,BOOLEAN_P));
 E int FDECL(thitmonst, (struct monst *,struct obj *));
 E int FDECL(hero_breaks, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P));
@@ -516,6 +527,8 @@
 E void FDECL(food_disappears, (struct obj *));
 E void FDECL(food_substitution, (struct obj *,struct obj *));
 E void NDECL(fix_petrification);
+E void FDECL(consume_oeaten, (struct obj *,int));
+E boolean FDECL(maybe_finished_meal, (BOOLEAN_P));
 
 /* ### end.c ### */
 
@@ -568,7 +581,7 @@
 
 /* ### explode.c ### */
 
-E void FDECL(explode, (int,int,int,int,CHAR_P));
+E void FDECL(explode, (int,int,int,int,CHAR_P,int));
 E void FDECL(scatter, (int, int, int, unsigned int, struct obj *));
 E void FDECL(splatter_burning_oil, (int, int));
 
@@ -617,8 +630,14 @@
 E void FDECL(uncompress, (const char *));
 E boolean FDECL(lock_file, (const char *,int,int));
 E void FDECL(unlock_file, (const char *));
+#ifdef USER_SOUNDS
+E boolean FDECL(can_read_file, (const char *));
+#endif
 E void FDECL(read_config_file, (const char *));
 E void FDECL(check_recordfile, (const char *));
+#if defined(WIZARD)
+E void NDECL(read_wizkit);
+#endif
 
 /* ### fountain.c ### */
 
@@ -643,6 +662,7 @@
 E boolean FDECL(may_passwall, (XCHAR_P,XCHAR_P));
 E boolean FDECL(bad_rock, (struct permonst *,XCHAR_P,XCHAR_P));
 E boolean FDECL(invocation_pos, (XCHAR_P,XCHAR_P));
+E boolean FDECL(test_move, (int, int, int, int, BOOLEAN_P));
 E void NDECL(domove);
 E void NDECL(invocation_message);
 E void FDECL(spoteffects, (BOOLEAN_P));
@@ -669,6 +689,7 @@
 E char FDECL(highc, (CHAR_P));
 E char FDECL(lowc, (CHAR_P));
 E char *FDECL(lcase, (char *));
+E char *FDECL(upstart, (char *));
 E char *FDECL(mungspaces, (char *));
 E char *FDECL(eos, (char *));
 E char *FDECL(s_suffix, (const char *));
@@ -731,19 +752,19 @@
 E struct obj *FDECL(g_at, (int,int));
 E struct obj *FDECL(mkgoldobj, (long));
 E struct obj *FDECL(getobj, (const char *,const char *));
-E int FDECL(ggetobj, (const char *,int (*)(OBJ_P),int,BOOLEAN_P));
+E int FDECL(ggetobj, (const char *,int (*)(OBJ_P),int,BOOLEAN_P,unsigned *));
 E void FDECL(fully_identify_obj, (struct obj *));
 E int FDECL(identify, (struct obj *));
 E void FDECL(identify_pack, (int));
 E int FDECL(askchain, (struct obj **,const char *,int,int (*)(OBJ_P),
 			int (*)(OBJ_P),int,const char *));
 E void FDECL(prinv, (const char *,struct obj *,long));
-E char *FDECL(xprname, (struct obj *,const char *,CHAR_P,BOOLEAN_P,long));
+E char *FDECL(xprname, (struct obj *,const char *,CHAR_P,BOOLEAN_P,long,long));
 E int NDECL(ddoinv);
 E char FDECL(display_inventory, (const char *,BOOLEAN_P));
 E int FDECL(display_binventory, (int,int,BOOLEAN_P));
 E struct obj *FDECL(display_cinventory,(struct obj *));
-E struct obj *FDECL(display_minventory,(struct monst *,int));
+E struct obj *FDECL(display_minventory,(struct monst *,int,char *));
 E int NDECL(dotypeinv);
 E const char *FDECL(dfeature_at, (int,int,char *));
 E int FDECL(look_here, (int,BOOLEAN_P));
@@ -763,7 +784,9 @@
 E void NDECL(reassign);
 E int NDECL(doorganize);
 E int FDECL(count_unpaid, (struct obj *));
+E int FDECL(count_buc, (struct obj *,int));
 E void FDECL(carry_obj_effects, (struct obj *));
+E const char *FDECL(currency, (long));
 
 /* ### ioctl.c ### */
 
@@ -878,10 +901,15 @@
 E boolean FDECL(peace_minded, (struct permonst *));
 E void FDECL(set_malign, (struct monst *));
 E void FDECL(set_mimic_sym, (struct monst *));
+E int FDECL(mbirth_limit, (int));
+
+/* ### mapglyph.c ### */
+
+E void FDECL(mapglyph, (int, int *, int *, unsigned *, int, int));
 
 /* ### mcastu.c ### */
 
-E int FDECL(castmu, (struct monst *,struct attack *));
+E int FDECL(castmu, (struct monst *,struct attack *,BOOLEAN_P,BOOLEAN_P));
 E int FDECL(buzzmu, (struct monst *,struct attack *));
 
 /* ### mhitm.c ### */
@@ -898,6 +926,7 @@
 E void NDECL(u_slow_down);
 E struct monst *NDECL(cloneu);
 E void FDECL(expels, (struct monst *,struct permonst *,BOOLEAN_P));
+E struct attack *FDECL(getmattk, (struct permonst *,int,int *,struct attack *));
 E int FDECL(mattacku, (struct monst *));
 E int FDECL(gazemu, (struct monst *,struct attack *));
 E void FDECL(mdamageu, (struct monst *,int));
@@ -967,7 +996,7 @@
 /* ### mkobj.c ### */
 
 E struct obj *FDECL(mkobj_at, (CHAR_P,int,int,BOOLEAN_P));
-E struct obj *FDECL(mksobj_at, (int,int,int,BOOLEAN_P));
+E struct obj *FDECL(mksobj_at, (int,int,int,BOOLEAN_P,BOOLEAN_P));
 E struct obj *FDECL(mkobj, (CHAR_P,BOOLEAN_P));
 E int NDECL(rndmonnum);
 E struct obj *FDECL(splitobj, (struct obj *,long));
@@ -992,6 +1021,7 @@
 E void FDECL(uncurse, (struct obj *));
 E void FDECL(blessorcurse, (struct obj *,int));
 E boolean FDECL(is_flammable, (struct obj *));
+E boolean FDECL(is_rottable, (struct obj *));
 E void FDECL(place_object, (struct obj *,int,int));
 E void FDECL(remove_object, (struct obj *));
 E void FDECL(discard_minvent, (struct monst *));
@@ -999,7 +1029,7 @@
 E void FDECL(extract_nobj, (struct obj *, struct obj **));
 E void FDECL(extract_nexthere, (struct obj *, struct obj **));
 E int FDECL(add_to_minv, (struct monst *, struct obj *));
-E void FDECL(add_to_container, (struct obj *, struct obj *));
+E struct obj *FDECL(add_to_container, (struct obj *, struct obj *));
 E void FDECL(add_to_migration, (struct obj *));
 E void FDECL(add_to_buried, (struct obj *));
 E void FDECL(dealloc_obj, (struct obj *));
@@ -1030,9 +1060,9 @@
 
 E int FDECL(undead_to_corpse, (int));
 E int FDECL(pm_to_cham, (int));
-E int FDECL(minwater, (struct monst *));
+E int FDECL(minliquid, (struct monst *));
 E int NDECL(movemon);
-E int FDECL(meatgold, (struct monst *));
+E int FDECL(meatmetal, (struct monst *));
 E int FDECL(meatobj, (struct monst *));
 E void FDECL(mpickgold, (struct monst *));
 E boolean FDECL(mpickstuff, (struct monst *,const char *));
@@ -1047,6 +1077,7 @@
 E void FDECL(replmon, (struct monst *,struct monst *));
 E void FDECL(relmon, (struct monst *));
 E struct obj *FDECL(mlifesaver, (struct monst *));
+E boolean FDECL(corpse_chance,(struct monst *,struct monst *,BOOLEAN_P));
 E void FDECL(mondead, (struct monst *));
 E void FDECL(mondied, (struct monst *));
 E void FDECL(mongone, (struct monst *));
@@ -1070,7 +1101,7 @@
 E void NDECL(restartcham);
 E void FDECL(restore_cham, (struct monst *));
 E void FDECL(mon_animal_list, (BOOLEAN_P));
-E int FDECL(newcham, (struct monst *,struct permonst *));
+E int FDECL(newcham, (struct monst *,struct permonst *,BOOLEAN_P));
 E int FDECL(can_be_hatched, (int));
 E int FDECL(egg_type_from_parent, (int,BOOLEAN_P));
 E boolean FDECL(dead_species, (int,BOOLEAN_P));
@@ -1082,6 +1113,7 @@
 /* ### mondata.c ### */
 
 E void FDECL(set_mon_data, (struct monst *,struct permonst *,int));
+E struct attack *FDECL(attacktype_fordmg, (struct permonst *,int,int));
 E boolean FDECL(attacktype, (struct permonst *,int));
 E boolean FDECL(poly_when_stoned, (struct permonst *));
 E boolean FDECL(resists_drli, (struct monst *));
@@ -1095,6 +1127,7 @@
 E boolean FDECL(sliparm, (struct permonst *));
 E boolean FDECL(sticks, (struct permonst *));
 /* E boolean FDECL(canseemon, (struct monst *)); */
+E struct attack *FDECL(dmgtype_fromattack, (struct permonst *,int,int));
 E boolean FDECL(dmgtype, (struct permonst *,int));
 E int FDECL(max_passive_dmg, (struct monst *,struct monst *));
 E int FDECL(monsndx, (struct permonst *));
@@ -1105,6 +1138,7 @@
 E int FDECL(little_to_big, (int));
 E int FDECL(big_to_little, (int));
 E const char *FDECL(locomotion, (const struct permonst *,const char *));
+E const char *FDECL(stagger, (const struct permonst *,const char *));
 
 /* ### monmove.c ### */
 
@@ -1113,6 +1147,7 @@
 E void FDECL(mon_regen, (struct monst *,BOOLEAN_P));
 E int FDECL(dochugw, (struct monst *));
 E boolean FDECL(onscary, (int,int,struct monst *));
+E void FDECL(monflee, (struct monst *, int, BOOLEAN_P, BOOLEAN_P));
 E int FDECL(dochug, (struct monst *));
 E int FDECL(m_move, (struct monst *,int));
 E boolean FDECL(closed_door, (int,int));
@@ -1270,22 +1305,29 @@
 E boolean FDECL(obj_is_pname, (struct obj *));
 E char *FDECL(distant_name, (struct obj *,char *(*)(OBJ_P)));
 E char *FDECL(xname, (struct obj *));
+E char *FDECL(mshot_xname, (struct obj *));
+E boolean FDECL(the_unique_obj, (struct obj *obj));
 E char *FDECL(doname, (struct obj *));
 E boolean FDECL(not_fully_identified, (struct obj *));
-E const char *FDECL(corpse_xname, (struct obj *,BOOLEAN_P));
+E char *FDECL(corpse_xname, (struct obj *,BOOLEAN_P));
+E char *FDECL(cxname, (struct obj *));
 E const char *FDECL(singular, (struct obj *,char *(*)(OBJ_P)));
 E char *FDECL(an, (const char *));
 E char *FDECL(An, (const char *));
 E char *FDECL(The, (const char *));
 E char *FDECL(the, (const char *));
 E char *FDECL(aobjnam, (struct obj *,const char *));
+E char *FDECL(Tobjnam, (struct obj *,const char *));
+E char *FDECL(otense, (struct obj *,const char *));
+E char *FDECL(vtense, (const char *,const char *));
 E char *FDECL(Doname2, (struct obj *));
 E char *FDECL(yname, (struct obj *));
 E char *FDECL(Yname2, (struct obj *));
 E char *FDECL(makeplural, (const char *));
 E char *FDECL(makesingular, (const char *));
-E struct obj *FDECL(readobjnam, (char *));
+E struct obj *FDECL(readobjnam, (char *,struct obj *,BOOLEAN_P));
 E int FDECL(rnd_class, (int,int));
+E const char *FDECL(cloak_simple_name, (struct obj *));
 
 /* ### options.c ### */
 
@@ -1302,6 +1344,9 @@
 E char FDECL(map_menu_cmd, (CHAR_P));
 E void FDECL(assign_warnings, (uchar *));
 E char *FDECL(nh_getenv, (const char *));
+E void FDECL(set_duplicate_opt_detection, (int));
+E void FDECL(set_wc_option_mod_status, (unsigned long, int));
+E void FDECL(set_option_mod_status, (char *, int));
 
 /* ### pager.c ### */
 
@@ -1355,7 +1400,6 @@
 /* ### pcunix.c ### */
 
 #if defined(MICRO)
-E void FDECL(gethdate, (char *));
 E void FDECL(regularize, (char *));
 #endif /* MICRO */
 #if defined(PC_LOCKING)
@@ -1385,6 +1429,7 @@
 E int NDECL(encumber_msg);
 E int NDECL(doloot);
 E int FDECL(use_container, (struct obj *,int));
+E int FDECL(loot_mon, (struct monst *,int *,boolean *));
 
 /* ### pline.c ### */
 
@@ -1410,7 +1455,7 @@
 
 E void NDECL(set_uasmon);
 E void NDECL(change_sex);
-E void NDECL(polyself);
+E void FDECL(polyself, (BOOLEAN_P));
 E int FDECL(polymon, (int));
 E void NDECL(rehumanize);
 E int NDECL(dobreathe);
@@ -1418,9 +1463,10 @@
 E int NDECL(doremove);
 E int NDECL(dospinweb);
 E int NDECL(dosummon);
-E int NDECL(doconfuse);
+E int NDECL(dogaze);
 E int NDECL(dohide);
 E int NDECL(domindblast);
+E void FDECL(skinback, (BOOLEAN_P));
 E const char *FDECL(mbodypart, (struct monst *,int));
 E const char *FDECL(body_part, (int));
 E int NDECL(poly_gender);
@@ -1447,6 +1493,7 @@
 E int NDECL(dodip);
 E void FDECL(djinni_from_bottle, (struct obj *));
 E struct monst *FDECL(split_mon, (struct monst *,struct monst *));
+E const char *NDECL(bottlename);
 
 /* ### pray.c ### */
 
@@ -1507,7 +1554,6 @@
 E short FDECL(quest_info, (int));
 E const char *NDECL(ldrname);
 E boolean FDECL(is_quest_artifact, (struct obj*));
-E boolean NDECL(leaderless);
 E void FDECL(com_pager, (int));
 E void FDECL(qt_pager, (int));
 E struct permonst *NDECL(qt_montype);
@@ -1607,17 +1653,20 @@
 E int FDECL(str2gend, (char *));
 E int FDECL(str2align, (char *));
 E boolean FDECL(ok_role, (int, int, int, int));
-E int FDECL(pick_role, (int, int, int));
+E int FDECL(pick_role, (int, int, int, int));
 E boolean FDECL(ok_race, (int, int, int, int));
-E int FDECL(pick_race, (int, int, int));
+E int FDECL(pick_race, (int, int, int, int));
 E boolean FDECL(ok_gend, (int, int, int, int));
-E int FDECL(pick_gend, (int, int, int));
+E int FDECL(pick_gend, (int, int, int, int));
 E boolean FDECL(ok_align, (int, int, int, int));
-E int FDECL(pick_align, (int, int, int));
+E int FDECL(pick_align, (int, int, int, int));
 E void NDECL(role_init);
+E void NDECL(rigid_role_checks);
 E void NDECL(plnamesuffix);
 E const char *FDECL(Hello, (struct monst *));
 E const char *NDECL(Goodbye);
+E char *FDECL(build_plselection_prompt, (char *, int, int, int, int, int));
+E char *FDECL(root_plselection_prompt, (char *, int, int, int, int, int));
 
 /* ### rumors.c ### */
 
@@ -1663,6 +1712,7 @@
 E void FDECL(restshk, (struct monst *,BOOLEAN_P));
 E char FDECL(inside_shop, (XCHAR_P,XCHAR_P));
 E void FDECL(u_left_shop, (char *,BOOLEAN_P));
+E void FDECL(remote_burglary, (XCHAR_P,XCHAR_P));
 E void FDECL(u_entered_shop, (char *));
 E boolean FDECL(same_price, (struct obj *,struct obj *));
 E void NDECL(shopper_financial_report);
@@ -1679,7 +1729,7 @@
 E boolean FDECL(paybill, (BOOLEAN_P));
 E void NDECL(finish_paybill);
 E struct obj *FDECL(find_oid, (unsigned));
-E long FDECL(contained_cost, (struct obj *,struct monst *,long,BOOLEAN_P));
+E long FDECL(contained_cost, (struct obj *,struct monst *,long,BOOLEAN_P, BOOLEAN_P));
 E long FDECL(contained_gold, (struct obj *));
 E void FDECL(picked_container, (struct obj *));
 E long FDECL(unpaid_cost, (struct obj *));
@@ -1687,10 +1737,10 @@
 E void FDECL(splitbill, (struct obj *,struct obj *));
 E void FDECL(subfrombill, (struct obj *,struct monst *));
 E long FDECL(stolen_value, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P,BOOLEAN_P));
-E void FDECL(sellobj_state, (BOOLEAN_P));
+E void FDECL(sellobj_state, (int));
 E void FDECL(sellobj, (struct obj *,XCHAR_P,XCHAR_P));
 E int FDECL(doinvbill, (int));
-E int FDECL(shkcatch, (struct obj *,XCHAR_P,XCHAR_P));
+E struct monst *FDECL(shkcatch, (struct obj *,XCHAR_P,XCHAR_P));
 E void FDECL(add_damage, (XCHAR_P,XCHAR_P,long));
 E int FDECL(repair_damage, (struct monst *,struct damage *,BOOLEAN_P));
 E int FDECL(shk_move, (struct monst *));
@@ -1731,7 +1781,9 @@
 E void FDECL(whimper, (struct monst *));
 E void FDECL(beg, (struct monst *));
 E int NDECL(dotalk);
-
+#ifdef USER_SOUNDS
+E int FDECL(add_sound_mapping, (const char *));
+#endif
 
 /* ### sys/msdos/sound.c ### */
 
@@ -1755,6 +1807,7 @@
 E int NDECL(learn);
 #endif
 E int FDECL(study_book, (struct obj *));
+E void FDECL(book_disappears, (struct obj *));
 E void FDECL(book_substitution, (struct obj *,struct obj *));
 E void NDECL(age_spells);
 E int NDECL(docast);
@@ -1772,7 +1825,7 @@
 E long NDECL(somegold);
 E void FDECL(stealgold, (struct monst *));
 E void FDECL(remove_worn_item, (struct obj *));
-E int FDECL(steal, (struct monst *));
+E int FDECL(steal, (struct monst *, char *));
 E int FDECL(mpickobj, (struct monst *,struct obj *));
 E void FDECL(stealamulet, (struct monst *));
 E void FDECL(relobj, (struct monst *,int,BOOLEAN_P));
@@ -1863,13 +1916,13 @@
 
 E boolean FDECL(burnarmor,(struct monst *));
 E boolean FDECL(rust_dmg, (struct obj *,const char *,int,BOOLEAN_P,struct monst *));
-E void FDECL(grease_protect, (struct obj *,const char *,BOOLEAN_P,struct monst *));
+E void FDECL(grease_protect, (struct obj *,const char *,struct monst *));
 E struct trap *FDECL(maketrap, (int,int,int));
 E void FDECL(fall_through, (BOOLEAN_P));
 E struct monst *FDECL(animate_statue, (struct obj *,XCHAR_P,XCHAR_P,int,int *));
 E struct monst *FDECL(activate_statue_trap,
 			(struct trap *,XCHAR_P,XCHAR_P,BOOLEAN_P));
-E void FDECL(dotrap, (struct trap *));
+E void FDECL(dotrap, (struct trap *, unsigned));
 E void FDECL(seetrap, (struct trap *));
 E int FDECL(mintrap, (struct monst *));
 E void FDECL(instapetrify, (const char *));
@@ -1879,6 +1932,7 @@
 E void NDECL(float_up);
 E void FDECL(fill_pit, (int,int));
 E int FDECL(float_down, (long, long));
+E int FDECL(fire_damage, (struct obj *,BOOLEAN_P,BOOLEAN_P,XCHAR_P,XCHAR_P));
 E void FDECL(water_damage, (struct obj *,BOOLEAN_P,BOOLEAN_P));
 E boolean NDECL(drown);
 E void FDECL(drain_en, (int));
@@ -1891,6 +1945,7 @@
 E void FDECL(b_trapped, (const char *,int));
 E boolean NDECL(unconscious);
 E boolean NDECL(lava_effects);
+E void FDECL(blow_up_landmine, (struct trap *));
 E int FDECL(launch_obj,(SHORT_P,int,int,int,int,int));
 
 /* ### u_init.c ### */
@@ -1907,6 +1962,7 @@
 E int FDECL(damageum, (struct monst *,struct attack *));
 E void FDECL(missum, (struct monst *,struct attack *));
 E int FDECL(passive, (struct monst *,BOOLEAN_P,int,UCHAR_P));
+E void FDECL(passive_obj, (struct monst *,struct obj *,struct attack *));
 E void FDECL(stumble_onto_mimic, (struct monst *));
 E int FDECL(flash_hits_mon, (struct monst *,struct obj *));
 
@@ -1933,7 +1989,6 @@
 /* ### unixunix.c ### */
 
 #ifdef UNIX
-E void FDECL(gethdate, (const char *));
 E void NDECL(getlock);
 E void FDECL(regularize, (char *));
 # ifdef SHELL
@@ -1944,6 +1999,14 @@
 # endif
 #endif /* UNIX */
 
+/* ### unixres.c ### */
+
+#ifdef UNIX
+# ifdef GNOME_GRAPHICS 
+E int FDECL(hide_privileges, (BOOLEAN_P));
+# endif
+#endif /* UNIX */
+
 /* ### vault.c ### */
 
 E boolean FDECL(grddead, (struct monst *));
@@ -2049,7 +2112,6 @@
 
 /* ### vmsunix.c ### */
 
-E void FDECL(gethdate, (const char *));
 E void NDECL(getlock);
 E void FDECL(regularize, (char *));
 E int NDECL(vms_getuid);
@@ -2116,15 +2178,17 @@
 E void NDECL(uwepgone);
 E void NDECL(uswapwepgone);
 E void NDECL(uqwepgone);
-E void FDECL(erode_weapon, (struct obj *,BOOLEAN_P));
+E void FDECL(erode_obj, (struct obj *,BOOLEAN_P,BOOLEAN_P));
 E int FDECL(chwepon, (struct obj *,int));
 E int FDECL(welded, (struct obj *));
 E void FDECL(weldmsg, (struct obj *));
+E void FDECL(setmnotwielded, (struct monst *,struct obj *));
 
 /* ### windows.c ### */
 
 E void FDECL(choose_windows, (const char *));
 E char FDECL(genl_message_menu, (CHAR_P,int,const char *));
+E void FDECL(genl_preference_update, (const char *));
 
 /* ### wizard.c ### */
 
@@ -2135,7 +2199,7 @@
 E void NDECL(aggravate);
 E void NDECL(clonewiz);
 E int NDECL(pick_nasty);
-E void FDECL(nasty, (struct monst*));
+E int FDECL(nasty, (struct monst*));
 E void NDECL(resurrect);
 E void NDECL(intervene);
 E void NDECL(wizdead);
@@ -2164,12 +2228,14 @@
 E void FDECL(setworn, (struct obj *,long));
 E void FDECL(setnotworn, (struct obj *));
 E void FDECL(mon_set_minvis, (struct monst *));
-E void FDECL(mon_adjust_speed, (struct monst *,int));
+E void FDECL(mon_adjust_speed, (struct monst *,int,struct obj *));
 E void FDECL(update_mon_intrinsics, (struct monst *,struct obj *,BOOLEAN_P));
 E int FDECL(find_mac, (struct monst *));
 E void FDECL(m_dowear, (struct monst *,BOOLEAN_P));
 E struct obj *FDECL(which_armor, (struct monst *,long));
-E void FDECL(mon_break_armor, (struct monst *));
+E void FDECL(mon_break_armor, (struct monst *,BOOLEAN_P));
+E void FDECL(bypass_obj, (struct obj *));
+E void NDECL(clear_bypasses);
 
 /* ### write.c ### */
 
@@ -2181,12 +2247,13 @@
 E void FDECL(probe_monster, (struct monst *));
 E boolean FDECL(get_obj_location, (struct obj *,xchar *,xchar *,int));
 E boolean FDECL(get_mon_location, (struct monst *,xchar *,xchar *,int));
+E struct monst *FDECL(get_container_location, (struct obj *obj, int *, int *));
 E struct monst *FDECL(montraits, (struct obj *,coord *));
 E struct monst *FDECL(revive, (struct obj *));
 E int FDECL(unturn_dead, (struct monst *));
 E void FDECL(cancel_item, (struct obj *));
 E boolean FDECL(drain_item, (struct obj *));
-E void FDECL(poly_obj, (struct obj *, int));
+E struct obj *FDECL(poly_obj, (struct obj *, int));
 E boolean FDECL(obj_resists, (struct obj *,int,int));
 E boolean FDECL(obj_shudders, (struct obj *));
 E void FDECL(do_osshock, (struct obj *));
@@ -2206,7 +2273,7 @@
 E struct monst *FDECL(bhit, (int,int,int,int,int (*)(MONST_P,OBJ_P),
 			     int (*)(OBJ_P,OBJ_P),struct obj *));
 E struct monst *FDECL(boomhit, (int,int));
-E int FDECL(burn_floor_paper, (int,int,BOOLEAN_P));
+E int FDECL(burn_floor_paper, (int,int,BOOLEAN_P,BOOLEAN_P));
 E void FDECL(buzz, (int,int,XCHAR_P,XCHAR_P,int,int));
 E void FDECL(melt_ice, (XCHAR_P,XCHAR_P));
 E int FDECL(zap_over_floor, (XCHAR_P,XCHAR_P,int,boolean *));
diff -Naurd ../nethack-3.3.1/include/flag.h ./include/flag.h
--- ../nethack-3.3.1/include/flag.h Sat Jul 22 01:59:00 2000
+++ ./include/flag.h Fri Mar 22 14:40:55 2002
@@ -22,6 +22,7 @@
 #ifdef	MFLOPPY
 	boolean  asksavedisk;
 #endif
+	boolean  autodig;       /* MRKR: Automatically dig */
 	boolean  autoquiver;	/* Automatically fill quiver */
 	boolean  beginner;
 #ifdef MAIL
@@ -53,6 +54,7 @@
 	boolean  mon_moving;	/* monsters' turn to move */
 	boolean  move;
 	boolean  mv;
+	boolean  bypasses;	/* bypass flag is set on at least one fobj */
 	boolean  nap;		/* `timed_delay' option for display effects */
 	boolean  nopick;	/* do not pickup objects (as when running) */
 	boolean  null;		/* OK to send nulls to the terminal */
@@ -74,6 +76,7 @@
 	boolean  silent;	/* whether the bell rings or not */
 	boolean  sortpack;	/* sorted inventory */
 	boolean  soundok;	/* ok to tell about sounds heard */
+	boolean  sparkle;	/* show "resisting" special FX (Scott Bigham) */
 	boolean  standout;	/* use standout for --More-- */
 	boolean  time;		/* display elapsed 'time' */
 	boolean  tombstone;	/* print tombstone */
@@ -87,15 +90,23 @@
 #define NEW_MOON	0
 #define FULL_MOON	4
 	unsigned no_of_wizards; /* 0, 1 or 2 (wizard and his shadow) */
+	boolean  travel;	/* find way automatically to u.tx,u.ty */
 	unsigned run;		/* 0: h (etc), 1: H (etc), 2: fh (etc) */
 				/* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */
+				/* 8: travel */
 	unsigned long warntype; /* warn_of_mon monster type M2 */
 	int	 warnlevel;
 	int	 djinni_count, ghost_count;	/* potion effect tuning */
 	int	 pickup_burden;		/* maximum burden before prompt */
 	char	 inv_order[MAXOCLASSES];
 	char	 pickup_types[MAXOCLASSES];
-	char	 end_disclose[6];	/* disclose various info upon exit */
+#define NUM_DISCLOSURE_OPTIONS		5
+#define DISCLOSE_PROMPT_DEFAULT_YES	'y'
+#define DISCLOSE_PROMPT_DEFAULT_NO	'n'
+#define DISCLOSE_YES_WITHOUT_PROMPT	'+'
+#define DISCLOSE_NO_WITHOUT_PROMPT	'-'
+	char	 end_disclose[NUM_DISCLOSURE_OPTIONS + 1];  /* disclose various info
+								upon exit */
 	char	 menu_style;	/* User interface style setting */
 #ifdef AMII_GRAPHICS
 	int numcols;
@@ -133,6 +144,7 @@
 	int	 initrace;	/* starting race      (index into races[])   */
 	int	 initgend;	/* starting gender    (index into genders[]) */
 	int	 initalign;	/* starting alignment (index into aligns[])  */
+	int	 randomall;	/* randomly assign everything not specified */
 	int	 pantheon;	/* deity selection for priest character */
 };
 
@@ -146,34 +158,24 @@
 	boolean  cbreak;	/* in cbreak mode, rogue format */
 	boolean  DECgraphics;	/* use DEC VT-xxx extended character set */
 	boolean  echo;		/* 1 to echo characters */
-#ifdef TTY_GRAPHICS
-	boolean  eight_bit_tty;	/* pass eight-bit characters through to tty */
-	boolean  extmenu;	/* extended commands use menu interface */
-#endif
 	boolean  IBMgraphics;	/* use IBM extended character set */
 	unsigned msg_history;	/* hint: # of top lines to save */
 	boolean  num_pad;	/* use numbers for movement commands */
 	boolean  news;		/* print news */
 	boolean  window_inited; /* true if init_nhwindows() completed */
+	boolean  vision_inited; /* true if vision is ready */
+	boolean  menu_tab_sep;	/* Use tabs to separate option menu fields */
 	int      purge_monsters;	/* # of dead monsters still on fmon list */
-
+	int *opt_booldup;	/* for duplication of boolean opts in config file */
+	int *opt_compdup;	/* for duplication of compound opts in config file */
+	uchar bouldersym;		/* symbol for boulder display */
 #ifdef WIZARD
 	boolean  sanity_check;	/* run sanity checks */
+	boolean  mon_polycontrol;	/* debug: control monster polymorphs */
 #endif
-#ifdef TEXTCOLOR
-	boolean  hilite_pet;	/* hilight pets on monochome displays */
-	boolean  use_color;	/* use color graphics */
-#endif
-#ifdef MAC_GRAPHICS_ENV
-	boolean  large_font;	/* draw in larger fonts (say, 12pt instead
-				   of 9pt) */
-	boolean  MACgraphics;	/* use Macintosh extended character set, as
-				   as defined in the special font HackFont */
-	unsigned  use_stone;		/* use the stone ppats */
-#endif
-#ifdef MAC
-	boolean  popup_dialog;	/* put queries in pop up dialogs instead of
-				   in the message window */
+#ifdef TTY_GRAPHICS
+	boolean prevmsg_window;	/* show more old messages at a time */
+	boolean  extmenu;	/* extended commands use menu interface */
 #endif
 #ifdef MFLOPPY
 	boolean  checkspace;	/* check disk space before writing files */
@@ -184,30 +186,93 @@
 	boolean  BIOS;		/* use IBM or ST BIOS calls when appropriate */
 	boolean  rawio;		/* whether can use rawio (IOCTL call) */
 #endif
-#ifdef MSDOS
-	boolean hasvga;		/* has a vga adapter */
-	boolean usevga;		/* use the vga adapter */
-	boolean has8514;
-	boolean use8514;
-	boolean hasvesa;
-	boolean usevesa;
-	boolean grmode;		/* currently in graphics mode */
+#ifdef MAC_GRAPHICS_ENV
+	boolean  MACgraphics;	/* use Macintosh extended character set, as
+				   as defined in the special font HackFont */
+	unsigned  use_stone;		/* use the stone ppats */
 #endif
 #if defined(MSDOS) || defined(WIN32)
 	boolean hassound;	/* has a sound card */
 	boolean usesound;	/* use the sound card */
 	boolean usepcspeaker;	/* use the pc speaker */
-	boolean preload_tiles;	/* preload the tiles into RAM */
 	boolean tile_view;
 	boolean over_view;
 	boolean traditional_view;
 #endif
+#ifdef MSDOS
+	boolean hasvga;		/* has a vga adapter */
+	boolean usevga;		/* use the vga adapter */
+	boolean grmode;		/* currently in graphics mode */
+#endif
 #ifdef LAN_FEATURES
 	boolean lan_mail;	/* mail is initialized */
 	boolean lan_mail_fetched; /* mail is awaiting display */
 #endif
+/*
+ * Window capability support.
+ */
+	boolean wc_color;		/* use color graphics                  */
+	boolean wc_hilite_pet;		/* hilight pets                        */
+	boolean wc_ascii_map;		/* show map using traditional ascii    */
+	boolean wc_tiled_map;		/* show map using tiles                */
+	boolean wc_preload_tiles;	/* preload tiles into memory           */
+	int	wc_tile_width;		/* tile width                          */
+	int	wc_tile_height;		/* tile height                         */
+	char	*wc_tile_file;		/* name of tile file;overrides default */
+	boolean wc_inverse;		/* use inverse video for some things   */
+	int	wc_align_status;	/*  status win at top|bot|right|left   */
+	int	wc_align_message;	/* message win at top|bot|right|left   */
+	int     wc_vary_msgcount;	/* show more old messages at a time    */
+	char    *wc_foregrnd_menu;	/* points to foregrnd color name for menu win   */
+	char    *wc_backgrnd_menu;	/* points to backgrnd color name for menu win   */
+	char    *wc_foregrnd_message;	/* points to foregrnd color name for msg win    */
+	char    *wc_backgrnd_message;	/* points to backgrnd color name for msg win    */
+	char    *wc_foregrnd_status;	/* points to foregrnd color name for status win */
+	char    *wc_backgrnd_status;	/* points to backgrnd color name for status win */
+	char    *wc_foregrnd_text;	/* points to foregrnd color name for text win   */
+	char    *wc_backgrnd_text;	/* points to backgrnd color name for text win   */
+	char    *wc_font_map;		/* points to font name for the map win */
+	char    *wc_font_message;	/* points to font name for message win */
+	char    *wc_font_status;	/* points to font name for status win  */
+	char    *wc_font_menu;		/* points to font name for menu win    */
+	char    *wc_font_text;		/* points to font name for text win    */
+	int     wc_fontsiz_map;		/* font size for the map win           */
+	int     wc_fontsiz_message;	/* font size for the message window    */
+	int     wc_fontsiz_status;	/* font size for the status window     */
+	int     wc_fontsiz_menu;	/* font size for the menu window       */
+	int     wc_fontsiz_text;	/* font size for text windows          */
+	int	wc_scroll_margin;	/* scroll map when this far from
+						the edge */
+	int	wc_map_mode;		/* specify map viewing options, mostly
+						for backward compatibility */
+	int	wc_player_selection;	/* method of choosing character */
+	boolean	wc_splash_screen;	/* display an opening splash screen or not */
+	boolean	wc_popup_dialog;	/* put queries in pop up dialogs instead of
+				   		in the message window */
+	boolean wc_large_font;		/* draw in larger fonts (say, 12pt instead
+				   		of 9pt) */
+	boolean wc_eight_bit_input;	/* allow eight bit input               */
 };
 
+/*
+ * Old deprecated names
+ */
+#ifdef TTY_GRAPHICS
+#define eight_bit_tty wc_eight_bit_input
+#endif
+#ifdef TEXTCOLOR
+#define use_color wc_color
+#endif
+#define hilite_pet wc_hilite_pet
+#define use_inverse wc_inverse
+#ifdef MAC_GRAPHICS_ENV
+#define large_font wc_large_font
+#endif
+#ifdef MAC
+#define popup_dialog wc_popup_dialog
+#endif
+#define preload_tiles wc_preload_tiles
+
 extern NEARDATA struct flag flags;
 extern NEARDATA struct instance_flags iflags;
 
diff -Naurd ../nethack-3.3.1/include/global.h ./include/global.h
--- ../nethack-3.3.1/include/global.h Sat Jul 15 19:04:04 2000
+++ ./include/global.h Fri Mar 22 14:40:55 2002
@@ -8,7 +8,7 @@
 #include <stdio.h>
 
 
-/*#define BETA	*/	/* if a beta-test copy	[MRS] */
+/* #define BETA	*/	/* if a beta-test copy	[MRS] */
 
 /*
  * Files expected to exist in the playground directory.
diff -Naurd ../nethack-3.3.1/include/hack.h ./include/hack.h
--- ../nethack-3.3.1/include/hack.h Sat Jul 22 01:59:00 2000
+++ ./include/hack.h Fri Mar 22 14:40:55 2002
@@ -43,9 +43,22 @@
 #define DISMOUNT_THROWN		2
 #define DISMOUNT_POLY		3
 #define DISMOUNT_ENGULFED	4
-#define DISMOUNT_BYCHOICE	5
+#define DISMOUNT_BONES		5
+#define DISMOUNT_BYCHOICE	6
 #endif
 
+/* Special returns from mapglyph() */
+#define MG_CORPSE	0x01
+#define MG_INVIS	0x02
+#define MG_DETECT	0x04
+#define MG_PET		0x08
+#define MG_RIDDEN	0x10
+
+/* sellobj_state() states */
+#define SELL_NORMAL	(0)
+#define SELL_DELIBERATE	(1)
+#define SELL_DONTSELL	(2)
+
 /*
  * This is the way the game ends.  If these are rearranged, the arrays
  * in end.c and topten.c will need to be changed.  Some parts of the
@@ -125,6 +138,10 @@
 #define MM_EMIN		  0x08	/* add emin structure */
 #define MM_ANGRY	  0x10  /* monster is created angry */
 #define MM_NONAME	  0x20  /* monster is not christened */
+#define MM_NOCOUNTBIRTH	  0x40  /* don't increment born counter (for revival) */
+
+/* flags for special ggetobj status returns */
+#define ALL_FINISHED	  0x01  /* called routine already finished the job */
 
 /* flags to control query_objlist() */
 #define BY_NEXTHERE	  0x1	/* follow objlist by nexthere field */
@@ -141,6 +158,11 @@
 #define ALL_TYPES    0x10
 #define BILLED_TYPES 0x20
 #define CHOOSE_ALL   0x40
+#define BUC_BLESSED  0x80
+#define BUC_CURSED   0x100
+#define BUC_UNCURSED 0x200
+#define BUC_UNKNOWN  0x400
+#define BUC_ALLBKNOWN (BUC_BLESSED|BUC_CURSED|BUC_UNCURSED)
 #define ALL_TYPES_SELECTED -2
 
 /* Flags to control find_mid() */
@@ -149,6 +171,13 @@
 #define FM_MYDOGS      0x04	/* search mydogs */
 #define FM_EVERYWHERE  (FM_FMON | FM_MIGRATE | FM_MYDOGS)
 
+/* Flags to control pick_[race,role,gend,align] routines in role.c */
+#define PICK_RANDOM	0
+#define PICK_RIGID	1
+
+/* Flags to control dotrap() in trap.c */
+#define NOWEBMSG	0x01	/* suppress stumble into web message */
+
 /*** some utility macros ***/
 #define yn(query) yn_function(query,ynchars, 'n')
 #define ynq(query) yn_function(query,ynqchars, 'q')
@@ -169,6 +198,16 @@
 #define ROLL	1
 #define FLING	2
 
+/* Macros for explosion types */
+#define EXPL_DARK	0
+#define EXPL_NOXIOUS	1
+#define EXPL_MUDDY	2
+#define EXPL_WET	3
+#define EXPL_MAGICAL	4
+#define EXPL_FIERY	5
+#define EXPL_FROSTY	6
+#define EXPL_MAX	7
+
 /* Macros for messages referring to hands, eyes, feet, etc... */
 #define ARM 0
 #define EYE 1
@@ -187,6 +226,8 @@
 #define HAIR 14
 #define BLOOD 15
 #define LUNG 16
+#define NOSE 17
+#define STOMACH 18
 
 /* Flags to control menus */
 #define MENUTYPELEN sizeof("traditional ")
@@ -198,6 +239,16 @@
 #define MENU_SELECTED	TRUE
 #define MENU_UNSELECTED FALSE
 
+/*
+ * Option flags
+ * Each higher number includes the characteristics of the numbers
+ * below it.
+ */
+#define SET_IN_FILE	0 /* config file option only */
+#define SET_VIA_PROG	1 /* may be set via extern program, not seen in game */
+#define DISP_IN_GAME	2 /* may be set via extern program, displayed in game */
+#define SET_IN_GAME	3 /* may be set via extern program or set in the game */
+
 #define FEATURE_NOTICE_VER(major,minor,patch) (((unsigned long)major << 24) | \
 	((unsigned long)minor << 16) | \
 	((unsigned long)patch << 8) | \
diff -Naurd ../nethack-3.3.1/include/lev.h ./include/lev.h
--- ../nethack-3.3.1/include/lev.h Wed Jul 7 05:16:32 1999
+++ ./include/lev.h Fri Mar 22 14:40:55 2002
@@ -15,4 +15,35 @@
 #define perform_bwrite(mode)	((mode) & (COUNT_SAVE|WRITE_SAVE))
 #define release_data(mode)	((mode) & FREE_SAVE)
 
+/* The following are used in mkmaze.c */
+struct container {
+	struct container *next;
+	xchar x, y;
+	short what;
+	genericptr_t list;
+};
+
+#define CONS_OBJ   0
+#define CONS_MON   1
+#define CONS_HERO  2
+#define CONS_TRAP  3
+
+struct bubble {
+	xchar x, y;	/* coordinates of the upper left corner */
+	schar dx, dy;	/* the general direction of the bubble's movement */
+	uchar *bm;	/* pointer to the bubble bit mask */
+	struct bubble *prev, *next; /* need to traverse the list up and down */
+	struct container *cons;
+};
+
+/* used in light.c */
+typedef struct ls_t {
+    struct ls_t *next;
+    xchar x, y;		/* source's position */
+    short range;	/* source's current range */
+    short flags;
+    short type;		/* type of light source */
+    genericptr_t id;	/* source's identifier */
+} light_source;
+
 #endif /* LEV_H */
diff -Naurd ../nethack-3.3.1/include/monattk.h ./include/monattk.h
--- ../nethack-3.3.1/include/monattk.h Mon Dec 27 16:58:37 1999
+++ ./include/monattk.h Fri Mar 22 14:40:55 2002
@@ -75,7 +75,7 @@
 #define AD_FAMN		39	/* for Famine only */
 #define AD_SLIM		40	/* turns you into green slime */
 #define AD_ENCH		41	/* remove enchantment (disenchanter) */
-#define AD_CORRODE	42	/* corrode armor (black pudding) */
+#define AD_CORR		42	/* corrode armor (black pudding) */
 
 #define AD_CLRC		240	/* random clerical spell */
 #define AD_SPEL		241	/* random magic spell */
diff -Naurd ../nethack-3.3.1/include/mondata.h ./include/mondata.h
--- ../nethack-3.3.1/include/mondata.h Mon Jul 17 00:35:11 2000
+++ ./include/mondata.h Fri Mar 22 14:40:55 2002
@@ -33,12 +33,16 @@
 #define hides_under(ptr)	(((ptr)->mflags1 & M1_CONCEAL) != 0L)
 #define is_hider(ptr)		(((ptr)->mflags1 & M1_HIDE) != 0L)
 #define haseyes(ptr)		(((ptr)->mflags1 & M1_NOEYES) == 0L)
+#define eyecount(ptr)		(!haseyes(ptr) ? 0 : \
+				 ((ptr) == &mons[PM_CYCLOPS] || \
+				  (ptr) == &mons[PM_FLOATING_EYE]) ? 1 : 2)
 #define nohands(ptr)		(((ptr)->mflags1 & M1_NOHANDS) != 0L)
 #define nolimbs(ptr)		(((ptr)->mflags1 & M1_NOLIMBS) == M1_NOLIMBS)
 #define notake(ptr)		(((ptr)->mflags1 & M1_NOTAKE) != 0L)
 #define has_head(ptr)		(((ptr)->mflags1 & M1_NOHEAD) == 0L)
 #define is_whirly(ptr)		((ptr)->mlet == S_VORTEX || \
 				 (ptr) == &mons[PM_AIR_ELEMENTAL])
+#define is_silent(ptr)		((ptr)->msound == MS_SILENT)
 #define unsolid(ptr)		(((ptr)->mflags1 & M1_UNSOLID) != 0L)
 #define mindless(ptr)		(((ptr)->mflags1 & M1_MINDLESS) != 0L)
 #define humanoid(ptr)		(((ptr)->mflags1 & M1_HUMANOID) != 0L)
@@ -90,6 +94,7 @@
 #define strongmonst(ptr)	(((ptr)->mflags2 & M2_STRONG) != 0L)
 #define can_breathe(ptr)	attacktype(ptr, AT_BREA)
 #define cantwield(ptr)		(nohands(ptr) || verysmall(ptr))
+#define could_twoweap(ptr)	((ptr)->mattk[1].aatyp == AT_WEAP)
 #define cantweararm(ptr)	(breakarm(ptr) || sliparm(ptr))
 #define throws_rocks(ptr)	(((ptr)->mflags2 & M2_ROCKTHROW) != 0L)
 #define type_is_pname(ptr)	(((ptr)->mflags2 & M2_PNAME) != 0L)
@@ -125,6 +130,9 @@
 				 (ptr) == &mons[PM_GIANT] || \
 				 (ptr) == &mons[PM_ELF] || \
 				 (ptr) == &mons[PM_HUMAN])
+/* return TRUE if the monster tends to revive */
+#define is_reviver(ptr)		(is_rider(ptr) || (ptr)->mlet == S_TROLL)
+
 /* this returns the light's range, or 0 if none; if we add more light emitting
    monsters, we'll likely have to add a new light range field to mons[] */
 #define emits_light(ptr)	(((ptr)->mlet == S_LIGHT || \
@@ -149,7 +157,8 @@
 #define is_mind_flayer(ptr)	((ptr) == &mons[PM_MIND_FLAYER] || \
 				 (ptr) == &mons[PM_MASTER_MIND_FLAYER])
 
-#define nonliving(ptr)		(is_golem(ptr) || is_undead(ptr))
+#define nonliving(ptr)		(is_golem(ptr) || is_undead(ptr) || \
+				 (ptr)->mlet == S_VORTEX)
 
 /* Used for conduct with corpses, tins, and digestion attacks */
 /* G_NOCORPSE monsters might still be swallowed as a purple worm */
@@ -169,4 +178,7 @@
 				((ptr)->mlet == S_PUDDING &&         \
 				 (ptr) != &mons[PM_BLACK_PUDDING]))
 
+#define befriend_with_obj(ptr, obj) ((obj)->oclass == FOOD_CLASS && \
+				     is_domestic(ptr))
+
 #endif /* MONDATA_H */
diff -Naurd ../nethack-3.3.1/include/monst.h ./include/monst.h
--- ../nethack-3.3.1/include/monst.h Sun May 28 16:08:40 2000
+++ ./include/monst.h Fri Mar 22 14:40:57 2002
@@ -83,7 +83,7 @@
 	Bitfield(mspeed,2);	/* current speed */
 	Bitfield(permspeed,2);	/* intrinsic mspeed value */
 	Bitfield(mrevived,1);	/* has been revived from the dead */
-	Bitfield(not_used,1);	/*** available ***/
+	Bitfield(mavenge,1);	/* did something to deserve retaliation */
 
 	Bitfield(mflee,1);	/* fleeing */
 	Bitfield(mfleetim,7);	/* timeout for mflee */
diff -Naurd ../nethack-3.3.1/include/ntconf.h ./include/ntconf.h
--- ../nethack-3.3.1/include/ntconf.h Mon Apr 24 02:49:17 2000
+++ ./include/ntconf.h Fri Mar 22 14:40:55 2002
@@ -8,15 +8,12 @@
 /* #define SHELL	/* nt use of pcsys routines caused a hang */
 
 #define RANDOM		/* have Berkeley random(3) */
-
 #define TEXTCOLOR	/* Color text */
 
-#define PATHLEN		64	/* maximum pathlength */
-#define FILENAME	80	/* maximum filename length (conservative) */
 #define EXEPATH			/* Allow .exe location to be used as HACKDIR */
 #define TRADITIONAL_GLYPHMAP	/* Store glyph mappings at level change time */
 #ifdef WIN32CON
-#define LAN_FEATURES		/* Include code for lan-aware features. */
+#define LAN_FEATURES		/* Include code for lan-aware features. Untested in 3.4.0*/
 #endif
 
 #define PC_LOCKING		/* Prevent overwrites of aborted or in-progress games */
@@ -36,6 +33,18 @@
 #define NO_TERMS
 #define ASCIIGRAPH
 
+#ifdef OPTIONS_USED
+#undef OPTIONS_USED
+#endif
+#ifdef MSWIN_GRAPHICS
+#define OPTIONS_USED	"guioptions"
+#else
+#define OPTIONS_USED	"ttyoptions"
+#endif
+#define OPTIONS_FILE OPTIONS_USED
+
+#define PORT_HELP	"porthelp"
+
 /* The following is needed for prototypes of certain functions */
 #if defined(_MSC_VER)
 #include <process.h>	/* Provides prototypes of exit(), spawn()      */
@@ -48,6 +57,23 @@
 
 #include <sys/types.h>
 #include <stdlib.h>
+#ifdef __BORLANDC__
+#undef randomize
+#undef random
+#endif
+
+#define PATHLEN		BUFSZ /* maximum pathlength */
+#define FILENAME	BUFSZ /* maximum filename length (conservative) */
+
+#if defined(_MAX_PATH) && defined(_MAX_FNAME)
+# if (_MAX_PATH < BUFSZ) && (_MAX_FNAME < BUFSZ)
+#undef PATHLEN
+#undef FILENAME
+#define PATHLEN		_MAX_PATH
+#define FILENAME	_MAX_FNAME
+# endif
+#endif
+
 
 #define NO_SIGNAL
 #define index	strchr
@@ -85,8 +111,20 @@
 #endif
 
 #include <fcntl.h>
+#ifndef __BORLANDC__
 #include <io.h>
 #include <direct.h>
+#else
+int  _RTLENTRY _EXPFUNC _chdrive(int __drive);
+int  _RTLENTRYF _EXPFUNC32   chdir( const char _FAR *__path );
+char _FAR * _RTLENTRY  _EXPFUNC     getcwd( char _FAR *__buf, int __buflen );
+int  _RTLENTRY _EXPFUNC write (int __handle, const void _FAR *__buf, unsigned __len);
+int  _RTLENTRY _EXPFUNC creat   (const char _FAR *__path, int __amode);
+int  _RTLENTRY _EXPFUNC close   (int __handle);
+int  _RTLENTRY _EXPFUNC open  (const char _FAR *__path, int __access,... /*unsigned mode*/);
+long _RTLENTRY _EXPFUNC lseek  (int __handle, long __offset, int __fromwhere);
+int  _RTLENTRY _EXPFUNC read  (int __handle, void _FAR *__buf, unsigned __len);
+#endif
 #include <conio.h>
 #undef kbhit		/* Use our special NT kbhit */
 #define kbhit (*nt_kbhit)
@@ -118,4 +156,6 @@
 #endif
 #endif
 
+extern int FDECL(set_win32_option, (const char *, const char *));
+
 #endif /* NTCONF_H */
diff -Naurd ../nethack-3.3.1/include/obj.h ./include/obj.h
--- ../nethack-3.3.1/include/obj.h Fri Feb 4 21:59:47 2000
+++ ./include/obj.h Fri Mar 22 14:40:55 2002
@@ -65,6 +65,7 @@
 #define MAX_ERODE 3
 #define orotten oeroded		/* rotten food */
 #define odiluted oeroded	/* diluted potions */
+#define norevive oeroded2
 	Bitfield(oerodeproof,1); /* erodeproof weapon/armor */
 	Bitfield(olocked,1);	/* object is locked */
 	Bitfield(obroken,1);	/* lock has been broken */
@@ -84,7 +85,8 @@
 #define OATTACHED_UNUSED3 3
 
 	Bitfield(in_use,1);	/* for magic items before useup items */
-	/* 7 free bits */
+	Bitfield(bypass,1);	/* mark this as an object to be skipped by bhito() */
+	/* 6 free bits */
 
 	int	corpsenm;	/* type of corpse is mons[corpsenm] */
 #define leashmon  corpsenm	/* gets m_id of attached pet */
@@ -197,11 +199,41 @@
 #define Is_mbag(otmp)	(otmp->otyp == BAG_OF_HOLDING || \
 			 otmp->otyp == BAG_OF_TRICKS)
 
+/* dragon gear */
+#define Is_dragon_scales(obj)	((obj)->otyp >= GRAY_DRAGON_SCALES && \
+				 (obj)->otyp <= YELLOW_DRAGON_SCALES)
+#define Is_dragon_mail(obj)	((obj)->otyp >= GRAY_DRAGON_SCALE_MAIL && \
+				 (obj)->otyp <= YELLOW_DRAGON_SCALE_MAIL)
+#define Is_dragon_armor(obj)	(Is_dragon_scales(obj) || Is_dragon_mail(obj))
+#define Dragon_scales_to_pm(obj) &mons[PM_GRAY_DRAGON + (obj)->otyp \
+				       - GRAY_DRAGON_SCALES]
+#define Dragon_mail_to_pm(obj)	&mons[PM_GRAY_DRAGON + (obj)->otyp \
+				      - GRAY_DRAGON_SCALE_MAIL]
+#define Dragon_to_scales(pm)	(GRAY_DRAGON_SCALES + (pm - mons))
+
 /* Light sources */
 #define Is_candle(otmp) (otmp->otyp == TALLOW_CANDLE || \
 			 otmp->otyp == WAX_CANDLE)
 #define MAX_OIL_IN_FLASK 400	/* maximum amount of oil in a potion of oil */
 
+/* special stones */
+#define is_graystone(obj)	((obj)->otyp == LUCKSTONE || \
+				 (obj)->otyp == LOADSTONE || \
+				 (obj)->otyp == FLINT     || \
+				 (obj)->otyp == TOUCHSTONE)
+
+/* misc */
+#ifdef KOPS
+#define is_flimsy(otmp)		(objects[(otmp)->otyp].oc_material <= LEATHER || \
+				 (otmp)->otyp == RUBBER_HOSE)
+#else
+#define is_flimsy(otmp)		(objects[(otmp)->otyp].oc_material <= LEATHER)
+#endif
+
+/* helpers, simple enough to be macros */
+#define is_plural(o)	((o)->quan > 1 || \
+			 (o)->oartifact == ART_EYES_OF_THE_OVERWORLD)
+
 /* Flags for get_obj_location(). */
 #define CONTAINED_TOO	0x1
 #define BURIED_TOO	0x2
diff -Naurd ../nethack-3.3.1/include/objclass.h ./include/objclass.h
--- ../nethack-3.3.1/include/objclass.h Mon Dec 6 21:22:37 1999
+++ ./include/objclass.h Fri Mar 22 14:40:55 2002
@@ -68,14 +68,14 @@
 				 objects[otmp->otyp].oc_material <= MITHRIL)
 
 /* primary damage: fire/rust/--- */
-/* is_flammable() in mkobj.c */
+/* is_flammable(otmp), is_rottable(otmp) in mkobj.c */
 #define is_rustprone(otmp)	(objects[otmp->otyp].oc_material == IRON)
 
 /* secondary damage: rot/acid/acid */
-#define is_rottable(otmp) is_flammable(otmp) /* we might want to change this */
 #define is_corrodeable(otmp)	(objects[otmp->otyp].oc_material == COPPER || objects[otmp->otyp].oc_material == IRON)
 
-#define is_damageable(otmp) (is_rustprone(otmp) || is_flammable(otmp) || is_corrodeable(otmp))
+#define is_damageable(otmp) (is_rustprone(otmp) || is_flammable(otmp) || \
+				is_rottable || is_corrodeable(otmp))
 
 	schar	oc_subtyp;
 #define oc_skill	oc_subtyp   /* Skills of weapons, spellbooks, tools, gems */
@@ -91,9 +91,8 @@
 	uchar	oc_oprop;		/* property (invis, &c.) conveyed */
 	char	oc_class;		/* object class */
 	schar	oc_delay;		/* delay when using such an object */
-#ifdef TEXTCOLOR
-	uchar	oc_color;		/* display color of the object */
-#endif /* TEXTCOLOR */
+	uchar	oc_color;		/* color of the object */
+
 	short	oc_prob;		/* probability, used in mkobj() */
 	unsigned short	oc_weight;	/* encumbrance (1 cn = 0.1 lb.) */
 	short	oc_cost;		/* base cost in shops */
diff -Naurd ../nethack-3.3.1/include/os2conf.h ./include/os2conf.h
--- ../nethack-3.3.1/include/os2conf.h Thu Oct 28 02:53:17 1999
+++ ./include/os2conf.h Fri Mar 22 14:40:55 2002
@@ -13,7 +13,7 @@
  */
 
 /* #define OS2_MSC		/* Microsoft C 5.1 and 6.0 */
-/* #define OS2_GCC		/* GCC emx 0.8f */
+#define OS2_GCC		/* GCC emx 0.8f */
 /* #define OS2_CSET2		/* IBM C Set/2 (courtesy Jeff Urlwin) */
 /* #define OS2_CSET2_VER_1	/* CSet/2 version selection */
 /* #define OS2_CSET2_VER_2	/* - " - */
@@ -34,7 +34,7 @@
  * reason to touch the defaults, I think.
  */
 
-#define MFLOPPY			/* floppy and ramdisk support */
+/*#define MFLOPPY			/* floppy and ramdisk support */
 #define RANDOM			/* Berkeley random(3) */
 #define SHELL			/* shell escape */
 /* #define TERMLIB		/* use termcap file */
diff -Naurd ../nethack-3.3.1/include/pcconf.h ./include/pcconf.h
--- ../nethack-3.3.1/include/pcconf.h Sun Nov 14 02:40:46 1999
+++ ./include/pcconf.h Fri Mar 22 14:40:55 2002
@@ -29,7 +29,7 @@
  *  For pre-V7.0 Microsoft Compilers only, manually define OVERLAY here.
  */
 
-/*#define OVERLAY	/* Manual overlay definition (MSC 6.0ax only) */
+/*#define OVERLAY */	/* Manual overlay definition (MSC 6.0ax only) */
 
 # ifndef __GO32__
 #define MFLOPPY		/* Support for floppy drives and ramdisks by dgk */
@@ -51,23 +51,23 @@
  *		  or   NO_TERMS
  */
 
-/* # define TERMLIB	   /* enable use of termcap file /etc/termcap */
+/* # define TERMLIB */	   /* enable use of termcap file /etc/termcap */
 			/* or ./termcap for MSDOS (SAC) */
 			/* compile and link in Fred Fish's termcap library, */
 			/* enclosed in TERMCAP.ARC, to use this */
 
-/* # define ANSI_DEFAULT    /* allows NetHack to run without a ./termcap */
+/* # define ANSI_DEFAULT */   /* allows NetHack to run without a ./termcap */
 
 # define NO_TERMS	/* Allows Nethack to run without ansi.sys by linking */
 			/* screen routines into the .exe     */
 
 # ifdef NO_TERMS	/* if NO_TERMS select one screen package below */
 #define SCREEN_BIOS		/* Use bios calls for all screen control */
-/* #define SCREEN_DJGPPFAST	/* Use djgpp fast screen routines	*/
+/* #define SCREEN_DJGPPFAST */	/* Use djgpp fast screen routines	*/
 # endif
 
 
-/* # define PC9800	/* Allows NetHack to run on NEC PC-9800 machines */
+/* # define PC9800 */	/* Allows NetHack to run on NEC PC-9800 machines */
 			/* Yamamoto Keizo */
 
 
@@ -99,7 +99,7 @@
 			/* amiconf.h).	In the future this will be the */
 			/* hook for mail reader implementation.        */
 
-/*# define PC_LOCKING	/* Allow confirmation before overwriting game  */
+/*# define PC_LOCKING */	/* Allow confirmation before overwriting game  */
 			/* that is in progress or aborted when another */
 			/* game is started with the same player name.  */
 
@@ -139,6 +139,10 @@
 # ifdef PCMUSIC
 #define TIMED_DELAY	/* need it anyway */
 # endif
+#define NOCWD_ASSUMPTIONS	/* Allow paths to be specified for HACKDIR,
+				   LEVELDIR, SAVEDIR, BONESDIR, DATADIR,
+				   SCOREDIR, LOCKDIR, and CONFIGDIR */
+
 #endif /* MSDOS configuration stuff */
 
 #define PATHLEN		64	/* maximum pathlength */
@@ -147,6 +151,7 @@
 #include "micro.h"		/* contains necessary externs for [os_name].c */
 #endif
 
+
 /* ===================================================
  *  The remaining code shouldn't need modification.
  */
diff -Naurd ../nethack-3.3.1/include/qtext.h ./include/qtext.h
--- ../nethack-3.3.1/include/qtext.h Wed Jul 7 05:16:34 1999
+++ ./include/qtext.h Fri Mar 22 14:40:55 2002
@@ -59,20 +59,21 @@
 #define QT_NEXTTIME	 2
 #define QT_OTHERTIME	 3
 
-#define QT_GUARDTALK	 5	/* 5 random things for guards to say */
+#define QT_GUARDTALK	 5	/* 5 random things guards say before quest */
+#define QT_GUARDTALK2	10	/* 5 random things guards say after quest */
 
-#define QT_FIRSTLEADER	10
-#define QT_NEXTLEADER	11
-#define QT_OTHERLEADER	12
-#define QT_LASTLEADER	13
-#define QT_BADLEVEL	14
-#define QT_BADALIGN	15
-#define QT_ASSIGNQUEST	16
+#define QT_FIRSTLEADER	15
+#define QT_NEXTLEADER	16
+#define QT_OTHERLEADER	17
+#define QT_LASTLEADER	18
+#define QT_BADLEVEL	19
+#define QT_BADALIGN	20
+#define QT_ASSIGNQUEST	21
 
-#define QT_ENCOURAGE	20	/* 1-10 random encouragement messages */
+#define QT_ENCOURAGE	25	/* 1-10 random encouragement messages */
 
-#define QT_FIRSTLOCATE	30
-#define QT_NEXTLOCATE	31
+#define QT_FIRSTLOCATE	35
+#define QT_NEXTLOCATE	36
 
 #define QT_FIRSTGOAL	40
 #define QT_NEXTGOAL	41
diff -Naurd ../nethack-3.3.1/include/quest.h ./include/quest.h
--- ../nethack-3.3.1/include/quest.h Wed Jul 7 05:16:34 1999
+++ ./include/quest.h Fri Mar 22 14:40:55 2002
@@ -25,6 +25,11 @@
 	Bitfield(touched_artifact,1);	/* for a special message */
 	Bitfield(offered_artifact,1);	/* offered to leader */
 	Bitfield(got_thanks,1);		/* final message from leader */
+
+	/* keep track of leader presence/absence even if leader is
+	   polymorphed, raised from dead, etc */
+	Bitfield(leader_is_dead,1);
+	unsigned leader_m_id;
 };
 
 #define MAX_QUEST_TRIES  7	/* exceed this and you "fail" */
diff -Naurd ../nethack-3.3.1/include/rect.h ./include/rect.h
--- ../nethack-3.3.1/include/rect.h Wed Jul 7 05:16:34 1999
+++ ./include/rect.h Fri Mar 22 14:40:55 2002
@@ -1,11 +1,11 @@
-/*	SCCS Id: @(#)rect.h	3.3	90/02/22	*/
+/*	SCCS Id: @(#)rect.h	3.4	1990/02/22	*/
 /* Copyright (c) 1990 by Jean-Christophe Collet			  */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #ifndef RECT_H
 #define RECT_H
 
-typedef struct {
+typedef struct nhrect {
 	xchar lx, ly;
 	xchar hx, hy;
 } NhRect;
diff -Naurd ../nethack-3.3.1/include/rm.h ./include/rm.h
--- ../nethack-3.3.1/include/rm.h Sat Jul 22 01:59:01 2000
+++ ./include/rm.h Fri Mar 22 14:40:55 2002
@@ -213,6 +213,7 @@
 #define MAXDCHARS	41	/* maximum of mapped dungeon characters */
 #define MAXTCHARS	22	/* maximum of mapped trap characters */
 #define MAXECHARS	29	/* maximum of mapped effects characters */
+#define MAXEXPCHARS	9	/* number of explosion characters */
 
 struct symdef {
     uchar sym;
@@ -425,6 +426,7 @@
 #define icedpool	flags
 
 #define blessedftn	horizontal  /* a fountain that grants attribs */
+#define disturbed	horizontal  /* a grave that has been disturbed */
 
 struct damage {
 	struct damage *next;
diff -Naurd ../nethack-3.3.1/include/system.h ./include/system.h
--- ../nethack-3.3.1/include/system.h Thu Jan 6 19:20:08 2000
+++ ./include/system.h Fri Mar 22 14:40:55 2002
@@ -1,11 +1,11 @@
-/*	SCCS Id: @(#)system.h	3.3	99/07/02	*/
+/*	SCCS Id: @(#)system.h	3.4	2001/12/07	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #ifndef SYSTEM_H
 #define SYSTEM_H
 
-#ifndef __GO32__  /* djgpp compiler for msdos */
+#if !defined(__cplusplus) && !defined(__GO32__)
 
 #define E extern
 
@@ -44,7 +46,7 @@
 typedef long	off_t;
 #endif
 
-#endif /* __GO32__ */
+#endif /* !__cplusplus && !__GO32__ */
 
 /* You may want to change this to fit your system, as this is almost
  * impossible to get right automatically.
@@ -68,7 +70,7 @@
 # define SIG_RET_TYPE int (*)()
 #endif
 
-#ifndef __GO32__
+#if !defined(__cplusplus) && !defined(__GO32__)
 
 #if defined(BSD) || defined(ULTRIX) || defined(RANDOM)
 # ifdef random
@@ -247,7 +249,10 @@
 E int FDECL(atoi, (const char *));
 E int FDECL(chdir, (const char *));
 E int FDECL(chown, (const char *,unsigned,unsigned));
-# ifndef __DECC_VER	/* suppress for recent DEC C */
+# ifdef __DECC_VER
+E int FDECL(chmod, (const char *,mode_t));
+E mode_t FDECL(umask, (mode_t));
+# else
 E int FDECL(chmod, (const char *,int));
 E int FDECL(umask, (int));
 # endif
@@ -361,7 +366,10 @@
 E pid_t NDECL(getpid);
 E uid_t NDECL(getuid);
 E gid_t NDECL(getgid);
-# else
+#  ifdef VMS
+E pid_t NDECL(getppid);
+#  endif
+# else	/*!POSIX_TYPES*/
 #  ifndef getpid		/* Borland C defines getpid() as a macro */
 E int NDECL(getpid);
 #  endif
@@ -370,14 +378,14 @@
 E unsigned NDECL(getuid);
 E unsigned NDECL(getgid);
 #  endif
-# endif
-# if defined(ULTRIX) && !defined(_UNISTD_H_)
+#  if defined(ULTRIX) && !defined(_UNISTD_H_)
 E unsigned NDECL(getuid);
 E unsigned NDECL(getgid);
 E int FDECL(setgid, (int));
 E int FDECL(setuid, (int));
-# endif
-#endif
+#  endif
+# endif	/*?POSIX_TYPES*/
+#endif	/*?(HPUX && !_POSIX_SOURCE)*/
 
 /* add more architectures as needed */
 #if defined(HPUX)
@@ -538,6 +546,6 @@
 
 #undef E
 
-#endif /* __GO32__ */
+#endif /*  !__cplusplus && !__GO32__ */
 
 #endif /* SYSTEM_H */
diff -Naurd ../nethack-3.3.1/include/tile2x11.h ./include/tile2x11.h
--- ../nethack-3.3.1/include/tile2x11.h Wed Jul 7 05:16:34 1999
+++ ./include/tile2x11.h Fri Mar 22 14:40:55 2002
@@ -13,6 +13,10 @@
     unsigned long tile_width;
     unsigned long tile_height;
     unsigned long ntiles;
+    unsigned long per_row;
 } x11_header;
 
+/* how wide each row in the tile file is, in tiles */
+#define TILES_PER_ROW (40)
+
 #endif	/* TILE2X11_H */
diff -Naurd ../nethack-3.3.1/include/timeout.h ./include/timeout.h
--- ../nethack-3.3.1/include/timeout.h Wed Jul 7 05:16:34 1999
+++ ./include/timeout.h Fri Mar 22 14:40:55 2002
@@ -30,4 +30,15 @@
 #define FIG_TRANSFORM	5
 #define NUM_TIME_FUNCS	6
 
+/* used in timeout.c */
+typedef struct fe {
+    struct fe *next;		/* next item in chain */
+    long timeout;		/* when we time out */
+    unsigned long tid;		/* timer ID */
+    short kind;			/* kind of use */
+    short func_index;		/* what to call when we time out */
+    genericptr_t arg;		/* pointer to timeout argument */
+    Bitfield (needs_fixup,1);	/* does arg need to be patched? */
+} timer_element;
+
 #endif /* TIMEOUT_H */
diff -Naurd ../nethack-3.3.1/include/trap.h ./include/trap.h
--- ../nethack-3.3.1/include/trap.h Wed Jul 7 05:16:35 1999
+++ ./include/trap.h Fri Mar 22 14:40:55 2002
@@ -7,6 +7,11 @@
 #ifndef TRAP_H
 #define TRAP_H
 
+union vlaunchinfo {
+	short v_launch_otyp;	/* type of object to be triggered */
+	coord v_launch2;	/* secondary launch point (for boulders) */
+};
+
 struct trap {
 	struct trap *ntrap;
 	xchar tx,ty;
@@ -22,12 +27,9 @@
 				 when you untrap a monster.  It would be too
 				 easy to make a monster peaceful if you could
 				 set a trap for it and then untrap it. */
-	union {
-	    short v_launch_otyp;	/* type of object to be triggered */
-	    coord v_launch2;	/* secondary launch point (for boulders) */
-	} v;
-#define launch_otyp	v.v_launch_otyp
-#define launch2		v.v_launch2
+	union vlaunchinfo vl;
+#define launch_otyp	vl.v_launch_otyp
+#define launch2		vl.v_launch2
 };
 
 extern struct trap *ftrap;
diff -Naurd ../nethack-3.3.1/include/unixconf.h ./include/unixconf.h
--- ../nethack-3.3.1/include/unixconf.h Mon Jul 17 02:03:51 2000
+++ ./include/unixconf.h Fri Mar 22 14:40:55 2002
@@ -80,7 +80,7 @@
 /* #define RANDOM */		/* if neither random/srandom nor lrand48/srand48
 				   is available from your system */
 
-/* see sys/unix/snd86.shr for more information on these */
+/* see sys/unix/snd86unx.shr for more information on these */
 /* #define UNIX386MUSIC */	/* play real music through speaker on systems
 				   with music driver installed */
 /* #define VPIX_MUSIC */	/* play real music through speaker on systems
@@ -174,7 +174,7 @@
 #define DEF_MAILREADER	"/usr/ucb/Mail"
 #  endif
 #else
-# if defined(SYSV) || defined(DGUX) || defined(HPUX)
+# if (defined(SYSV) || defined(DGUX) || defined(HPUX)) && !defined(LINUX)
 #  if defined(M_XENIX) || defined(__FreeBSD__)
 #define DEF_MAILREADER	"/usr/bin/mail"
 #  else
@@ -320,5 +320,28 @@
 # undef hc
 #endif
 
+#if defined(GNOME_GRAPHICS)
+#if defined(LINUX)
+# include <linux/unistd.h>
+# if defined(__NR_getresuid) && defined(__NR_getresgid)	/* ie., >= v2.1.44 */
+#  define GETRES_SUPPORT
+# endif
+#else
+# if defined(BSD) || defined(SVR4)
+/*
+ * [ALI] We assume that SVR4 means we can safely include syscall.h
+ * (although it's really a BSDism). This is certainly true for Solaris 2.5,
+ * Solaris 7, Solaris 8 and Compaq Tru64 5.1
+ * Later BSD systems will have the getresid system calls.
+ */
+# include <sys/syscall.h>
+# if (defined (SYS_getuid) || defined(SYS_getresuid)) && \
+  (defined(SYS_getgid) || defined(SYS_getresgid))
+#  define GETRES_SUPPORT
+# endif
+# endif	/* BSD || SVR4 */
+#endif /* LINUX */
+#endif	/* GNOME_GRAPHICS */
+
 #endif /* UNIXCONF_H */
 #endif /* UNIX */
diff -Naurd ../nethack-3.3.1/include/vmsconf.h ./include/vmsconf.h
--- ../nethack-3.3.1/include/vmsconf.h Mon Nov 29 21:25:05 1999
+++ ./include/vmsconf.h Fri Mar 22 14:40:55 2002
@@ -190,6 +190,10 @@
 # define __GID_T
 typedef __gid_t gid_t;
 # endif
+# ifndef __MODE_T
+# define __MODE_T
+typedef __mode_t mode_t;
+# endif
 #endif	/* _DECC_V4_SOURCE */
 
 #include <time.h>
diff -Naurd ../nethack-3.3.1/include/winX.h ./include/winX.h
--- ../nethack-3.3.1/include/winX.h Wed Dec 22 21:58:10 1999
+++ ./include/winX.h Fri Mar 22 14:40:55 2002
@@ -56,6 +56,8 @@
     unsigned short glyphs[ROWNO][COLNO];	/* Saved glyph numbers. */
     GC	white_gc;
     GC	black_gc;
+    unsigned long image_width;			/* dimensions of tile image */
+    unsigned long image_height;
 };
 
 struct map_info_t {
@@ -277,6 +279,7 @@
 /* ### winX.c ### */
 E struct xwindow *FDECL(find_widget,(Widget));
 E Boolean FDECL(nhApproxColor,(Screen*, Colormap, char*, XColor*));
+E Dimension FDECL(nhFontHeight,(Widget));
 E char FDECL(key_event_to_char,(XKeyEvent*));
 E void FDECL(msgkey, (Widget, XtPointer, XEvent*));
 E void FDECL(nh_XtPopup, (Widget, int, Widget));
diff -Naurd ../nethack-3.3.1/include/winprocs.h ./include/winprocs.h
--- ../nethack-3.3.1/include/winprocs.h Thu Oct 21 23:18:07 1999
+++ ./include/winprocs.h Fri Mar 22 14:40:55 2002
@@ -7,6 +7,7 @@
 
 struct window_procs {
     const char *name;
+    unsigned long wincap;	/* window port capability options supported */
     void FDECL((*win_init_nhwindows), (int *, char **));
     void NDECL((*win_player_selection));
     void NDECL((*win_askname));
@@ -62,6 +63,7 @@
     void NDECL((*win_end_screen));
 
     void FDECL((*win_outrip), (winid,int));
+    void FDECL((*win_preference_update), (const char *));
 };
 
 extern NEARDATA struct window_procs windowprocs;
@@ -125,4 +127,82 @@
 #define end_screen (*windowprocs.win_end_screen)
 
 #define outrip (*windowprocs.win_outrip)
+#define preference_update (*windowprocs.win_preference_update)
+
+/*
+ * WINCAP
+ * Window port preference capability bits.
+ * Some day this might be better in its own wincap.h file.
+ */
+#define WC_COLOR	 0x01L		/* 01 Port can display things in color       */
+#define WC_HILITE_PET	 0x02L		/* 02 supports hilite pet                    */
+#define WC_ASCII_MAP	 0x04L		/* 03 supports an ascii map                  */
+#define WC_TILED_MAP	 0x08L		/* 04 supports a tiled map                   */
+#define WC_PRELOAD_TILES 0x10L		/* 05 supports pre-loading tiles             */
+#define WC_TILE_WIDTH	 0x20L		/* 06 prefer this width of tile              */
+#define WC_TILE_HEIGHT	 0x40L		/* 07 prefer this height of tile             */
+#define WC_TILE_FILE	 0x80L		/* 08 alternative tile file name             */
+#define WC_INVERSE	 0x100L		/* 09 Port supports inverse video            */
+#define WC_ALIGN_MESSAGE 0x200L		/* 10 supports message alignmt top|b|l|r     */
+#define WC_ALIGN_STATUS	 0x400L		/* 11 supports status alignmt top|b|l|r      */
+#define WC_VARY_MSGCOUNT 0x800L		/* 12 supports varying message window        */
+#define WC_FONT_MAP	 0x1000L	/* 13 supports specification of map win font */
+#define WC_FONT_MESSAGE	 0x2000L	/* 14 supports specification of msg win font */
+#define WC_FONT_STATUS	 0x4000L	/* 15 supports specification of sts win font */
+#define WC_FONT_MENU	 0x8000L	/* 16 supports specification of mnu win font */
+#define WC_FONT_TEXT	 0x10000L	/* 17 supports specification of txt win font */
+#define WC_FONTSIZ_MAP	 0x20000L	/* 18 supports specification of map win font */
+#define WC_FONTSIZ_MESSAGE 0x40000L	/* 19 supports specification of msg win font */
+#define WC_FONTSIZ_STATUS 0x80000L	/* 20 supports specification of sts win font */
+#define WC_FONTSIZ_MENU	 0x100000L	/* 21 supports specification of mnu win font */
+#define WC_FONTSIZ_TEXT	 0x200000L	/* 22 supports specification of txt win font */
+#define WC_SCROLL_MARGIN 0x400000L	/* 23 supports setting scroll margin for map */
+#define WC_SPLASH_SCREEN 0x800000L	/* 24 supports setting scroll margin for map */
+#define WC_POPUP_DIALOG	 0x1000000L	/* 25 supports queries in pop dialogs        */
+#define WC_LARGE_FONT	 0x2000000L	/* 26 Port supports large font               */
+#define WC_EIGHT_BIT_IN	 0x4000000L	/* 27 8-bit character input                  */
+#define WC_PERM_INVENT	 0x8000000L	/* 28 8-bit character input                  */
+#define WC_MAP_MODE	 0x10000000L	/* 29 map_mode option                        */
+#define WC_WINDOWCOLORS  0x20000000L	/* 30 background color for message window    */
+#define WC_PLAYER_SELECTION  0x40000000L /* 31 background color for message window    */
+					/* 1 free bit */
+
+#define ALIGN_LEFT	1
+#define ALIGN_RIGHT	2
+#define ALIGN_TOP	3
+#define ALIGN_BOTTOM	4
+
+/* player_selection */
+#define VIA_DIALOG	0
+#define VIA_PROMPTS	1
+
+/* map_mode settings - deprecated */
+#define MAP_MODE_TILES		0
+#define MAP_MODE_ASCII4x6	1
+#define MAP_MODE_ASCII6x8	2
+#define MAP_MODE_ASCII8x8	3
+#define MAP_MODE_ASCII16x8	4
+#define MAP_MODE_ASCII7x12	5
+#define MAP_MODE_ASCII8x12	6
+#define MAP_MODE_ASCII16x12	7
+#define MAP_MODE_ASCII12x16	8
+#define MAP_MODE_ASCII10x18	9
+#define MAP_MODE_ASCII_FIT_TO_SCREEN 10
+#define MAP_MODE_TILES_FIT_TO_SCREEN 11
+
+#if 0
+#define WC_SND_SOUND	 0x01L		/* 01 Port has some sound capabilities       */
+#define WC_SND_SPEAKER	 0x02L		/* 02 Sound supported via built-in speaker   */
+#define WC_SND_STEREO	 0x04L		/* 03 Stereo sound supported                 */
+#define WC_SND_RAW	 0x08L		/* 04 Raw sound supported                    */
+#define WC_SND_WAVE	 0x10L		/* 05 Wave support                           */
+#define WC_SND_MIDI	 0x20L		/* 06 Midi support                           */
+					/* 26 free bits */
 #endif
+
+struct wc_Opt {
+	char *wc_name;
+	unsigned long wc_bit;
+};
+
+#endif /* WINPROCS_H */
diff -Naurd ../nethack-3.3.1/include/wintty.h ./include/wintty.h
--- ../nethack-3.3.1/include/wintty.h Thu Oct 21 23:18:08 1999
+++ ./include/wintty.h Fri Mar 22 14:40:55 2002
@@ -111,7 +111,7 @@
 #endif
 E void FDECL(xputs, (const char *));
 #if defined(SCREEN_VGA) || defined(SCREEN_8514)
-E void FDECL(xputg, (int, int));
+E void FDECL(xputg, (int, int, unsigned));
 #endif
 E void NDECL(cl_end);
 E void NDECL(clear_screen);
diff -Naurd ../nethack-3.3.1/include/you.h ./include/you.h
--- ../nethack-3.3.1/include/you.h Sat Jul 15 16:08:29 2000
+++ ./include/you.h Fri Mar 22 14:40:58 2002
@@ -207,6 +207,12 @@
 				/* increment to 3 if you allow neuter roles */
 
 extern const struct Gender genders[];	/* table of available genders */
+#define uhe()	(genders[flags.female ? 1 : 0].he)
+#define uhim()	(genders[flags.female ? 1 : 0].him)
+#define uhis()	(genders[flags.female ? 1 : 0].his)
+#define mhe(mtmp)	(genders[pronoun_gender(mtmp)].he)
+#define mhim(mtmp)	(genders[pronoun_gender(mtmp)].him)
+#define mhis(mtmp)	(genders[pronoun_gender(mtmp)].his)
 
 
 /*** Unified structure specifying alignment information ***/
@@ -227,6 +233,7 @@
 	xchar ux, uy;
 	schar dx, dy, dz;	/* direction of move (or zap or ... ) */
 	schar di;		/* direction of FF */
+	xchar tx, ty;		/* destination of travel */
 	xchar ux0, uy0;		/* initial position FF */
 	d_level uz, uz0;	/* your level on this and the previous turn */
 	d_level utolev;		/* level monster teleported you to, or uz */
@@ -295,7 +302,8 @@
 	Bitfield(mfemale,1);		/* saved human value of flags.female */
 	Bitfield(uinvulnerable,1);	/* you're invulnerable (praying) */
 	Bitfield(uburied,1);		/* you're buried */
-	/* 2 free bits! */
+	Bitfield(uedibility,1);		/* blessed food detection; sense unsafe food */
+	/* 1 free bit! */
 
 	unsigned udg_cnt;		/* how long you have been demigod */
 	struct u_event	uevent;		/* certain events have happened */
diff -Naurd ../nethack-3.3.1/src/allmain.c ./src/allmain.c
--- ../nethack-3.3.1/src/allmain.c Sat Jul 22 01:59:01 2000
+++ ./src/allmain.c Fri Mar 22 14:40:55 2002
@@ -138,6 +138,7 @@
 		    /* once-per-turn things go here */
 		    /********************************/
 
+		    if (flags.bypasses) clear_bypasses();
 		    if(Glib) glibr();
 		    nh_timeout();
 		    run_regions();
@@ -156,16 +157,22 @@
 		    if (u.uinvulnerable) {
 			/* for the moment at least, you're in tiptop shape */
 			wtcap = UNENCUMBERED;
+		    } else if (Upolyd && youmonst.data->mlet == S_EEL && !is_pool(u.ux,u.uy) && !Is_waterlevel(&u.uz)) {
+			if (u.mh > 1) {
+			    u.mh--;
+			    flags.botl = 1;
+			} else if (u.mh < 1)
+			    rehumanize();
 		    } else if (Upolyd && u.mh < u.mhmax) {
 			if (u.mh < 1)
-			   rehumanize();
+			    rehumanize();
 			else if (Regeneration ||
 				    (wtcap < MOD_ENCUMBER && !(moves%20))) {
 			    flags.botl = 1;
 			    u.mh++;
 			}
 		    } else if (u.uhp < u.uhpmax &&
-			 (wtcap < MOD_ENCUMBER || !flags.mv || Regeneration)) {
+			 (wtcap < MOD_ENCUMBER || !u.umoved || Regeneration)) {
 			if (u.ulevel > 9 && !(moves % 3)) {
 			    int heal, Con = (int) ACURR(A_CON);
 
@@ -187,7 +194,8 @@
 			}
 		    }
 
-		    if (wtcap > MOD_ENCUMBER && flags.mv) {
+		    /* moving around while encumbered is hard work */
+		    if (wtcap > MOD_ENCUMBER && u.umoved) {
 			if(!(wtcap < EXT_ENCUMBER ? moves%30 : moves%10)) {
 			    if (Upolyd && u.mh > 1) {
 				u.mh--;
@@ -235,7 +243,7 @@
 				    stop_occupation();
 				else
 				    nomul(0);
-				if (change == 1) polyself();
+				if (change == 1) polyself(FALSE);
 				else you_were();
 				change = 0;
 			    }
@@ -371,11 +379,12 @@
 	    if (!multi) {
 		/* lookaround may clear multi */
 		flags.move = 0;
+		if (flags.time) flags.botl = 1;
 		continue;
 	    }
 	    if (flags.mv) {
 		if(multi < COLNO && !--multi)
-		    flags.mv = flags.run = 0;
+		    flags.travel = flags.mv = flags.run = 0;
 		domove();
 	    } else {
 		--multi;
@@ -406,8 +415,10 @@
 stop_occupation()
 {
 	if(occupation) {
-		You("stop %s.", occtxt);
+		if (!maybe_finished_meal(TRUE))
+		    You("stop %s.", occtxt);
 		occupation = 0;
+		flags.botl = 1; /* in case u.uhs changed */
 /* fainting stops your occupation, there's no reason to sync.
 		sync_hunger();
 */
diff -Naurd ../nethack-3.3.1/src/apply.c ./src/apply.c
--- ../nethack-3.3.1/src/apply.c Thu Aug 3 20:30:51 2000
+++ ./src/apply.c Fri Mar 22 14:40:58 2002
@@ -9,7 +9,7 @@
 
 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, 0 };
+				  WEAPON_CLASS, WAND_CLASS, GEM_CLASS, 0 };
 
 #ifdef TOURIST
 STATIC_DCL int FDECL(use_camera, (struct obj *));
@@ -30,6 +30,7 @@
 STATIC_DCL void FDECL(use_figurine, (struct obj *));
 STATIC_DCL void FDECL(use_grease, (struct obj *));
 STATIC_DCL void FDECL(use_trap, (struct obj *));
+STATIC_DCL void FDECL(use_stone, (struct obj *));
 STATIC_PTR int NDECL(set_trap);		/* occupation callback */
 STATIC_DCL int FDECL(use_whip, (struct obj *));
 STATIC_DCL int FDECL(use_pole, (struct obj *));
@@ -37,6 +38,7 @@
 STATIC_DCL int FDECL(do_break_wand, (struct obj *));
 STATIC_DCL boolean FDECL(figurine_location_checks,
 				(struct obj *, coord *, BOOLEAN_P));
+STATIC_DCL boolean NDECL(uhave_graystone);
 
 #ifdef	AMIGA
 void FDECL( amii_speaker, ( struct obj *, char *, int ) );
@@ -61,12 +63,13 @@
 		pline(nothing_happens);
 		return (1);
 	}
+	check_unpaid(obj);
 	obj->spe--;
 	if (obj->cursed && !rn2(2)) {
 		(void) zapyourself(obj, TRUE);
 	} else if (u.uswallow) {
 		You("take a picture of %s %s.", s_suffix(mon_nam(u.ustuck)),
-		    is_animal(u.ustuck->data) ? "stomach" : "interior");
+		    mbodypart(u.ustuck, STOMACH));
 	} else if (u.dz) {
 		You("take a picture of the %s.",
 			(u.dz > 0) ? surface(u.ux,u.uy) : ceiling(u.ux,u.uy));
@@ -275,6 +278,7 @@
 	case SCORR:
 		You_hear(hollow_str, "passage");
 		lev->typ = CORR;
+		unblock_point(rx,ry);
 		if (Blind) feel_location(rx,ry);
 		else newsym(rx,ry);
 		return res;
@@ -356,11 +360,18 @@
 }
 
 void
-m_unleash(mtmp)		/* mtmp is about to die, or become untame */
+m_unleash(mtmp, feedback)	/* mtmp is about to die, or become untame */
 register struct monst *mtmp;
+boolean feedback;
 {
 	register struct obj *otmp;
 
+	if (feedback) {
+	    if (canseemon(mtmp))
+		pline("%s pulls free of %s leash!", Monnam(mtmp), mhis(mtmp));
+	    else
+		Your("leash falls slack.");
+	}
 	for(otmp = invent; otmp; otmp = otmp->nobj)
 		if(otmp->otyp == LEASH &&
 				otmp->leashmon == (int)mtmp->m_id)
@@ -377,7 +388,7 @@
 	for(otmp = invent; otmp; otmp = otmp->nobj)
 		if(otmp->otyp == LEASH) otmp->leashmon = 0;
 	for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
-		if(mtmp->mtame) mtmp->mleashed = 0;
+		mtmp->mleashed = 0;
 }
 
 #define MAXLEASHED	2
@@ -402,6 +413,13 @@
 	y = u.uy + u.dy;
 
 	if((x == u.ux) && (y == u.uy)) {
+#ifdef STEED
+		if (u.usteed && u.dz > 0) {
+		    mtmp = u.usteed;
+		    spotmon = 1;
+		    goto got_target;
+		}
+#endif
 		pline("Leash yourself?  Very funny...");
 		return;
 	}
@@ -412,6 +430,9 @@
 	}
 
 	spotmon = canspotmon(mtmp);
+#ifdef STEED
+ got_target:
+#endif
 
 	if(!mtmp->mtame) {
 	    if(!spotmon)
@@ -492,6 +513,10 @@
 			}
 		}
 	}
+#ifdef STEED
+	/* no pack mules for the Amulet */
+	if (u.usteed && mon_has_amulet(u.usteed)) return FALSE;
+#endif
 	return(TRUE);
 }
 
@@ -524,7 +549,7 @@
 			    if(um_dist(mtmp->mx, mtmp->my, 5)) {
 				pline("%s leash snaps loose!",
 					s_suffix(Monnam(mtmp)));
-				m_unleash(mtmp);
+				m_unleash(mtmp, FALSE);
 			    } else {
 				if(um_dist(mtmp->mx, mtmp->my, 3)) {
 				    You("pull on the leash.");
@@ -641,7 +666,7 @@
 	}
 	if(u.uswallow) {
 		if (!Blind) You("reflect %s %s.", s_suffix(mon_nam(u.ustuck)),
-		    is_animal(u.ustuck->data)? "stomach" : "interior");
+		    mbodypart(u.ustuck, STOMACH));
 		return 1;
 	}
 	if(Underwater) {
@@ -676,7 +701,8 @@
 	} else if (mlet == S_VAMPIRE || mlet == S_GHOST) {
 	    if (vis)
 		pline ("%s doesn't have a reflection.", Monnam(mtmp));
-	} else if(!mtmp->mcan && mtmp->data == &mons[PM_MEDUSA]) {
+	} else if(!mtmp->mcan && !mtmp->minvis &&
+					mtmp->data == &mons[PM_MEDUSA]) {
 		if (mon_reflects(mtmp, "The gaze is reflected away by %s %s!"))
 			return 1;
 		if (vis)
@@ -687,9 +713,6 @@
 					mtmp->data == &mons[PM_FLOATING_EYE]) {
 		int tmp = d((int)mtmp->m_lev, (int)mtmp->data->mattk[0].damd);
 		if (!rn2(4)) tmp = 120;
-	/* Note: floating eyes cannot use their abilities while invisible,
-	 * but Medusa and umber hulks can.
-	 */
 		if (vis)
 			pline("%s is frozen by its reflection.", Monnam(mtmp));
 		else You_hear("%s stop moving.",something);
@@ -697,7 +720,8 @@
 		if ( (int) mtmp->mfrozen + tmp > 127)
 			mtmp->mfrozen = 127;
 		else mtmp->mfrozen += tmp;
-	} else if(!mtmp->mcan && mtmp->data == &mons[PM_UMBER_HULK]) {
+	} else if(!mtmp->mcan && !mtmp->minvis &&
+					mtmp->data == &mons[PM_UMBER_HULK]) {
 		if (vis)
 			pline ("%s confuses itself!", Monnam(mtmp));
 		mtmp->mconf = 1;
@@ -714,10 +738,8 @@
 	} else if (!is_unicorn(mtmp->data) && !humanoid(mtmp->data) &&
 			(!mtmp->minvis || perceives(mtmp->data)) && rn2(5)) {
 		if (vis)
-			pline ("%s is frightened by its reflection.",
-				Monnam(mtmp));
-		mtmp->mflee = 1;
-		mtmp->mfleetim += d(2,4);
+		    pline("%s is frightened by its reflection.", Monnam(mtmp));
+		monflee(mtmp, d(2,4), FALSE, FALSE);
 	} else if (!Blind) {
 		if (mtmp->minvis && !See_invisible)
 		    ;
@@ -727,7 +749,7 @@
 			Monnam(mtmp));
 		else
 		    pline("%s ignores %s reflection.",
-			  Monnam(mtmp), his[pronoun_gender(mtmp)]);
+			  Monnam(mtmp), mhis(mtmp));
 	}
 	return 1;
 }
@@ -768,12 +790,13 @@
 					u.ux, u.uy, NO_MINVENT)) != 0) {
 		You("summon %s!", a_monnam(mtmp));
 		if (!obj_resists(obj, 93, 100)) {
-		    pline("%s has shattered!", The(xname(obj)));
+		    pline("%s shattered!", Tobjnam(obj, "have"));
 		    useup(obj);
 		} else switch (rn2(3)) {
 			default:
 				break;
-			case 1: mon_adjust_speed(mtmp, 2);
+			case 1:
+				mon_adjust_speed(mtmp, 2, (struct obj *)0);
 				break;
 			case 2: /* no explanation; it just happens... */
 				nomovemsg = "";
@@ -803,8 +826,8 @@
 		wakem = TRUE;
 
 	    } else  if (invoking) {
-		pline("%s issues an unsettling shrill sound...",
-		      The(xname(obj)));
+		pline("%s an unsettling shrill sound...",
+		      Tobjnam(obj, "issue"));
 #ifdef	AMIGA
 		amii_speaker( obj, "aefeaefeaefeaefeaefe", AMII_LOUDER_VOLUME );
 #endif
@@ -852,50 +875,47 @@
 use_candelabrum(obj)
 register struct obj *obj;
 {
+	char *s = obj->spe != 1 ? "candles" : "candle";
+
 	if(Underwater) {
 		You("cannot make fire under water.");
 		return;
 	}
 	if(obj->lamplit) {
-		You("snuff the candle%s.", obj->spe > 1 ? "s" : "");
+		You("snuff the %s.", s);
 		end_burn(obj, TRUE);
 		return;
 	}
 	if(obj->spe <= 0) {
-		pline("This %s has no candles.", xname(obj));
+		pline("This %s has no %s.", xname(obj), s);
 		return;
 	}
 	if(u.uswallow || obj->cursed) {
 		if (!Blind)
-		    pline_The("candle%s flicker%s for a moment, then die%s.",
-		    	obj->spe > 1 ? "s" : "",
-		    	obj->spe > 1 ? "" : "s",
-		    	obj->spe > 1 ? "" : "s");
+		    pline_The("%s %s for a moment, then %s.",
+			      s, vtense(s, "flicker"), vtense(s, "die"));
 		return;
 	}
 	if(obj->spe < 7) {
-		There("%s only %d candle%s in %s.",
-		       obj->spe == 1 ? "is" : "are",
-		       obj->spe,
-		       obj->spe > 1 ? "s" : "",
-		       the(xname(obj)));
+		There("%s only %d %s in %s.",
+		      vtense(s, "are"), obj->spe, s, the(xname(obj)));
 		if (!Blind)
-		    pline("%s lit.  %s shines dimly.",
-		       obj->spe == 1 ? "It is" : "They are", The(xname(obj)));
+		    pline("%s lit.  %s dimly.",
+			  obj->spe == 1 ? "It is" : "They are",
+			  Tobjnam(obj, "shine"));
 	} else {
-		pline("%s's candles burn%s", The(xname(obj)),
+		pline("%s's %s burn%s", The(xname(obj)), s,
 			(Blind ? "." : " brightly!"));
 	}
 	if (!invocation_pos(u.ux, u.uy)) {
-		pline_The("candle%s being rapidly consumed!",
-			(obj->spe > 1 ? "s are" : " is"));
+		pline_The("%s %s being rapidly consumed!", s, vtense(s, "are"));
 		obj->age /= 2;
 	} else {
 		if(obj->spe == 7) {
 		    if (Blind)
-		      pline("%s radiates a strange warmth!", The(xname(obj)));
+		      pline("%s a strange warmth!", Tobjnam(obj, "radiate"));
 		    else
-		      pline("%s glows with a strange light!", The(xname(obj)));
+		      pline("%s with a strange light!", Tobjnam(obj, "glow"));
 		}
 		obj->known = 1;
 	}
@@ -907,6 +927,7 @@
 register struct obj *obj;
 {
 	register struct obj *otmp;
+	char *s = obj->quan != 1 ? "candles" : "candle";
 	char qbuf[QBUFSZ];
 
 	if(u.uswallow) {
@@ -933,17 +954,15 @@
 		return;
 	} else {
 		if ((long)otmp->spe + obj->quan > 7L)
-		    (void)splitobj(obj, 7L - (long)otmp->spe);
-		You("attach %ld%s candle%s to %s.",
+		    obj = splitobj(obj, 7L - (long)otmp->spe);
+		You("attach %ld%s %s to %s.",
 		    obj->quan, !otmp->spe ? "" : " more",
-		    plur(obj->quan), the(xname(otmp)));
+		    s, the(xname(otmp)));
 		if (!otmp->spe || otmp->age > obj->age)
 		    otmp->age = obj->age;
 		otmp->spe += (int)obj->quan;
 		if (otmp->lamplit && !obj->lamplit)
-		    pline_The("new candle%s magically ignite%s!",
-			      plur(obj->quan),
-			      (obj->quan > 1L) ? "" : "s");
+		    pline_The("new %s magically %s!", s, vtense(s, "ignite"));
 		else if (!otmp->lamplit && obj->lamplit)
 		    pline("%s out.", (obj->quan > 1L) ? "They go" : "It goes");
 		if (obj->unpaid)
@@ -952,8 +971,8 @@
 			      (obj->quan > 1L) ? "them" : "it",
 			      (obj->quan > 1L) ? "them" : "it");
 		if (obj->quan < 7L && otmp->spe == 7)
-		    pline("%s now has seven%s candles attached.",
-			  The(xname(otmp)), otmp->lamplit ? " lit" : "");
+		    pline("%s now has seven%s %s attached.",
+			  The(xname(otmp)), otmp->lamplit ? " lit" : "", s);
 		/* candelabrum's light range might increase */
 		if (otmp->lamplit) obj_merge_light_sources(otmp, otmp);
 		/* candles are no longer a separate light source */
@@ -1001,7 +1020,7 @@
 		    obj->otyp == BRASS_LANTERN || obj->otyp == POT_OIL) {
 		(void) get_obj_location(obj, &x, &y, 0);
 		if (obj->where == OBJ_MINVENT ? cansee(x,y) : !Blind)
-		    pline("%s goes out!", Yname2(obj));
+		    pline("%s %s out!", Yname2(obj), otense(obj, "go"));
 		end_burn(obj, TRUE);
 		return TRUE;
 	    }
@@ -1010,6 +1029,39 @@
 	return FALSE;
 }
 
+/* Called when potentially lightable object is affected by fire_damage().
+   Return TRUE if object was lit and FALSE otherwise --ALI */
+boolean
+catch_lit(obj)
+struct obj *obj;
+{
+	xchar x, y;
+
+	if (!obj->lamplit && (obj->otyp == CANDELABRUM_OF_INVOCATION ||
+		obj->otyp == WAX_CANDLE || obj->otyp == TALLOW_CANDLE ||
+		obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP ||
+		obj->otyp == BRASS_LANTERN || obj->otyp == POT_OIL)) {
+	    if ((obj->otyp == MAGIC_LAMP ||
+		 obj->otyp == CANDELABRUM_OF_INVOCATION) &&
+		obj->spe == 0)
+		return FALSE;
+	    else if (obj->otyp != MAGIC_LAMP && obj->age == 0)
+		return FALSE;
+	    if (!get_obj_location(obj, &x, &y, 0))
+		return FALSE;
+	    if (obj->otyp == CANDELABRUM_OF_INVOCATION && obj->cursed)
+		return FALSE;
+	    if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP ||
+		 obj->otyp == BRASS_LANTERN) && obj->cursed && !rn2(2))
+		return FALSE;
+	    if (obj->where == OBJ_MINVENT ? cansee(x,y) : !Blind)
+		pline("%s %s light!", Yname2(obj), otense(obj, "catch"));
+	    begin_burn(obj, TRUE);
+	    return TRUE;
+	}
+	return FALSE;
+}
+
 STATIC_OVL void
 use_lamp(obj)
 struct obj *obj;
@@ -1038,20 +1090,17 @@
 		return;
 	}
 	if (obj->cursed && !rn2(2)) {
-		pline("%s flicker%s for a moment, then die%s.",
-		       The(xname(obj)),
-		       obj->quan > 1L ? "" : "s",
-		       obj->quan > 1L ? "" : "s");
+		pline("%s for a moment, then %s.",
+		      Tobjnam(obj, "flicker"), otense(obj, "die"));
 	} else {
 		if(obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP ||
 				obj->otyp == BRASS_LANTERN) {
 		    check_unpaid(obj);
 		    pline("%s lamp is now on.", Shk_Your(buf, obj));
 		} else {	/* candle(s) */
-		    pline("%s flame%s burn%s%s",
+		    pline("%s flame%s %s%s",
 			s_suffix(Yname2(obj)),
-			plur(obj->quan),
-			obj->quan > 1L ? "" : "s",
+			plur(obj->quan), otense(obj, "burn"),
 			Blind ? "." : " brightly!");
 		    if (obj->unpaid && costly_spot(u.ux, u.uy) &&
 			  obj->age == 20L * (long)objects[obj->otyp].oc_cost) {
@@ -1098,12 +1147,13 @@
 	     * for an item, but (Yendorian Fuel) Taxes are inevitable...
 	     */
 	    check_unpaid(obj);
+	    verbalize("That's in addition to the cost of the potion, of course.");
 	    bill_dummy_object(obj);
 	}
 	makeknown(obj->otyp);
 
 	if (obj->quan > 1L) {
-	    (void) splitobj(obj, 1L);
+	    obj = splitobj(obj, 1L);
 	    begin_burn(obj, FALSE);	/* burn before free to get position */
 	    obj_extract_self(obj);	/* free from inv */
 
@@ -1114,13 +1164,23 @@
 	    begin_burn(obj, FALSE);
 }
 
-static NEARDATA const char cuddly[] = { TOOL_CLASS, 0 };
+static NEARDATA const char cuddly[] = { TOOL_CLASS, GEM_CLASS, 0 };
 
 int
 dorub()
 {
 	struct obj *obj = getobj(cuddly, "rub");
 
+	if (obj && obj->oclass == GEM_CLASS) {
+	    if (is_graystone(obj)) {
+		use_stone(obj);
+		return 1;
+	    } else {
+		pline("Sorry, I don't know how to use that.");
+		return 0;
+	    }
+	}
+
 	if(!obj || (obj != uwep && !wield_tool(obj))) return 0;
 
 	/* now uwep is obj */
@@ -1133,6 +1193,7 @@
 		uwep->spe = 0; /* for safety */
 		uwep->age = rn1(500,1000);
 		if (uwep->lamplit) begin_burn(uwep, TRUE);
+		update_inventory();
 	    } else if (rn2(2) && !Blind)
 		You("see a puff of smoke.");
 	    else pline(nothing_happens);
@@ -1180,6 +1241,11 @@
 		pline("This calls for swimming, not jumping!");
 		return 0;
 	} else if (u.ustuck) {
+		if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf) {
+		    You("pull free from %s.", mon_nam(u.ustuck));
+		    u.ustuck = 0;
+		    return 1;
+		}
 		if (magic) {
 			You("writhe a little in the grasp of %s!", mon_nam(u.ustuck));
 			return 1;
@@ -1206,7 +1272,7 @@
 		if (wl == BOTH_SIDES) bp = makeplural(bp);
 #ifdef STEED
 		if (u.usteed)
-	 	    pline("%s is in no shape for jumping.", Monnam(u.usteed));
+		    pline("%s is in no shape for jumping.", Monnam(u.usteed));
 		else
 #endif
 		Your("%s%s %s in no shape for jumping.",
@@ -1424,16 +1490,13 @@
 
 	/* collect property troubles */
 	if (Sick) prop_trouble(SICK);
-	if (Blinded > (long)(u.ucreamed + 1)) prop_trouble(BLINDED);
+	if (Blinded > (long)u.ucreamed) prop_trouble(BLINDED);
 	if (HHallucination) prop_trouble(HALLUC);
 	if (Vomiting) prop_trouble(VOMITING);
 	if (HConfusion) prop_trouble(CONFUSION);
 	if (HStun) prop_trouble(STUNNED);
-	/* keep track of unfixed trouble, for message adjustment below
-	   (can't "feel great" with these problems present) */
-	if (Stoned) unfixable_trbl++;
-	if (Strangled) unfixable_trbl++;
-	if (Wounded_legs) unfixable_trbl++;
+
+	unfixable_trbl = unfixable_trouble_count(TRUE);
 
 	/* collect attribute troubles */
 	for (idx = 0; idx < A_MAX; idx++) {
@@ -1482,7 +1545,7 @@
 		did_prop++;
 		break;
 	    case prop2trbl(BLINDED):
-		make_blinded(u.ucreamed ? (long)(u.ucreamed+1) : 0L, TRUE);
+		make_blinded((long)u.ucreamed, TRUE);
 		did_prop++;
 		break;
 	    case prop2trbl(HALLUC):
@@ -1551,17 +1614,17 @@
 	silent = (timeout != monstermoves); /* happened while away */
 	okay_spot = get_obj_location(figurine, &cc.x, &cc.y, 0);
 	if (figurine->where == OBJ_INVENT ||
-	    figurine->where == OBJ_MINVENT) 
-	        okay_spot = enexto(&cc, cc.x, cc.y,
+	    figurine->where == OBJ_MINVENT)
+		okay_spot = enexto(&cc, cc.x, cc.y,
 				   &mons[figurine->corpsenm]);
 	if (!okay_spot ||
 	    !figurine_location_checks(figurine,&cc, TRUE)) {
-	    	/* reset the timer to try again later */
+		/* reset the timer to try again later */
 		(void) start_timer((long)rnd(5000), TIMER_OBJECT,
 				FIG_TRANSFORM, (genericptr_t)figurine);
 		return;
 	}
-	
+
 	cansee_spot = cansee(cc.x, cc.y);
 	mtmp = make_familiar(figurine, cc.x, cc.y, TRUE);
 	if (mtmp) {
@@ -1587,8 +1650,8 @@
 
 		case OBJ_MINVENT:
 		    if (cansee_spot && !silent) {
-		    	struct monst *mon;
-		    	mon = figurine->ocarry;
+			struct monst *mon;
+			mon = figurine->ocarry;
 			/* figurine carring monster might be invisible */
 			if (canseemon(figurine->ocarry)) {
 			    Sprintf(carriedby, "%s pack",
@@ -1626,7 +1689,7 @@
 boolean quietly;
 {
 	xchar x,y;
-	
+
 	x = cc->x; y = cc->y;
 	if (!isok(x,y)) {
 		if (!quietly)
@@ -1636,7 +1699,8 @@
 	if (IS_ROCK(levl[x][y].typ) &&
 	    !(passes_walls(&mons[obj->corpsenm]) && may_passwall(x,y))) {
 		if (!quietly)
-			You("cannot place a figurine in solid rock!");
+		    You("cannot place a figurine in %s!",
+			IS_TREE(levl[x][y].typ) ? "a tree" : "solid rock");
 		return FALSE;
 	}
 	if (sobj_at(BOULDER,x,y) && !passes_walls(&mons[obj->corpsenm])
@@ -1654,7 +1718,7 @@
 {
 	xchar x, y;
 	coord cc;
-	
+
 	if(!getdir((char *)0)) {
 		flags.move = multi = 0;
 		return;
@@ -1688,7 +1752,7 @@
 
 	if (Glib) {
 	    dropx(obj);
-	    pline("%s slips from your %s.", The(xname(obj)),
+	    pline("%s from your %s.", Tobjnam(obj, "slip"),
 		  makeplural(body_part(FINGER)));
 	    return;
 	}
@@ -1698,7 +1762,7 @@
 			check_unpaid(obj);
 			obj->spe--;
 			dropx(obj);
-			pline("%s slips from your %s.", The(xname(obj)),
+			pline("%s from your %s.", Tobjnam(obj, "slip"),
 			      makeplural(body_part(FINGER)));
 			return;
 		}
@@ -1735,9 +1799,12 @@
 			    makeplural(body_part(FINGER)));
 		}
 	} else {
-		pline("%s %s empty.", The(xname(obj)),
-			obj->known ? "is" : "seems to be");
+	    if (obj->known)
+		pline("%s empty.", Tobjnam(obj, "are"));
+	    else
+		pline("%s to be empty.", Tobjnam(obj, "seem"));
 	}
+	update_inventory();
 }
 
 static struct trapinfo {
@@ -1752,6 +1819,132 @@
 	trapinfo.tobj = 0;
 }
 
+/* touchstones - by Ken Arnold */
+STATIC_OVL void
+use_stone(tstone)
+struct obj *tstone;
+{
+    struct obj *obj;
+    boolean do_scratch;
+    const char *streak_color;
+    char stonebuf[QBUFSZ];
+    static const char scritch[] = "\"scritch, scritch\"";
+    static char allowall[3] = { GOLD_CLASS, ALL_CLASSES, 0 };
+    struct obj goldobj;
+
+    Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan));
+    if ((obj = getobj(allowall, stonebuf)) == 0)
+	return;
+    if (obj->oclass == GOLD_CLASS) {
+	u.ugold += obj->quan;	/* keep botl up to date */
+	goldobj = *obj;
+	dealloc_obj(obj);
+	obj = &goldobj;
+    }
+
+    if (obj == tstone && obj->quan == 1) {
+	You_cant("rub %s on itself.", the(xname(obj)));
+	return;
+    }
+
+    if (tstone->otyp == TOUCHSTONE && tstone->cursed &&
+	    obj->oclass == GEM_CLASS && !is_graystone(obj) &&
+	    !obj_resists(obj, 80, 100)) {
+	if (Blind)
+	    pline("You feel something shatter.");
+	else if (Hallucination)
+	    pline("Oh, wow, look at the pretty shards.");
+	else
+	    pline("A sharp crack shatters %s%s.",
+		  (obj->quan > 1) ? "one of " : "", the(xname(obj)));
+     /* assert(obj != &goldobj); */
+	useup(obj);
+	return;
+    }
+
+    if (Blind) {
+	pline(scritch);
+	return;
+    } else if (Hallucination) {
+	pline("Oh wow, man: Fractals!");
+	return;
+    }
+
+    do_scratch = FALSE;
+    streak_color = 0;
+
+    switch (obj->oclass) {
+    case GEM_CLASS:	/* these have class-specific handling below */
+    case RING_CLASS:
+	if (tstone->otyp != TOUCHSTONE) {
+	    do_scratch = TRUE;
+	} else if (obj->oclass == GEM_CLASS && (tstone->blessed ||
+		(!tstone->cursed &&
+		    (Role_if(PM_ARCHEOLOGIST) || Race_if(PM_GNOME))))) {
+	    makeknown(TOUCHSTONE);
+	    makeknown(obj->otyp);
+	    prinv((char *)0, obj, 0L);
+	    return;
+	} else {
+	    /* either a ring or the touchstone was not effective */
+	    if (objects[obj->otyp].oc_material == GLASS) {
+		do_scratch = TRUE;
+		break;
+	    }
+	}
+	streak_color = c_obj_colors[objects[obj->otyp].oc_color];
+	break;		/* gem or ring */
+
+    default:
+	switch (objects[obj->otyp].oc_material) {
+	case CLOTH:
+	    pline("%s a little more polished now.", Tobjnam(tstone, "look"));
+	    return;
+	case LIQUID:
+	    if (!obj->known)		/* note: not "whetstone" */
+		You("must think this is a wetstone, do you?");
+	    else
+		pline("%s a little wetter now.", Tobjnam(tstone, "are"));
+	    return;
+	case WAX:
+	    streak_color = "waxy";
+	    break;		/* okay even if not touchstone */
+	case WOOD:
+	    streak_color = "wooden";
+	    break;		/* okay even if not touchstone */
+	case GOLD:
+	    do_scratch = TRUE;	/* scratching and streaks */
+	    streak_color = "golden";
+	    break;
+	case SILVER:
+	    do_scratch = TRUE;	/* scratching and streaks */
+	    streak_color = "silvery";
+	    break;
+	default:
+	    /* Objects passing the is_flimsy() test will not
+	       scratch a stone.  They will leave streaks on
+	       non-touchstones and touchstones alike. */
+	    if (is_flimsy(obj))
+		streak_color = c_obj_colors[objects[obj->otyp].oc_color];
+	    else
+		do_scratch = (tstone->otyp != TOUCHSTONE);
+	    break;
+	}
+	break;		/* default oclass */
+    }
+
+    Sprintf(stonebuf, "stone%s", plur(tstone->quan));
+    if (do_scratch)
+	pline("You make %s%sscratch marks on the %s.",
+	      streak_color ? streak_color : (const char *)"",
+	      streak_color ? " " : "", stonebuf);
+    else if (streak_color)
+	pline("You see %s streaks on the %s.", streak_color, stonebuf);
+    else
+	pline(scritch);
+    return;
+}
+
 /* Place a landmine/bear trap.  Helge Hafting */
 STATIC_OVL void
 use_trap(otmp)
@@ -1845,7 +2038,7 @@
 	    }
 	    You("finish arming %s.",
 		the(defsyms[trap_to_defsym(what_trap(ttyp))].explanation));
-	    if ((otmp->cursed || Fumbling) && (rnl(10) > 5)) dotrap(ttmp);
+	    if ((otmp->cursed || Fumbling) && (rnl(10) > 5)) dotrap(ttmp, 0);
 	} else {
 	    /* this shouldn't happen */
 	    Your("trap setting attempt fails.");
@@ -1930,16 +2123,14 @@
 	dam = rnd(2) + dbon() + obj->spe;
 	if (dam <= 0) dam = 1;
 	You("hit your %s with your bullwhip.", body_part(FOOT));
-	/* self_pronoun() won't work twice in a sentence */
-	Strcpy(buf, self_pronoun("killed %sself with %%s bullwhip", "him"));
-	losehp(dam, self_pronoun(buf, "his"), NO_KILLER_PREFIX);
+	Sprintf(buf, "killed %sself with %s bullwhip", uhim(), uhis());
+	losehp(dam, buf, NO_KILLER_PREFIX);
 	flags.botl = 1;
 	return 1;
 
     } else if ((Fumbling || Glib) && !rn2(5)) {
 	pline_The("bullwhip slips out of your %s.", body_part(HAND));
 	dropx(obj);
-	setuwep((struct obj *)0);
 
     } else if (u.utrap && u.utraptype == TT_PIT) {
 	/*
@@ -2005,7 +2196,7 @@
 	    const char *mon_hand;
 	    boolean gotit = proficient && (!Fumbling || !rn2(10));
 
-	    Strcpy(onambuf, xname(otmp));
+	    Strcpy(onambuf, cxname(otmp));
 	    if (gotit) {
 		mon_hand = mbodypart(mtmp, HAND);
 		if (bimanual(otmp)) mon_hand = makeplural(mon_hand);
@@ -2017,7 +2208,7 @@
 	    if (gotit && otmp->cursed) {
 		pline("%s welded to %s %s%c",
 		      (otmp->quan == 1L) ? "It is" : "They are",
-		      his[pronoun_gender(mtmp)], mon_hand,
+		      mhis(mtmp), mon_hand,
 		      !otmp->bknown ? '!' : '.');
 		otmp->bknown = 1;
 		gotit = FALSE;	/* can't pull it free */
@@ -2025,18 +2216,13 @@
 	    if (gotit) {
 		obj_extract_self(otmp);
 		possibly_unwield(mtmp);
-		otmp->owornmask &= ~W_WEP;
+		setmnotwielded(mtmp,otmp);
 
 		switch (rn2(proficient + 1)) {
 		case 2:
 		    /* to floor near you */
 		    You("yank %s %s to the %s!", s_suffix(mon_nam(mtmp)),
 			onambuf, surface(u.ux, u.uy));
-		    if (otmp->otyp == CRYSKNIFE &&
-			    (!otmp->oerodeproof || !rn2(10))) {
-			otmp->otyp = WORM_TOOTH;
-			otmp->oerodeproof = 0;
-		    }
 		    place_object(otmp, u.ux, u.uy);
 		    stackobj(otmp);
 		    break;
@@ -2053,7 +2239,7 @@
 				     dmgval(otmp, &youmonst),
 				     otmp, (char *)0);
 			if (hitu) {
-			    You("The %s hits you as you try to snatch it!",
+			    pline_The("%s hits you as you try to snatch it!",
 				the(onambuf));
 			}
 			place_object(otmp, u.ux, u.uy);
@@ -2071,7 +2257,7 @@
 			char kbuf[BUFSZ];
 
 			Sprintf(kbuf, "%s corpse",
-			        an(mons[otmp->corpsenm].mname));
+				an(mons[otmp->corpsenm].mname));
 			pline("Snatching %s is a fatal mistake.", kbuf);
 			instapetrify(kbuf);
 		    }
@@ -2082,11 +2268,7 @@
 		    /* to floor beneath mon */
 		    You("yank %s from %s %s!", the(onambuf),
 			s_suffix(mon_nam(mtmp)), mon_hand);
-		    if (otmp->otyp == CRYSKNIFE &&
-			    (!otmp->oerodeproof || !rn2(10))) {
-			otmp->otyp = WORM_TOOTH;
-			otmp->oerodeproof = 0;
-		    }
+		    obj_no_longer_held(otmp);
 		    place_object(otmp, mtmp->mx, mtmp->my);
 		    stackobj(otmp);
 		    break;
@@ -2096,7 +2278,10 @@
 	    }
 	    wakeup(mtmp);
 	} else {
-	    You("flick your bullwhip towards %s.", mon_nam(mtmp));
+	    if (mtmp->m_ap_type &&
+		!Protection_from_shape_changers && !sensemon(mtmp))
+		stumble_onto_mimic(mtmp);
+	    else You("flick your bullwhip towards %s.", mon_nam(mtmp));
 	    if (proficient) {
 		if (attack(mtmp)) return 1;
 		else pline(msg_snap);
@@ -2165,9 +2350,17 @@
 	}
 
 	/* Attack the monster there */
-	if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *)0)
+	if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *)0) {
+	    int oldhp = mtmp->mhp;
+
 	    (void) thitmonst(mtmp, uwep);
-	else
+	    /* check the monster's HP because thitmonst() doesn't return
+	     * an indication of whether it hit.  Not perfect (what if it's a
+	     * non-silver weapon on a shade?)
+	     */
+	    if (mtmp->mhp < oldhp)
+		u.uconduct.weaphit++;
+	} else
 	    /* Now you know that nothing is there... */
 	    pline(nothing_happens);
 	return (1);
@@ -2215,8 +2408,7 @@
 	}
 
 	/* What did you hit? */
-	switch (rn2(5))
-	{
+	switch (rn2(5)) {
 	case 0:	/* Trap */
 	    /* FIXME -- untrap needs to deal with non-adjacent traps */
 	    break;
@@ -2244,9 +2436,13 @@
 	    }
 	    /* FALL THROUGH */
 	case 3:	/* Surface */
-	    You("are yanked toward the %s!",
-			surface(cc.x, cc.y));
-	    hurtle(sgn(cc.x-u.ux), sgn(cc.y-u.uy), 1, FALSE);
+	    if (IS_AIR(levl[cc.x][cc.y].typ) || is_pool(cc.x, cc.y))
+		pline_The("hook slices through the %s.", surface(cc.x, cc.y));
+	    else {
+		You("are yanked toward the %s!", surface(cc.x, cc.y));
+		hurtle(sgn(cc.x-u.ux), sgn(cc.y-u.uy), 1, FALSE);
+		spoteffects(TRUE);
+	    }
 	    return (1);
 	default:	/* Yourself (oops!) */
 	    if (P_SKILL(typ) <= P_BASIC) {
@@ -2273,7 +2469,8 @@
     register struct monst *mon;
     int dmg, damage;
     boolean affects_objects;
-    char confirm[QBUFSZ], the_wand[BUFSZ];
+    int expltype = EXPL_MAGICAL;
+    char confirm[QBUFSZ], the_wand[BUFSZ], buf[BUFSZ];
 
     Strcpy(the_wand, yname(obj));
     Sprintf(confirm, "Are you really sure you want to break %s?", the_wand);
@@ -2289,6 +2486,14 @@
     pline("Raising %s high above your %s, you break it in two!",
 	  the_wand, body_part(HEAD));
 
+    /* [ALI] Do this first so that wand is removed from bill. Otherwise,
+     * the freeinv() below also hides it from setpaid() which causes problems.
+     */
+    if (obj->unpaid) {
+	check_unpaid(obj);		/* Extra charge for use */
+	bill_dummy_object(obj);
+    }
+
     current_wand = obj;		/* destroy_item might reset this */
     freeinv(obj);		/* hide it from destroy_item instead... */
     setnotworn(obj);		/* so we need to do this ourselves */
@@ -2314,12 +2519,17 @@
 	goto discard_broken_wand;
     case WAN_DEATH:
     case WAN_LIGHTNING:
-	dmg *= 2;
+	dmg *= 4;
+	goto wanexpl;
     case WAN_FIRE:
+	expltype = EXPL_FIERY;
     case WAN_COLD:
+	if (expltype == EXPL_MAGICAL) expltype = EXPL_FROSTY;
 	dmg *= 2;
     case WAN_MAGIC_MISSILE:
-	explode(u.ux, u.uy, (obj->otyp - WAN_MAGIC_MISSILE), dmg, WAND_CLASS);
+    wanexpl:
+	explode(u.ux, u.uy,
+		(obj->otyp - WAN_MAGIC_MISSILE), dmg, WAND_CLASS, expltype);
 	makeknown(obj->otyp);	/* explode described the effect */
 	goto discard_broken_wand;
     case WAN_STRIKING:
@@ -2337,7 +2547,7 @@
     }
 
     /* magical explosion and its visual effect occur before specific effects */
-    explode(obj->ox, obj->oy, 0, rnd(dmg), WAND_CLASS);
+    explode(obj->ox, obj->oy, 0, rnd(dmg), WAND_CLASS, EXPL_MAGICAL);
 
     /* this makes it hit us last, so that we can see the action first */
     for (i = 0; i <= 8; i++) {
@@ -2366,11 +2576,10 @@
 		    if (flags.botl) bot();		/* potion effects */
 		}
 		damage = zapyourself(obj, FALSE);
-		if (damage)
-		    losehp(damage,
-			   self_pronoun("killed %sself by breaking a wand",
-					"him"),
-			   NO_KILLER_PREFIX);
+		if (damage) {
+		    Sprintf(buf, "killed %sself by breaking a wand", uhim());
+		    losehp(damage, buf, NO_KILLER_PREFIX);
+		}
 		if (flags.botl) bot();		/* blindness */
 	    } else if ((mon = m_at(x, y)) != 0) {
 		(void) bhitm(mon, obj);
@@ -2389,15 +2598,23 @@
  discard_broken_wand:
     obj = current_wand;		/* [see dozap() and destroy_item()] */
     current_wand = 0;
-    if (obj) {
-	/* extra charge for _use_ prior to destruction */
-	check_unpaid(obj);
+    if (obj)
 	delobj(obj);
-    }
     nomul(0);
     return 1;
 }
 
+STATIC_OVL boolean
+uhave_graystone()
+{
+	register struct obj *otmp;
+
+	for(otmp = invent; otmp; otmp = otmp->nobj)
+		if(is_graystone(otmp))
+			return TRUE;
+	return FALSE;
+}
+
 int
 doapply()
 {
@@ -2405,7 +2622,8 @@
 	register int res = 1;
 
 	if(check_capacity((char *)0)) return (0);
-	obj = getobj(carrying(POT_OIL) ? tools_too : tools, "use or apply");
+	obj = getobj(carrying(POT_OIL) || uhave_graystone()
+		? tools_too : tools, "use or apply");
 	if(!obj) return 0;
 
 	if (obj->oclass == WAND_CLASS)
@@ -2419,7 +2637,7 @@
 		} else if (!ublindf)
 		    Blindf_on(obj);
 		else You("are already %s.",
-			ublindf->otyp == TOWEL ?     "covered by a towel" : 
+			ublindf->otyp == TOWEL ?     "covered by a towel" :
 			ublindf->otyp == BLINDFOLD ? "wearing a blindfold" :
 						     "wearing lenses");
 		break;
@@ -2589,6 +2807,12 @@
 	case BEARTRAP:
 		use_trap(obj);
 		break;
+	case FLINT:
+	case LUCKSTONE:
+	case LOADSTONE:
+	case TOUCHSTONE:
+		use_stone(obj);
+		break;
 	default:
 		/* Pole-weapons can strike at a distance */
 		if (is_pole(obj)) {
@@ -2608,6 +2832,37 @@
 	return res;
 }
 
+/* Keep track of unfixable troubles for purposes of messages saying you feel
+ * great.
+ */
+int unfixable_trouble_count(is_horn)
+	boolean is_horn;
+{
+	int unfixable_trbl = 0;
+
+	if (Stoned) unfixable_trbl++;
+	if (Strangled) unfixable_trbl++;
+	if (Wounded_legs
+#ifdef STEED
+		    && !u.usteed
+#endif
+				) unfixable_trbl++;
+	if (Slimed) unfixable_trbl++;
+	/* lycanthropy is not desirable, but it doesn't actually make you feel
+	   bad */
+
+	/* we'll assume that intrinsic stunning from being a bat/stalker
+	   doesn't make you feel bad */
+	if (!is_horn) {
+	    if (Confusion) unfixable_trbl++;
+	    if (Sick) unfixable_trbl++;
+	    if (HHallucination) unfixable_trbl++;
+	    if (Vomiting) unfixable_trbl++;
+	    if (HStun) unfixable_trbl++;
+	}
+	return unfixable_trbl;
+}
+
 #endif /* OVLB */
 
 /*apply.c*/
diff -Naurd ../nethack-3.3.1/src/artifact.c ./src/artifact.c
--- ../nethack-3.3.1/src/artifact.c Sun Jul 23 16:00:15 2000
+++ ./src/artifact.c Fri Mar 22 14:40:55 2002
@@ -23,13 +23,15 @@
 STATIC_DCL int FDECL(spec_applies, (const struct artifact *,struct monst *));
 STATIC_DCL int FDECL(arti_invoke, (struct obj*));
 
-/* The amount added to normal damage which should guarantee that the
+/* The amount added to the victim's total hit points to insure that the
    victim will be killed even after damage bonus/penalty adjustments.
-   It needs to be big enough so that halving will still kill, but not
-   so big that doubling ends up overflowing 15 bits.  This value used
-   to be 1234, but it was possible for players to accumulate enough
-   hit points so that taking (uhp + 1234)/2 damage was survivable. */
-#define FATAL_DAMAGE 9999
+   Most such penalties are small, and 200 is plenty; the exception is
+   half physical damage.  3.3.1 and previous versions tried to use a very
+   large number to account for this case; now, we just compute the fatal
+   damage by adding it to 2 times the total hit points instead of 1 time.
+   Note: this will still break if they have more than about half the number
+   of hit points that will fit in a 15 bit integer. */
+#define FATAL_DAMAGE_MODIFIER 200
 
 #ifndef OVLB
 STATIC_DCL int spec_dbon_applies;
@@ -60,7 +62,7 @@
 
 	/* Excalibur can be used by any lawful character, not just knights */
 	if (!Role_if(PM_KNIGHT))
-	    artilist[ART_EXCALIBUR].role = 0;		/****FIXME****/
+	    artilist[ART_EXCALIBUR].role = NON_PM;
 
 	/* Fix up the quest artifact */
 	if (urole.questarti) {
@@ -472,16 +474,18 @@
     /* all quest artifacts are self-willed; it this ever changes, `badclass'
        will have to be extended to explicitly include quest artifacts */
     self_willed = ((oart->spfx & SPFX_INTEL) != 0);
-    if (yours || !(mon->data->mflags3 & M3_WANTSALL)) {
-	badclass = (self_willed && (!yours ||
-			(oart->role != NON_PM && !Role_if(oart->role)) ||
-			(oart->race != NON_PM && !Race_if(oart->race))));
-	badalign = (oart->spfx & SPFX_RESTR) &&
-		   oart->alignment != A_NONE &&
-	    ((oart->alignment !=
-	      (yours ? u.ualign.type : sgn(mon->data->maligntyp))) ||
-	     (yours && u.ualign.record < 0));
-    } else {	/* an M3_WANTSxxx monster */
+    if (yours) {
+	badclass = self_willed &&
+		   ((oart->role != NON_PM && !Role_if(oart->role)) ||
+		    (oart->race != NON_PM && !Race_if(oart->race)));
+	badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE &&
+		   (oart->alignment != u.ualign.type || u.ualign.record < 0);
+    } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) {
+	badclass = self_willed &&
+		   oart->role != NON_PM && oart != &artilist[ART_EXCALIBUR];
+	badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE &&
+		   (oart->alignment != sgn(mon->data->maligntyp));
+    } else {    /* an M3_WANTSxxx monster or a fake player */
 	/* special monsters trying to take the Amulet, invocation tools or
 	   quest item can touch anything except for `spec_applies' artifacts */
 	badclass = badalign = FALSE;
@@ -511,7 +515,7 @@
 
     /* can pick it up unless you're totally non-synch'd with the artifact */
     if (badclass && badalign && self_willed) {
-	if (yours) pline("%s evades your grasp!", The(xname(obj)));
+	if (yours) pline("%s your grasp!", Tobjnam(obj, "evade"));
 	return 0;
     }
 
@@ -596,8 +600,11 @@
 {
 	register const struct artifact *weap = get_artifact(otmp);
 
-	if (weap && spec_applies(weap, mon))
-	    return weap->attk.damn ? rnd((int)weap->attk.damn) : 0;
+	/* no need for an extra check for `NO_ATTK' because this will
+	   always return 0 for any artifact which has that attribute */
+
+	if (weap && weap->attk.damn && spec_applies(weap, mon))
+	    return rnd((int)weap->attk.damn);
 	return 0;
 }
 
@@ -610,7 +617,13 @@
 {
 	register const struct artifact *weap = get_artifact(otmp);
 
-	if ((spec_dbon_applies = (weap && spec_applies(weap, mon))) != 0)
+	if (!weap || (weap->attk.adtyp == AD_PHYS && /* check for `NO_ATTK' */
+			weap->attk.damn == 0 && weap->attk.damd == 0))
+	    spec_dbon_applies = FALSE;
+	else
+	    spec_dbon_applies = spec_applies(weap, mon);
+
+	if (spec_dbon_applies)
 	    return weap->attk.damd ? rnd((int)weap->attk.damd) : max(tmp,1);
 	return 0;
 }
@@ -695,9 +708,11 @@
 	boolean vis = (!youattack && magr && cansee(magr->mx, magr->my))
 		|| (!youdefend && cansee(mdef->mx, mdef->my));
 	boolean realizes_damage;
-
+	const char *wepdesc;
 	static const char you[] = "you";
-	const char *hittee = youdefend ? you : mon_nam(mdef);
+	char hittee[BUFSZ];
+
+	Strcpy(hittee, youdefend ? you : mon_nam(mdef));
 
 	/* The following takes care of most of the damage, but not all--
 	 * the exception being for level draining, which is specially
@@ -706,51 +721,61 @@
 	*dmgptr += spec_dbon(otmp, mdef, *dmgptr);
 
 	if (youattack && youdefend) {
-		impossible("attacking yourself with weapon?");
-		return FALSE;
-	} else if (!spec_dbon_applies) {
-		/* since damage bonus didn't apply, nothing more to do */
-		return FALSE;
+	    impossible("attacking yourself with weapon?");
+	    return FALSE;
 	}
 
 	realizes_damage = (youdefend || vis);
 
 	/* the four basic attacks: fire, cold, shock and missiles */
 	if (attacks(AD_FIRE, otmp)) {
-	    if (realizes_damage) {
-		pline_The("fiery blade %s %s!",
+	    if (realizes_damage)
+		pline_The("fiery blade %s %s%c",
+			!spec_dbon_applies ? "hits" :
 			(mdef->data == &mons[PM_WATER_ELEMENTAL]) ?
-			"vaporizes part of" : "burns", hittee);
-		if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
-		if (!rn2(4)) (void) destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
-		if (!rn2(7)) (void) destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
-		return TRUE;
-	    }
+			"vaporizes part of" : "burns",
+			hittee, !spec_dbon_applies ? '.' : '!');
+	    if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
+	    if (!rn2(4)) (void) destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
+	    if (!rn2(7)) (void) destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
+	    if (youdefend && Slimed) burn_away_slime();
+	    return realizes_damage;
 	}
 	if (attacks(AD_COLD, otmp)) {
-	    if (realizes_damage) {
-		pline_The("ice-cold blade freezes %s!", hittee);
-		if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD);
-		return TRUE;
-	    }
+	    if (realizes_damage)
+		pline_The("ice-cold blade %s %s%c",
+			!spec_dbon_applies ? "hits" : "freezes",
+			hittee, !spec_dbon_applies ? '.' : '!');
+	    if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD);
+	    return realizes_damage;
 	}
 	if (attacks(AD_ELEC, otmp)) {
 	    if (realizes_damage) {
-		if(youattack && otmp != uwep)
-		    pline("%s hits %s!", The(xname(otmp)), hittee);
-		pline("Lightning strikes %s!", hittee);
-		if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC);
-		if (!rn2(5)) (void) destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
-		return TRUE;
+		if (youattack ? otmp != uwep : !spec_dbon_applies)
+		    pline("%s %s%c", Tobjnam(otmp, "hit"),
+			  hittee, !spec_dbon_applies ? '.' : '!');
+		if (spec_dbon_applies)
+		    pline("Lightning strikes %s!", hittee);
 	    }
+	    if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC);
+	    if (!rn2(5)) (void) destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
+	    return realizes_damage;
 	}
 	if (attacks(AD_MAGM, otmp)) {
-		if (realizes_damage) {
-			if(youattack && otmp != uwep)
-			    pline("%s hits %s!", The(xname(otmp)), hittee);
-			pline("A hail of magic missiles strikes %s!", hittee);
-			return TRUE;
-		}
+	    if (realizes_damage) {
+		if (youattack ? otmp != uwep : !spec_dbon_applies)
+		    pline("%s %s%c", Tobjnam(otmp, "hit"),
+			  hittee, !spec_dbon_applies ? '.' : '!');
+		if (spec_dbon_applies)
+		    pline("A hail of magic missiles strikes %s!", hittee);
+	    }
+	    return realizes_damage;
+	}
+
+	if (!spec_dbon_applies) {
+	    /* since damage bonus didn't apply, nothing more to do;  
+	       no further attacks have side-effects on inventory */
+	    return FALSE;
 	}
 
 	/*
@@ -803,7 +828,7 @@
 				else {
 					nomul(-3);
 					nomovemsg = "";
-					if ((magr == u.ustuck)
+					if (magr && magr == u.ustuck
 						&& sticks(youmonst.data)) {
 					    u.ustuck = (struct monst *)0;
 					    You("release %s!", mon_nam(magr));
@@ -813,18 +838,7 @@
 				if (rn2(2) && resist(mdef,SPBOOK_CLASS,0,0)) {
 				    MB_RESIST_ATTACK;
 				} else {
-				    if (mdef == u.ustuck) {
-					if (u.uswallow)
-					    expels(mdef,mdef->data,TRUE);
-					else {
-					    if (!sticks(youmonst.data)) {
-						u.ustuck = (struct monst *)0;
-						You("get released!");
-					    }
-					}
-				    }
-				    mdef->mflee = 1;
-				    mdef->mfleetim += 3;
+				    monflee(mdef, 3, FALSE, TRUE);
 				}
 			}
 		}
@@ -926,10 +940,11 @@
 	/* reverse from AD&D. */
 	if (spec_ability(otmp, SPFX_BEHEAD)) {
 	    if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) {
+		wepdesc = "The razor-sharp blade";
 		/* not really beheading, but so close, why add another SPFX */
 		if (youattack && u.uswallow && mdef == u.ustuck) {
 		    You("slice %s wide open!", mon_nam(mdef));
-		    *dmgptr = mdef->mhp + FATAL_DAMAGE;
+		    *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
 		    return TRUE;
 		}
 		if (!youdefend) {
@@ -943,19 +958,18 @@
 						mon_nam(mdef));
 				else if (vis)
 					pline("%s cuts deeply into %s!",
-					      Monnam(magr), mon_nam(mdef));
+					      Monnam(magr), hittee);
 				*dmgptr *= 2;
 				return TRUE;
 			}
-			*dmgptr = mdef->mhp + FATAL_DAMAGE;
-			pline_The("razor-sharp blade cuts %s in half!",
-			      mon_nam(mdef));
+			*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
+			pline("%s cuts %s in half!", wepdesc, mon_nam(mdef));
 			otmp->dknown = TRUE;
 			return TRUE;
 		} else {
 			if (bigmonst(youmonst.data)) {
 				pline("%s cuts deeply into you!",
-					Monnam(magr));
+				      magr ? Monnam(magr) : wepdesc);
 				*dmgptr *= 2;
 				return TRUE;
 			}
@@ -965,8 +979,8 @@
 			 * value to the damage so that this reduction in
 			 * damage does not prevent death.
 			 */
-			*dmgptr = (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE;
-			pline_The("razor-sharp blade cuts you in half!");
+			*dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER;
+			pline("%s cuts you in half!", wepdesc);
 			otmp->dknown = TRUE;
 			return TRUE;
 		}
@@ -979,6 +993,7 @@
 
 		if (youattack && u.uswallow && mdef == u.ustuck)
 			return FALSE;
+		wepdesc = artilist[ART_VORPAL_BLADE].name;
 		if (!youdefend) {
 			if (!has_head(mdef->data) || notonhead || u.uswallow) {
 				if (youattack)
@@ -991,32 +1006,32 @@
 				return ((boolean)(youattack || vis));
 			}
 			if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
-				pline("%s slices through %s %s.",
-				      artilist[ART_VORPAL_BLADE].name,
-				      s_suffix(mon_nam(mdef)), mbodypart(mdef,NECK));
+				pline("%s slices through %s %s.", wepdesc,
+				      s_suffix(mon_nam(mdef)),
+				      mbodypart(mdef,NECK));
 				return TRUE;
 			}
-			*dmgptr = mdef->mhp + FATAL_DAMAGE;
+			*dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
 			pline(behead_msg[rn2(SIZE(behead_msg))],
-			      artilist[ART_VORPAL_BLADE].name,
-			      mon_nam(mdef));
+			      wepdesc, mon_nam(mdef));
 			otmp->dknown = TRUE;
 			return TRUE;
 		} else {
 			if (!has_head(youmonst.data)) {
 				pline("Somehow, %s misses you wildly.",
-					mon_nam(magr));
+				      magr ? mon_nam(magr) : wepdesc);
 				*dmgptr = 0;
 				return TRUE;
 			}
 			if (noncorporeal(youmonst.data) || amorphous(youmonst.data)) {
 				pline("%s slices through your %s.",
-				      artilist[ART_VORPAL_BLADE].name, body_part(NECK));
+				      wepdesc, body_part(NECK));
 				return TRUE;
 			}
-			*dmgptr = (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE;
+			*dmgptr = 2 * (Upolyd ? u.mh : u.uhp)
+				  + FATAL_DAMAGE_MODIFIER;
 			pline(behead_msg[rn2(SIZE(behead_msg))],
-			      artilist[ART_VORPAL_BLADE].name, "you");
+			      wepdesc, "you");
 			otmp->dknown = TRUE;
 			/* Should amulets fall off? */
 			return TRUE;
@@ -1036,7 +1051,7 @@
 				      mon_nam(mdef));
 			}
 			if (mdef->m_lev == 0) {
-			    *dmgptr = mdef->mhp + FATAL_DAMAGE;
+			    *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
 			} else {
 			    int drain = rnd(8);
 			    *dmgptr += drain;
@@ -1060,7 +1075,7 @@
 				pline("%s drains your life!",
 				      The(distant_name(otmp, xname)));
 			losexp("life drainage");
-			if (magr->mhp < magr->mhpmax) {
+			if (magr && magr->mhp < magr->mhpmax) {
 			    magr->mhp += (u.uhpmax - oldhpmax)/2;
 			    if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax;
 			}
@@ -1111,17 +1126,19 @@
 
 	switch(oart->inv_prop) {
 	case TAMING: {
-	    struct obj *pseudo = mksobj(SPE_CHARM_MONSTER, FALSE, FALSE);
-	    pseudo->blessed = pseudo->cursed = 0;
-	    pseudo->quan = 20L;			/* do not let useup get it */
-	    (void) seffects(pseudo);
-	    obfree(pseudo, (struct obj *)0);	/* now, get rid of it */
+	    struct obj pseudo;
+
+	    pseudo = zeroobj;	/* neither cursed nor blessed */
+	    pseudo.otyp = SCR_TAMING;
+	    (void) seffects(&pseudo);
 	    break;
 	  }
 	case HEALING: {
 	    int healamt = (u.uhpmax + 1 - u.uhp) / 2;
+	    long creamed = (long)u.ucreamed;
+
 	    if (Upolyd) healamt = (u.mhmax + 1 - u.mh) / 2;
-	    if(healamt || Sick || (Blinded > 1))
+	    if (healamt || Sick || Blinded > creamed)
 		You_feel("better.");
 	    else
 		goto nothing_special;
@@ -1131,7 +1148,7 @@
 	    }
 	    if(Sick) make_sick(0L,(char *)0,FALSE,SICK_ALL);
 	    if(Slimed) Slimed = 0L;
-	    if(Blinded > 1) make_blinded(0L,FALSE);
+	    if (Blinded > creamed) make_blinded(creamed, FALSE);
 	    flags.botl = 1;
 	    break;
 	  }
@@ -1162,8 +1179,10 @@
 		obj->age = 0;
 		return 0;
 	    }
-	    b_effect = obj->blessed && (Role_switch == oart->role || !oart->role);
+	    b_effect = obj->blessed &&
+		(Role_switch == oart->role || !oart->role);
 	    recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0);
+	    update_inventory();
 	    break;
 	  }
 	case LEV_TELE:
@@ -1276,8 +1295,10 @@
 	    else You_feel("the tension decrease around you.");
 	    break;
 	case LEVITATION:
-	    if(on) float_up();
-	    else (void) float_down(I_SPECIAL|TIMEOUT, W_ARTI);
+	    if(on) {
+		float_up();
+		spoteffects(FALSE);
+	    } else (void) float_down(I_SPECIAL|TIMEOUT, W_ARTI);
 	    break;
 	case INVIS:
 	    if (!See_invisible && !Blind) {
@@ -1297,6 +1318,14 @@
 }
 
 
+/* WAC return TRUE if artifact is always lit */
+boolean
+artifact_light(obj)
+    struct obj *obj;
+{
+    return (get_artifact(obj) && obj->oartifact == ART_SUNSWORD);
+}
+
 /* KMH -- Talking artifacts are finally implemented */
 void arti_speak(obj)
     struct obj *obj;
@@ -1313,11 +1342,33 @@
 	line = getrumor(bcsign(obj), buf, TRUE);
 	if (!*line)
 		line = "NetHack rumors file closed for renovation.";
-	pline("%s whispers:", The(xname(obj)));
+	pline("%s:", Tobjnam(obj, "whisper"));
 	verbalize("%s", line);
 	return;
 }
 
+boolean
+artifact_has_invprop(otmp, inv_prop)
+struct obj *otmp;
+uchar inv_prop;
+{
+	const struct artifact *arti = get_artifact(otmp);
+
+	return((boolean)(arti && (arti->inv_prop == inv_prop)));
+}
+
+/* Return the price sold to the hero of a given artifact or unique item */
+long
+arti_cost(otmp)
+struct obj *otmp;
+{
+	if (!otmp->oartifact)
+	    return ((long)objects[otmp->otyp].oc_cost);
+	else if (artilist[(int) otmp->oartifact].cost)
+	    return (artilist[(int) otmp->oartifact].cost);
+	else
+	    return (100L * (long)objects[otmp->otyp].oc_cost);
+}
 
 #endif /* OVLB */
 
diff -Naurd ../nethack-3.3.1/src/attrib.c ./src/attrib.c
--- ../nethack-3.3.1/src/attrib.c Thu May 18 22:19:50 2000
+++ ./src/attrib.c Fri Mar 22 14:40:55 2002
@@ -155,7 +155,7 @@
 		  (incr > 1 || incr < -1) ? "very ": "",
 		  (incr > 0) ? plusattr[ndx] : minusattr[ndx]);
 	flags.botl = 1;
-	if (moves > 0 && (ndx == A_STR || ndx == A_CON))
+	if (moves > 1 && (ndx == A_STR || ndx == A_CON))
 		(void)encumber_msg();
 	return TRUE;
 }
@@ -350,7 +350,11 @@
 
 		if(Sick || Vomiting)     exercise(A_CON, FALSE);
 		if(Confusion || Hallucination)		exercise(A_WIS, FALSE);
-		if(Wounded_legs || Fumbling || HStun)	exercise(A_DEX, FALSE);
+		if((Wounded_legs 
+#ifdef STEED
+		    && !u.usteed
+#endif
+			    ) || Fumbling || HStun)	exercise(A_DEX, FALSE);
 	}
 }
 
diff -Naurd ../nethack-3.3.1/src/ball.c ./src/ball.c
--- ../nethack-3.3.1/src/ball.c Sat Aug 5 00:28:52 2000
+++ ./src/ball.c Fri Mar 22 14:40:55 2002
@@ -345,7 +345,8 @@
 
 /* return TRUE if ball could be dragged
  *
- *  Should not be called while swallowed.
+ *  Should not be called while swallowed.  Should be called before movement,
+ *  because we might want to move the ball or chain to the hero's old position.
  */
 boolean
 drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay)
@@ -368,27 +369,108 @@
 	    return TRUE;
 	}
 
-	if (carried(uball) || dist2(x, y, uball->ox, uball->oy) < 3 ||
-		(uball->ox == uchain->ox && uball->oy == uchain->oy)) {
-	    /*
-	     * Case where the ball doesn't move but the chain can't just move
-	     * to the player's position:
-	     *   @                                             _
-	     *    _    moving southwest becomes  @_  and not  @
-	     *   0                                0            0
-	     */
+	/* only need to move the chain? */
+	if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) {
 	    *bc_control = BC_CHAIN;
 	    move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
-	    if (dist2(x, y, uball->ox, uball->oy) == 2 &&
-		    dist2(x, y, uchain->ox, uchain->oy) == 4) {
-		if (uchain->oy == y)
-		    *chainx = uball->ox;
-		else
-		    *chainy = uball->oy;
-	    } else {
-		*chainx = u.ux;
-		*chainy = u.uy;
+	    if (carried(uball)) {
+		/* move chain only if necessary; assume they didn't teleport */
+		if (distmin(x, y, uchain->ox, uchain->oy) > 1) {
+		    *chainx = u.ux;
+		    *chainy = u.uy;
+		}
+		return TRUE;
+	    }
+#define CHAIN_IN_MIDDLE(chx, chy) \
+(distmin(x, y, chx, chy) <= 1 && distmin(chx, chy, uball->ox, uball->oy) <= 1)
+	    switch(dist2(x, y, uball->ox, uball->oy)) {
+		/* two spaces diagonal from ball, move chain inbetween */
+		case 8:
+		    *chainx = (uball->ox + x)/2;
+		    *chainy = (uball->oy + y)/2;
+		    break;
+
+		/* player is distance 2/1 from ball; move chain to one of the
+		 * two spaces between
+		 *   @
+		 *   __
+		 *    0
+		 */
+		case 5: {
+		    xchar tempx, tempy, tempx2, tempy2;
+
+		    /* find position closest to current position of chain */
+		    /* no effect if current position is already OK */
+		    if (abs(x - uball->ox) == 1) {
+			tempx = x;
+			tempx2 = uball->ox;
+			tempy = tempy2 = (uball->oy + y)/2;
+		    } else {
+			tempx = tempx2 = (uball->ox + x)/2;
+			tempy = y;
+			tempy2 = uball->oy;
+		    }
+		    if (dist2(tempx, tempy, uchain->ox, uchain->oy) <
+			 dist2(tempx2, tempy2, uchain->ox, uchain->oy) ||
+		       ((dist2(tempx, tempy, uchain->ox, uchain->oy) ==
+			 dist2(tempx2, tempy2, uchain->ox, uchain->oy)) && rn2(2))) {
+			*chainx = tempx;
+			*chainy = tempy;
+		    } else {
+			*chainx = tempx2;
+			*chainy = tempy2;
+		    }
+		    break;
+		}
+
+		/* ball is two spaces horizontal or vertical from player; move*/
+		/* chain inbetween *unless* current chain position is OK */
+		case 4:
+		    if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
+			break;
+		    *chainx = (x + uchain->ox)/2;
+		    *chainy = (y + uchain->oy)/2;
+		    break;
+		
+		/* ball is one space diagonal from player.  Check for the
+		 * following special case:
+		 *   @
+		 *    _    moving southwest becomes  @_
+		 *   0                                0
+		 * (This will also catch teleporting that happens to resemble
+		 * this case, but oh well.)  Otherwise fall through.
+		 */
+		case 2:
+		    if (dist2(x, y, uball->ox, uball->oy) == 2 &&
+			    dist2(x, y, uchain->ox, uchain->oy) == 4) {
+			if (uchain->oy == y)
+			    *chainx = uball->ox;
+			else
+			    *chainy = uball->oy;
+			break;
+		    }
+		    /* fall through */
+		case 1:
+		case 0:
+		    /* do nothing if possible */
+		    if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
+			break;
+		    /* otherwise try to drag chain to player's old position */
+		    if (CHAIN_IN_MIDDLE(u.ux, u.uy)) {
+			*chainx = u.ux;
+			*chainy = u.uy;
+			break;
+		    }
+		    /* otherwise use player's new position (they must have
+		       teleported, for this to happen) */
+		    *chainx = x;
+		    *chainy = y;
+		    break;
+		
+		default: impossible("bad chain movement");
+		    break;
 	    }
+#undef CHAIN_IN_MIDDLE
 	    return TRUE;
 	}
 
@@ -559,9 +641,7 @@
 	while (otmp) {
 		nextobj = otmp->nobj;
 		if ((otmp != uball) && (rnd(capacity) <= (int)otmp->owt)) {
-			if (otmp == uwep)
-				setuwep((struct obj *)0);
-			if ((otmp != uwep) && (canletgo(otmp, ""))) {
+			if (canletgo(otmp, "")) {
 				Your("%s you down the stairs.",
 				     aobjnam(otmp, "follow"));
 				dropx(otmp);
diff -Naurd ../nethack-3.3.1/src/bones.c ./src/bones.c
--- ../nethack-3.3.1/src/bones.c Sun Jul 16 02:37:32 2000
+++ ./src/bones.c Fri Mar 22 14:40:58 2002
@@ -126,11 +126,12 @@
 
 	while ((otmp = invent) != 0) {
 		obj_extract_self(otmp);
+		obj_no_longer_held(otmp);
 
 		otmp->owornmask = 0;
 		/* lamps don't go out when dropped */
-		if (cont && obj_is_burning(otmp))	/* smother in statue */
-			end_burn(otmp, otmp->otyp != MAGIC_LAMP);
+		if ((cont || artifact_light(otmp)) && obj_is_burning(otmp))
+		    end_burn(otmp, TRUE);	/* smother in statue */
 
 		if(otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe);
 
@@ -138,17 +139,18 @@
 		if (mtmp)
 			(void) add_to_minv(mtmp, otmp);
 		else if (cont)
-			add_to_container(cont, otmp);
+			(void) add_to_container(cont, otmp);
 		else
 			place_object(otmp, u.ux, u.uy);
 	}
 	if(u.ugold) {
 		long ugold = u.ugold;
 		if (mtmp) mtmp->mgold = ugold;
-		else if (cont) add_to_container(cont, mkgoldobj(ugold));
+		else if (cont) (void) add_to_container(cont, mkgoldobj(ugold));
 		else (void)mkgold(ugold, u.ux, u.uy);
 		u.ugold = ugold;	/* undo mkgoldobj()'s removal */
 	}
+	if (cont) cont->owt = weight(cont);
 }
 
 /* check whether bones are feasible */
@@ -208,7 +210,9 @@
 		return;
 	}
 
+#ifdef WIZARD
  make_bones:
+#endif
 	unleash_all();
 	/* in case these characters are not in their home bases */
 	for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
@@ -220,14 +224,7 @@
 		mongone(mtmp);
 	}
 #ifdef STEED
-	if (u.usteed) {
-	    coord cc;
-
-	    /* Move the steed to an adjacent square */
-	    if (enexto(&cc, u.ux, u.uy, u.usteed->data))
-		rloc_to(u.usteed, cc.x, cc.y);
-	    u.usteed = 0;
-	}
+	if (u.usteed) dismount_steed(DISMOUNT_BONES);
 #endif
 	dmonsfree();		/* discard dead or gone monsters */
 
diff -Naurd ../nethack-3.3.1/src/botl.c ./src/botl.c
--- ../nethack-3.3.1/src/botl.c Tue May 2 14:37:13 2000
+++ ./src/botl.c Fri Mar 22 14:40:58 2002
@@ -248,7 +248,8 @@
 	(void) describe_level(newbot2);
 	Sprintf(nb = eos(newbot2),
 		"%c:%-2ld HP:%d(%d) Pw:%d(%d) AC:%-2d", oc_syms[GOLD_CLASS],
-		u.ugold, hp, hpmax, u.uen, u.uenmax, u.uac);
+		u.ugold,
+		hp, hpmax, u.uen, u.uenmax, u.uac);
 
 	if (Upolyd)
 		Sprintf(nb = eos(nb), " HD:%d", mons[u.umonnum].mlevel);
diff -Naurd ../nethack-3.3.1/src/cmd.c ./src/cmd.c
--- ../nethack-3.3.1/src/cmd.c Sat Jul 22 01:59:03 2000
+++ ./src/cmd.c Fri Mar 22 14:40:55 2002
@@ -14,6 +14,8 @@
 #define NR_OF_EOFS	20
 #endif
 
+#define CMD_TRAVEL (char)0x90
+
 #ifdef DEBUG
 /*
  * only one "wiz_debug_cmd" routine should be available (in whatever
@@ -105,6 +107,7 @@
 STATIC_PTR int NDECL(timed_occupation);
 STATIC_PTR int NDECL(doextcmd);
 STATIC_PTR int NDECL(domonability);
+STATIC_PTR int NDECL(dotravel);
 # ifdef WIZARD
 STATIC_PTR int NDECL(wiz_wish);
 STATIC_PTR int NDECL(wiz_identify);
@@ -112,11 +115,14 @@
 STATIC_PTR int NDECL(wiz_genesis);
 STATIC_PTR int NDECL(wiz_where);
 STATIC_PTR int NDECL(wiz_detect);
+STATIC_PTR int NDECL(wiz_polyself);
 STATIC_PTR int NDECL(wiz_level_tele);
+STATIC_PTR int NDECL(wiz_level_change);
 STATIC_PTR int NDECL(wiz_show_seenv);
 STATIC_PTR int NDECL(wiz_show_vision);
+STATIC_PTR int NDECL(wiz_mon_polycontrol);
 STATIC_PTR int NDECL(wiz_show_wmodes);
-#ifdef __BORLANDC__
+#if defined(__BORLANDC__) && !defined(_WIN32)
 extern void FDECL(show_borlandc_stats, (winid));
 #endif
 STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *, BOOLEAN_P, BOOLEAN_P));
@@ -138,6 +144,8 @@
 #endif
 #endif /* OVLB */
 
+static const char* readchar_queue="";
+
 STATIC_DCL char *NDECL(parse);
 
 #ifdef OVL1
@@ -297,7 +305,7 @@
 	putstr(datawin, 0, "");
 
 	for(efp = extcmdlist; efp->ef_txt; efp++) {
-		Sprintf(buf, "    %-14s  - %s.", efp->ef_txt, efp->ef_desc);
+		Sprintf(buf, "    %-15s - %s.", efp->ef_txt, efp->ef_desc);
 		putstr(datawin, 0, buf);
 	}
 	display_nhwindow(datawin, FALSE);
@@ -433,7 +441,7 @@
 	if (can_breathe(youmonst.data)) return dobreathe();
 	else if (attacktype(youmonst.data, AT_SPIT)) return dospit();
 	else if (youmonst.data->mlet == S_NYMPH) return doremove();
-	else if (youmonst.data->mlet == S_UMBER) return doconfuse();
+	else if (attacktype(youmonst.data, AT_GAZE)) return dogaze();
 	else if (is_were(youmonst.data)) return dosummon();
 	else if (webmaker(youmonst.data)) return dospinweb();
 	else if (is_hider(youmonst.data)) return dohide();
@@ -549,6 +557,61 @@
 }
 
 STATIC_PTR int
+wiz_mon_polycontrol()
+{
+    iflags.mon_polycontrol = !iflags.mon_polycontrol;
+    pline("Monster polymorph control is %s.",
+	  iflags.mon_polycontrol ? "on" : "off");
+    return 0;
+}
+
+STATIC_PTR int
+wiz_level_change()
+{
+    char buf[BUFSZ];
+    int newlevel;
+    int ret;
+
+    getlin("To what experience level do you want to be set?", buf);
+    (void)mungspaces(buf);
+    if (buf[0] == '\033' || buf[0] == '\0') ret = 0;
+    else ret = sscanf(buf, "%d", &newlevel);
+
+    if (ret != 1) {
+	pline(Never_mind);
+	return 0;
+    }
+    if (newlevel == u.ulevel) {
+	You("are already that experienced.");
+    } else if (newlevel < u.ulevel) {
+	if (u.ulevel == 1) {
+	    You("are already as inexperienced as you can get.");
+	    return 0;
+	}
+	if (newlevel < 1) newlevel = 1;
+	while (u.ulevel > newlevel)
+	    losexp((const char *)0);
+    } else {
+	if (u.ulevel >= MAXULEV) {
+	    You("are already as experienced as you can get.");
+	    return 0;
+	}
+	if (newlevel > MAXULEV) newlevel = MAXULEV;
+	while (u.ulevel < newlevel)
+	    pluslvl(FALSE);
+    }
+    u.ulevelmax = u.ulevel;
+    return 0;
+}
+
+STATIC_PTR int
+wiz_polyself()
+{
+        polyself(TRUE);
+        return 0;
+}
+
+STATIC_PTR int
 wiz_show_seenv()
 {
 	winid win;
@@ -639,7 +702,7 @@
 		lev = &levl[x][y];
 		if (x == u.ux && y == u.uy)
 		    row[x] = '@';
-		if (IS_WALL(lev->typ) || lev->typ == SDOOR)
+		else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
 		    row[x] = '0' + (lev->wall_info & WM_MASK);
 		else if (lev->typ == CORR)
 		    row[x] = '#';
@@ -743,6 +806,7 @@
 	if (Stone_resistance)
 		you_are("petrification resistant");
 	if (Invulnerable) you_are("invulnerable");
+	if (u.uedibility) you_can("recognize detrimental food");
 
 	/*** Troubles ***/
 	if (Halluc_resistance)
@@ -758,6 +822,18 @@
 			if (u.usick_type & SICK_NONVOMITABLE)
 				you_are("sick from illness");
 		}
+		/* added by JDS */
+		if (u.uhitinc) {
+			Sprintf(buf, "%s%i %s to hit", u.uhitinc > 0 ? "+" : "",
+				u.uhitinc, u.uhitinc > 0 ? "bonus" : "penalty");
+			you_have(buf);
+			
+		}
+		if (u.udaminc) {
+			Sprintf(buf, "%s%i %s to damage", u.udaminc > 0 ? "+" : "",
+				u.udaminc, u.udaminc > 0 ? "bonus" : "penalty");
+			you_have(buf);
+		} /* end JDS portion */
 	}
 	if (Stoned) you_are("turning to stone");
 	if (Slimed) you_are("turning into slime");
@@ -767,10 +843,22 @@
 		you_have(buf);
 	}
 	if (Fumbling) enl_msg("You fumble", "", "d", "");
-	if (Wounded_legs) {
+	if (Wounded_legs
+#ifdef STEED
+	    && !u.usteed
+#endif
+			  ) {
 		Sprintf(buf, "wounded %s", makeplural(body_part(LEG)));
 		you_have(buf);
 	}
+#if defined(WIZARD) && defined(STEED)
+	if (Wounded_legs && u.usteed && wizard) {
+	    Strcpy(buf, x_monnam(u.usteed, ARTICLE_YOUR, (char *)0, 
+		    SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION, FALSE));
+	    *buf = highc(*buf);
+	    enl_msg(buf, " has", " had", " wounded legs");
+	}
+#endif
 	if (Sleeping) enl_msg("You ", "fall", "fell", " asleep");
 	if (Hunger) enl_msg("You hunger", "", "ed", " rapidly");
 
@@ -790,6 +878,7 @@
 	if (Clairvoyant) you_are("clairvoyant");
 	if (Infravision) you_have("infravision");
 	if (Detect_monsters) you_are("sensing the presence of monsters");
+	if (u.umconf) you_are("going to confuse monsters");
 
 	/*** Appearance and behavior ***/
 	if (Adornment) you_are("adorned");
@@ -959,6 +1048,7 @@
 	anything any;
 	char buf[BUFSZ], buf2[BUFSZ];
 	static char fmtstr[] = "%-15s: %-12s";
+	static char deity_fmtstr[] = "%-17s%s";
 
 	any.a_void = 0;
 	buf[0] = buf2[0] = '\0';
@@ -986,7 +1076,11 @@
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, "Current", FALSE);
 	Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun);
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
-	if (!Upolyd) {
+	if (Upolyd) {
+	    Sprintf(buf, fmtstr, "role (base)",
+		(u.mfemale && urole.name.f) ? urole.name.f : urole.name.m);
+	    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
+	} else {
 	    Sprintf(buf, fmtstr, "role",
 		(flags.female && urole.name.f) ? urole.name.f : urole.name.m);
 	    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
@@ -1006,7 +1100,7 @@
 	/* Deity list */
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE);
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, "Deities", FALSE);
-	Sprintf(buf2, "%-17s%s", align_gname(A_CHAOTIC),
+	Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC),
 	    (u.ualignbase[A_ORIGINAL] == u.ualign.type
 		&& u.ualign.type == A_CHAOTIC) ? " (s,c)" :
 	    (u.ualignbase[A_ORIGINAL] == A_CHAOTIC)       ? " (s)" :
@@ -1014,7 +1108,7 @@
     	Sprintf(buf, fmtstr, "chaotic deity", buf2);
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
 
-	Sprintf(buf2, "%-17s%s", align_gname(A_NEUTRAL),
+	Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL),
 	    (u.ualignbase[A_ORIGINAL] == u.ualign.type
 		&& u.ualign.type == A_NEUTRAL) ? " (s,c)" :
 	    (u.ualignbase[A_ORIGINAL] == A_NEUTRAL)       ? " (s)" :
@@ -1022,7 +1116,7 @@
     	Sprintf(buf, fmtstr, "neutral deity", buf2);
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE);
 
-	Sprintf(buf2, "%-17s%s", align_gname(A_LAWFUL),
+	Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL),
 	    (u.ualignbase[A_ORIGINAL] == u.ualign.type && u.ualign.type == A_LAWFUL)  ? " (s,c)" :
 	    (u.ualignbase[A_ORIGINAL] == A_LAWFUL)        ? " (s)" :
 	    (u.ualign.type   == A_LAWFUL)        ? " (c)" : "");
@@ -1273,6 +1367,7 @@
 	{GOLD_SYM, TRUE, doprgold},
 	{SPBOOK_SYM, TRUE, dovspell},			/* Mike Stephenson */
 	{'#', TRUE, doextcmd},
+	{'_', TRUE, dotravel},
 	{0,0,0,0}
 };
 
@@ -1295,7 +1390,7 @@
 #ifdef STEED
 	{"ride", "ride (or stop riding) a monster", doride, FALSE},
 #endif
-	{"rub", "rub a lamp", dorub, FALSE},
+	{"rub", "rub a lamp or a stone", dorub, FALSE},
 	{"sit", "sit down", dosit, FALSE},
 	{"turn", "turn undead", doturn, TRUE},
 	{"twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE},
@@ -1314,6 +1409,9 @@
 	{(char *)0, (char *)0, donull, TRUE},
 	{(char *)0, (char *)0, donull, TRUE},
 	{(char *)0, (char *)0, donull, TRUE},
+        {(char *)0, (char *)0, donull, TRUE},
+	{(char *)0, (char *)0, donull, TRUE},
+	{(char *)0, (char *)0, donull, TRUE},
 #ifdef DEBUG
 	{(char *)0, (char *)0, donull, TRUE},
 #endif
@@ -1324,7 +1422,10 @@
 
 #if defined(WIZARD)
 static const struct ext_func_tab debug_extcmdlist[] = {
+	{"levelchange", "change experience level", wiz_level_change, TRUE},
 	{"light sources", "show mobile light sources", wiz_light_sources, TRUE},
+	{"monpoly_control", "control monster polymorphs", wiz_mon_polycontrol, TRUE},
+	{"poly", "polymorph self", wiz_polyself, TRUE},
 	{"seenv", "show seen vectors", wiz_show_seenv, TRUE},
 	{"stats", "show memory statistics", wiz_show_stats, TRUE},
 	{"timeout", "look at timeout queue", wiz_timeout_queue, TRUE},
@@ -1531,7 +1632,7 @@
 	Sprintf(buf, template, "Total", total_mon_count, total_mon_size);
 	putstr(win, 0, buf);
 
-#ifdef __BORLANDC__
+#if defined(__BORLANDC__) && !defined(_WIN32)
 	show_borlandc_stats(win);
 #endif
 
@@ -1589,6 +1690,7 @@
 
 	/* handle most movement commands */
 	do_walk = do_rush = prefix_seen = FALSE;
+	flags.travel = 0;
 	switch (*cmd) {
 	 case 'g':  if (movecmd(cmd[1])) {
 			flags.run = 2;
@@ -1635,6 +1737,12 @@
 		    flags.move = FALSE;
 		    multi = 0;
 		    return;
+	 case CMD_TRAVEL:
+		    flags.travel = 1;
+		    flags.run = 8;
+		    flags.nopick = 1;
+		    do_rush = TRUE;
+		    break;
 	 default:   if (movecmd(*cmd)) {	/* ordinary movement */
 			do_walk = TRUE;
 		    } else if (movecmd(iflags.num_pad ?
@@ -1768,8 +1876,8 @@
 {
 	char dirsym;
 
-#ifdef REDO	
-	if(in_doagain)
+#ifdef REDO
+	if(in_doagain || *readchar_queue)
 	    dirsym = readchar();
 	else
 #endif
@@ -1817,34 +1925,85 @@
 /*
  * convert a MAP window position into a movecmd
  */
-int
+const char *
 click_to_cmd(x, y, mod)
     int x, y, mod;
 {
+    int dir;
+    static char cmd[4];
+    cmd[1]=0;
+
     x -= u.ux;
     y -= u.uy;
-    /* convert without using floating point, allowing sloppy clicking */
-    if(x > 2*abs(y))
-	x = 1, y = 0;
-    else if(y > 2*abs(x))
-	x = 0, y = 1;
-    else if(x < -2*abs(y))
-	x = -1, y = 0;
-    else if(y < -2*abs(x))
-	x = 0, y = -1;
-    else
+
+    if ( abs(x) <= 1 && abs(y) <= 1 ) {
 	x = sgn(x), y = sgn(y);
+    } else {
+	u.tx = u.ux+x;
+	u.ty = u.uy+y;
+	cmd[0] = CMD_TRAVEL;
+	return cmd;
+    }
 
-    if(x == 0 && y == 0)	/* map click on player to "rest" command */
-	return '.';
+    if(x == 0 && y == 0) {
+	/* here */
+	if(IS_FOUNTAIN(levl[u.ux][u.uy].typ) || IS_SINK(levl[u.ux][u.uy].typ)) {
+	    cmd[0]=mod == CLICK_1 ? 'q' : M('d');
+	    return cmd;
+	} else if(IS_THRONE(levl[u.ux][u.uy].typ)) {
+	    cmd[0]=M('s');
+	    return cmd;
+	} else if((u.ux == xupstair && u.uy == yupstair)
+		  || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up)
+		  || (u.ux == xupladder && u.uy == yupladder)) {
+	    return "<";
+	} else if((u.ux == xdnstair && u.uy == ydnstair)
+		  || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up)
+		  || (u.ux == xdnladder && u.uy == ydnladder)) {
+	    return ">";
+	} else if(OBJ_AT(u.ux, u.uy)) {
+	    cmd[0] = Is_container(level.objects[u.ux][u.uy]) ? M('l') : ',';
+	    return cmd;
+	} else {
+	    return "."; /* just rest */
+	}
+    }
 
-    x = xytod(x, y);
+    /* directional commands */
+
+    dir = xytod(x, y);
+
+    if (!m_at(u.ux+x, u.uy+y) && !test_move(u.ux, u.uy, x, y, 1)) {
+	cmd[1] = (iflags.num_pad ? ndir[dir] : sdir[dir]);
+	cmd[2] = 0;
+	if (IS_DOOR(levl[u.ux+x][u.uy+y].typ)) {
+	    /* slight assistance to the player: choose kick/open for them */
+	    if (levl[u.ux+x][u.uy+y].doormask & D_LOCKED) {
+		cmd[0] = C('d');
+		return cmd;
+	    }
+	    if (levl[u.ux+x][u.uy+y].doormask & D_CLOSED) {
+		cmd[0] = 'o';
+		return cmd;
+	    }
+	}
+	if (levl[u.ux+x][u.uy+y].typ <= SCORR) {
+	    cmd[0] = 's';
+	    cmd[1] = 0;
+	    return cmd;
+	}
+    }
+
+    /* move, attack, etc. */
+    cmd[1] = 0;
     if(mod == CLICK_1) {
-	return (iflags.num_pad ? ndir[x] : sdir[x]);
+	cmd[0] = (iflags.num_pad ? ndir[dir] : sdir[dir]);
     } else {
-	return (iflags.num_pad ? M(ndir[x]) :
-		(sdir[x] - 'a' + 'A')); /* run command */
+	cmd[0] = (iflags.num_pad ? M(ndir[dir]) :
+		(sdir[dir] - 'a' + 'A')); /* run command */
     }
+
+    return cmd;
 }
 
 STATIC_OVL char *
@@ -1941,10 +2100,13 @@
 	register int sym;
 	int x = u.ux, y = u.uy, mod = 0;
 
+	if ( *readchar_queue )
+	    sym = *readchar_queue++;
+	else
 #ifdef REDO
-	sym = in_doagain ? Getchar() : nh_poskey(&x, &y, &mod);
+	    sym = in_doagain ? Getchar() : nh_poskey(&x, &y, &mod);
 #else
-	sym = Getchar();
+	    sym = Getchar();
 #endif
 
 #ifdef UNIX
@@ -1966,10 +2128,35 @@
 	    end_of_input();
 #endif /* UNIX */
 
-	if(sym == 0) /* click event */
-	    sym = click_to_cmd(x, y, mod);
+	if(sym == 0) {
+	    /* click event */
+	    readchar_queue = click_to_cmd(x, y, mod);
+	    sym = *readchar_queue++;
+	}
 	return((char) sym);
 }
+
+STATIC_PTR int
+dotravel()
+{
+	/* Keyboard travel command */
+	static char cmd[2];
+	coord cc;
+	cmd[1]=0;
+	cc.x = u.ux;
+	cc.y = u.uy;
+	pline("Where do you want to travel to?");
+	if (getpos(&cc, TRUE, "the desired destination") < 0) {
+		/* user pressed ESC */
+		return 0;
+	}
+	u.tx = cc.x;
+	u.ty = cc.y;
+	cmd[0] = CMD_TRAVEL;
+	readchar_queue = cmd;
+	return 0;
+}
+
 #endif /* OVL0 */
 
 /*cmd.c*/
diff -Naurd ../nethack-3.3.1/src/dbridge.c ./src/dbridge.c
--- ../nethack-3.3.1/src/dbridge.c Mon Jul 3 18:33:10 2000
+++ ./src/dbridge.c Fri Mar 22 14:40:55 2002
@@ -336,39 +336,14 @@
 const char *verb;
 {
 	static char wholebuf[80];
-	char verbbuf[30];
 
 	Strcpy(wholebuf, is_u(etmp) ? "You" : Monnam(etmp->emon));
-	if (!*verb)
-		return(wholebuf);
+	if (!*verb) return(wholebuf);
 	Strcat(wholebuf, " ");
-	verbbuf[0] = '\0';
 	if (is_u(etmp))
-		Strcpy(verbbuf, verb);
-	else {
-		if (!strcmp(verb, "are"))
-			Strcpy(verbbuf, "is");
-		if (!strcmp(verb, "have"))
-			Strcpy(verbbuf, "has");
-		if (!verbbuf[0]) {
-			Strcpy(verbbuf, verb);
-			switch (verbbuf[strlen(verbbuf) - 1]) {
-				case 'y':
-					verbbuf[strlen(verbbuf) - 1] = '\0';
-					Strcat(verbbuf, "ies");
-					break;
-				case 'h':
-				case 'o':
-				case 's':
-					Strcat(verbbuf, "es");
-					break;
-				default:
-					Strcat(verbbuf, "s");
-					break;
-			}
-		}
-	}
-	Strcat(wholebuf, verbbuf);
+	    Strcat(wholebuf, verb);
+	else
+	    Strcat(wholebuf, vtense((char *)0, verb));
 	return(wholebuf);
 }
 
diff -Naurd ../nethack-3.3.1/src/decl.c ./src/decl.c
--- ../nethack-3.3.1/src/decl.c Wed May 10 01:50:12 2000
+++ ./src/decl.c Fri Mar 22 14:40:58 2002
@@ -20,7 +20,9 @@
 NEARDATA int bases[MAXOCLASSES] = DUMMY;
 
 NEARDATA int multi = 0;
+#if 0
 NEARDATA int warnlevel = 0;		/* used by movemon and dochugw */
+#endif
 NEARDATA int nroom = 0;
 NEARDATA int nsubroom = 0;
 NEARDATA int occtime = 0;
@@ -70,6 +72,8 @@
 const char ynNaqchars[] = "yn#aq";
 NEARDATA long yn_number = 0L;
 
+const char disclosure_options[] = "iavgc";
+
 #ifdef MICRO
 char hackdir[PATHLEN];		/* where rumors, help, record are */
 char levels[PATHLEN];		/* where levels are */
@@ -97,6 +101,10 @@
 
 NEARDATA schar tbx = 0, tby = 0;	/* mthrowu: target */
 
+/* for xname handling of multiple shot missile volleys:
+   number of shots, index of current one, validity check, shoot vs throw */
+NEARDATA struct multishot m_shot = { 0, 0, STRANGE_OBJECT, FALSE };
+
 NEARDATA struct dig_info digging;
 
 NEARDATA dungeon dungeons[MAXDUNGEON];	/* ini'ed by init_dungeon() */
@@ -184,16 +192,11 @@
 /* used to zero all elements of a struct obj */
 NEARDATA struct obj zeroobj = DUMMY;
 
-/* monster pronouns, index is return value of gender(mtmp) */
-const char *he[3]  = { "he",  "she", "it" };
-const char *him[3] = { "him", "her", "it" };
-const char *his[3] = { "his", "her", "its" };
-
 /* originally from dog.c */
 NEARDATA char dogname[PL_PSIZ] = DUMMY;
 NEARDATA char catname[PL_PSIZ] = DUMMY;
 NEARDATA char horsename[PL_PSIZ] = DUMMY;
-char preferred_pet;	/* '\0', 'c', 'd' */
+char preferred_pet;	/* '\0', 'c', 'd', 'n' (none) */
 /* monsters that went down/up together with @ */
 NEARDATA struct monst *mydogs = (struct monst *)0;
 /* monsters that are moving to another dungeon level */
@@ -208,10 +211,30 @@
 	"white"
 };
 
+const char *c_obj_colors[] = {
+	"black",		/* CLR_BLACK */
+	"red",			/* CLR_RED */
+	"green",		/* CLR_GREEN */
+	"brown",		/* CLR_BROWN */
+	"blue",			/* CLR_BLUE */
+	"magenta",		/* CLR_MAGENTA */
+	"cyan",			/* CLR_CYAN */
+	"gray",			/* CLR_GRAY */
+	"transparent",		/* no_color */
+	"orange",		/* CLR_ORANGE */
+	"bright green",		/* CLR_BRIGHT_GREEN */
+	"yellow",		/* CLR_YELLOW */
+	"bright blue",		/* CLR_BRIGHT_BLUE */
+	"bright magenta",	/* CLR_BRIGHT_MAGENTA */
+	"bright cyan",		/* CLR_BRIGHT_CYAN */
+	"white",		/* CLR_WHITE */
+};
+
 struct c_common_strings c_common_strings = {
 	"Nothing happens.",		"That's enough tries!",
 	"That is a silly thing to %s.",	"shudder for a moment.",
-	"something", "Something", "You can move again.", "Never mind."
+	"something", "Something", "You can move again.", "Never mind.",
+	"vision quickly clears."
 };
 
 /* NOTE: the order of these words exactly corresponds to the
diff -Naurd ../nethack-3.3.1/src/detect.c ./src/detect.c
--- ../nethack-3.3.1/src/detect.c Sat Aug 5 00:29:31 2000
+++ ./src/detect.c Fri Mar 22 14:40:59 2002
@@ -13,8 +13,8 @@
 extern boolean known;	/* from read.c */
 
 STATIC_DCL void FDECL(do_dknown_of, (struct obj *));
-STATIC_DCL boolean FDECL(check_map_spot, (int,int,CHAR_P));
-STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P));
+STATIC_DCL boolean FDECL(check_map_spot, (int,int,CHAR_P,unsigned));
+STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P,unsigned));
 STATIC_DCL void FDECL(sense_trap, (struct trap *,XCHAR_P,XCHAR_P,int));
 STATIC_DCL void FDECL(show_map_spot, (int,int));
 STATIC_PTR void FDECL(findone,(int,int,genericptr_t));
@@ -40,6 +40,26 @@
     return (struct obj *) 0;
 }
 
+/* Recursively search obj for an object made of specified material and return 1st found */
+struct obj *
+o_material(obj, material)
+struct obj* obj;
+unsigned material;
+{
+    register struct obj* otmp;
+    struct obj *temp;
+
+    if (objects[obj->otyp].oc_material == material) return obj;
+
+    if (Has_contents(obj)) {
+	for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
+	    if (objects[otmp->otyp].oc_material == material) return otmp;
+	    else if (Has_contents(otmp) && (temp = o_material(otmp, material)))
+		return temp;
+    }
+    return (struct obj *) 0;
+}
+
 STATIC_OVL void
 do_dknown_of(obj)
 struct obj *obj;
@@ -55,9 +75,10 @@
 
 /* Check whether the location has an outdated object displayed on it. */
 STATIC_OVL boolean
-check_map_spot(x, y, oclass)
+check_map_spot(x, y, oclass, material)
 int x, y;
 register char oclass;
+unsigned material;
 {
 	register int glyph;
 	register struct obj *otmp;
@@ -69,20 +90,36 @@
 	    if (oclass == ALL_CLASSES) {
 		return((boolean)( !(level.objects[x][y] ||     /* stale if nothing here */
 			    ((mtmp = m_at(x,y)) != 0 &&
-				(mtmp->mgold || mtmp->minvent)))));
-	    } else if (objects[glyph_to_obj(glyph)].oc_class == oclass) {
-		/* the object shown here is of interest */
-		for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
-		    if (o_in(otmp, oclass)) return FALSE;
-		/* didn't find it; perhaps a monster is carrying it */
-		if ((mtmp = m_at(x,y)) != 0) {
-		    if (oclass == GOLD_CLASS && mtmp->mgold)
-			return FALSE;
-		    else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
-			    if (o_in(otmp, oclass)) return FALSE;
+				(
+				 mtmp->mgold ||
+						 mtmp->minvent)))));
+	    } else {
+		if (material && objects[glyph_to_obj(glyph)].oc_material == material) {
+			/* the object shown here is of interest because material matches */
+			for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
+				if (o_material(otmp, GOLD)) return FALSE;
+			/* didn't find it; perhaps a monster is carrying it */
+			if ((mtmp = m_at(x,y)) != 0) {
+				for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
+					if (o_material(otmp, GOLD)) return FALSE;
+		        }
+			/* detection indicates removal of this object from the map */
+			return TRUE;
 		}
-		/* detection indicates removal of this object from the map */
-		return TRUE;
+	        if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
+			/* the object shown here is of interest because its class matches */
+			for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
+				if (o_in(otmp, oclass)) return FALSE;
+			/* didn't find it; perhaps a monster is carrying it */
+			if ((mtmp = m_at(x,y)) != 0) {
+				if (oclass == GOLD_CLASS && mtmp->mgold)
+					return FALSE;
+				else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
+					if (o_in(otmp, oclass)) return FALSE;
+		        }
+			/* detection indicates removal of this object from the map */
+			return TRUE;
+	        }
 	    }
 	}
 	return FALSE;
@@ -95,15 +132,16 @@
    change occurs.
  */
 STATIC_OVL boolean
-clear_stale_map(oclass)
+clear_stale_map(oclass, material)
 register char oclass;
+unsigned material;
 {
 	register int zx, zy;
 	register boolean change_made = FALSE;
 
 	for (zx = 1; zx < COLNO; zx++)
 	    for (zy = 0; zy < ROWNO; zy++)
-		if (check_map_spot(zx, zy, oclass)) {
+		if (check_map_spot(zx, zy, oclass,material)) {
 		    unmap_object(zx, zy);
 		    change_made = TRUE;
 		}
@@ -122,7 +160,8 @@
     struct obj *temp;
     boolean stale;
 
-    known = stale = clear_stale_map(GOLD_CLASS);
+    known = stale = clear_stale_map(GOLD_CLASS,
+				(unsigned)(sobj->blessed ? GOLD : 0));
 
     /* look for gold carried by monsters (might be in a container) */
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
@@ -131,22 +170,42 @@
 	    known = TRUE;
 	    goto outgoldmap;	/* skip further searching */
 	} else for (obj = mtmp->minvent; obj; obj = obj->nobj)
-	    if (o_in(obj, GOLD_CLASS)) {
+	    if (sobj->blessed && o_material(obj, GOLD)) {
+	    	known = TRUE;
+	    	goto outgoldmap;
+	    } else if (o_in(obj, GOLD_CLASS)) {
 		known = TRUE;
 		goto outgoldmap;	/* skip further searching */
 	    }
     }
     
     /* look for gold objects */
-    for (obj = fobj; obj; obj = obj->nobj)
-	if (o_in(obj, GOLD_CLASS)) {
+    for (obj = fobj; obj; obj = obj->nobj) {
+	if (sobj->blessed && o_material(obj, GOLD)) {
+	    known = TRUE;
+	    if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
+	} else if (o_in(obj, GOLD_CLASS)) {
 	    known = TRUE;
 	    if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
 	}
+    }
 
     if (!known) {
-	/* no gold found */
-	if (sobj) strange_feeling(sobj, "You feel materially poor.");
+	/* no gold found on floor or monster's inventory.
+	   adjust message if you have gold in your inventory */
+	if (sobj) {
+		char buf[BUFSZ];
+		if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
+			Sprintf(buf, "You feel like a million %s!",
+				currency(2L));
+		} else if (hidden_gold() ||
+				u.ugold)
+			Strcpy(buf,
+				"You feel worried about your future financial situation.");
+		else
+			Strcpy(buf, "You feel materially poor.");
+		strange_feeling(sobj, buf);
+        }
 	return(1);
     }
     /* only under me - no separate display required */
@@ -159,14 +218,21 @@
 
     u.uinwater = 0;
     /* Discover gold locations. */
-    for (obj = fobj; obj; obj = obj->nobj)
-	if ((temp = o_in(obj, GOLD_CLASS))) {
+    for (obj = fobj; obj; obj = obj->nobj) {
+    	if (sobj->blessed && (temp = o_material(obj, GOLD))) {
+	    if (temp != obj) {
+		temp->ox = obj->ox;
+		temp->oy = obj->oy;
+	    }
+	    map_object(temp,1);
+	} else if ((temp = o_in(obj, GOLD_CLASS))) {
 	    if (temp != obj) {
 		temp->ox = obj->ox;
 		temp->oy = obj->oy;
 	    }
 	    map_object(temp,1);
 	}
+    }
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
     	if (DEADMONSTER(mtmp)) continue;	/* probably overkill here */
 	if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
@@ -177,7 +243,12 @@
 	    gold.oy = mtmp->my;
 	    map_object(&gold,1);
 	} else for (obj = mtmp->minvent; obj; obj = obj->nobj)
-	    if ((temp = o_in(obj, GOLD_CLASS))) {
+	    if (sobj->blessed && (temp = o_material(obj, GOLD))) {
+		temp->ox = mtmp->mx;
+		temp->oy = mtmp->my;
+		map_object(temp,1);
+		break;
+	    } else if ((temp = o_in(obj, GOLD_CLASS))) {
 		temp->ox = mtmp->mx;
 		temp->oy = mtmp->my;
 		map_object(temp,1);
@@ -210,7 +281,7 @@
     const char *what = confused ? something : "food";
     int uw = u.uinwater;
 
-    stale = clear_stale_map(oclass);
+    stale = clear_stale_map(oclass, 0);
 
     for (obj = fobj; obj; obj = obj->nobj)
 	if (o_in(obj, oclass)) {
@@ -231,12 +302,31 @@
 	if (stale) {
 	    docrt();
 	    You("sense a lack of %s nearby.", what);
-	} else if (sobj)
-	    strange_feeling(sobj, "Your nose twitches.");
+	    if (sobj && sobj->blessed) {
+		if (!u.uedibility) Your("%s starts to tingle.", body_part(NOSE));
+		u.uedibility = 1;
+	    }
+	} else if (sobj) {
+	    char buf[BUFSZ];
+	    Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
+			(sobj->blessed && !u.uedibility) ? " then starts to tingle" : "");
+	    if (sobj->blessed && !u.uedibility) {
+		boolean savebeginner = flags.beginner;	/* prevent non-delivery of */
+		flags.beginner = FALSE;			/* 	message            */
+		strange_feeling(sobj, buf);
+		flags.beginner = savebeginner;
+		u.uedibility = 1;
+	    } else
+		strange_feeling(sobj, buf);
+	}
 	return !stale;
     } else if (!ct) {
 	known = TRUE;
 	You("%s %s nearby.", sobj ? "smell" : "sense", what);
+	if (sobj && sobj->blessed) {
+		if (!u.uedibility) pline("Your %s starts to tingle.", body_part(NOSE));
+		u.uedibility = 1;
+	}
     } else {
 	struct obj *temp;
 	known = TRUE;
@@ -260,7 +350,14 @@
 		    break;	/* skip rest of this monster's inventory */
 		}
 	newsym(u.ux,u.uy);
-	if (sobj) Your("nose tingles and you smell %s.", what);
+	if (sobj) {
+	    if (sobj->blessed) {
+	    	Your("%s %s to tingle and you smell %s.", body_part(NOSE),
+	    		u.uedibility ? "continues" : "starts", what);
+		u.uedibility = 1;
+	    } else
+		Your("%s tingles and you smell %s.", body_part(NOSE), what);
+	}
 	else You("sense %s.", what);
 	display_nhwindow(WIN_MAP, TRUE);
 	exercise(A_WIS, TRUE);
@@ -335,7 +432,7 @@
 	}
     }
 
-    if (!clear_stale_map(!class ? ALL_CLASSES : class) && !ct) {
+    if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
 	if (!ctu) {
 	    if (detector)
 		strange_feeling(detector, "You feel a lack of something.");
@@ -551,10 +648,10 @@
 	    else found = TRUE;
 	}
     }
-    for (door = 0; door <= doorindex; door++) {
+    for (door = 0; door < doorindex; door++) {
 	cc = doors[door];
 	if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
-	    if (cc.x != u.ux || cc.x != u.uy)
+	    if (cc.x != u.ux || cc.y != u.uy)
 		goto outtrapmap;
 	    else found = TRUE;
 	}
@@ -579,7 +676,7 @@
 	if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped)
 	sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed);
 
-    for (door = 0; door <= doorindex; door++) {
+    for (door = 0; door < doorindex; door++) {
 	cc = doors[door];
 	if (levl[cc.x][cc.y].doormask & D_TRAPPED)
 	sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed);
@@ -643,32 +740,32 @@
 {
     char ch;
     int oops;
-    const char *bname = xname(obj);
 
     if (Blind) {
-	pline("Too bad you can't see %s", the(bname));
+	pline("Too bad you can't see %s.", the(xname(obj)));
 	return;
     }
     oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
     if (oops && (obj->spe > 0)) {
 	switch (rnd(obj->oartifact ? 4 : 5)) {
-	case 1 : pline("%s is too much to comprehend!", The(bname));
+	case 1 : pline("%s too much to comprehend!", Tobjnam(obj, "are"));
 	    break;
-	case 2 : pline("%s confuses you!", The(bname));
+	case 2 : pline("%s you!", Tobjnam(obj, "confuse"));
 	    make_confused(HConfusion + rnd(100),FALSE);
 	    break;
 	case 3 : if (!resists_blnd(&youmonst)) {
-		pline("%s damages your vision!", The(bname));
+		pline("%s your vision!", Tobjnam(obj, "damage"));
 		make_blinded(Blinded + rnd(100),FALSE);
+		if (!Blind) Your(vision_clears);
 	    } else {
-		pline("%s assaults your vision.", The(bname));
+		pline("%s your vision.", Tobjnam(obj, "assault"));
 		You("are unaffected!");
 	    }
 	    break;
-	case 4 : pline("%s zaps your mind!", The(bname));
+	case 4 : pline("%s your mind!", Tobjnam(obj, "zap"));
 	    make_hallucinated(HHallucination + rnd(100),FALSE,0L);
 	    break;
-	case 5 : pline("%s explodes!", The(bname));
+	case 5 : pline("%s!", Tobjnam(obj, "explode"));
 	    useup(obj);
 	    losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN);
 	    break;
@@ -711,7 +808,7 @@
 	if (flags.verbose) pline(Never_mind);
 	return;
     }
-    You("peer into %s...", the(bname));
+    You("peer into %s...", the(xname(obj)));
     nomul(-rnd(10));
     nomovemsg = "";
     if (obj->spe <= 0)
@@ -853,10 +950,13 @@
 
 	if(levl[zx][zy].typ == SDOOR) {
 		cvt_sdoor_to_door(&levl[zx][zy]);	/* .typ = DOOR */
+		magic_map_background(zx, zy, 0);
 		newsym(zx, zy);
 		(*(int*)num)++;
 	} else if(levl[zx][zy].typ == SCORR) {
 		levl[zx][zy].typ = CORR;
+		unblock_point(zx,zy);
+		magic_map_background(zx, zy, 0);
 		newsym(zx, zy);
 		(*(int*)num)++;
 	} else if ((ttmp = t_at(zx, zy)) != 0) {
@@ -918,10 +1018,12 @@
 		    levl[zx][zy].doormask = D_NODOOR;
 		} else
 		    levl[zx][zy].doormask = D_ISOPEN;
+		unblock_point(zx, zy);
 		newsym(zx, zy);
 		(*(int*)num)++;
 	} else if(levl[zx][zy].typ == SCORR) {
 		levl[zx][zy].typ = CORR;
+		unblock_point(zx, zy);
 		newsym(zx, zy);
 		(*(int*)num)++;
 	} else if ((ttmp = t_at(zx, zy)) != 0) {
@@ -970,14 +1072,29 @@
 struct trap *trap;
 {
     int tt = what_trap(trap->ttyp);
+    boolean cleared = FALSE;
 
-    You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
     trap->tseen = 1;
     exercise(A_WIS, TRUE);
     if (Blind)
 	feel_location(trap->tx, trap->ty);
     else
 	newsym(trap->tx, trap->ty);
+
+    if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
+    	/* There's too much clutter to see your find otherwise */
+	cls();
+	map_trap(trap, 1);
+	display_self();
+	cleared = TRUE;
+    }
+
+    You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
+
+    if (cleared) {
+	display_nhwindow(WIN_MAP, TRUE);	/* wait */
+	docrt();
+    }
 }
 
 int
@@ -1003,7 +1120,10 @@
 	} else {
 	    int fund = (uwep && uwep->oartifact &&
 		    spec_ability(uwep, SPFX_SEARCH)) ?
-			((uwep->spe > 5) ? 5 : uwep->spe) : 0;
+		    uwep->spe : 0;
+	    if (ublindf && ublindf->otyp == LENSES && !Blind)
+		    fund += 2; /* JDS: lenses help searching */
+	    if (fund > 5) fund = 5;
 	    for(x = u.ux-1; x < u.ux+2; x++)
 	      for(y = u.uy-1; y < u.uy+2; y++) {
 		if(!isok(x,y)) continue;
@@ -1045,18 +1165,17 @@
 					You_feel("an unseen monster!");
 					map_invisible(x, y);
 				    }
-				} else
+				} else if (!sensemon(mtmp))
 				    You("find %s.", a_monnam(mtmp));
 				return(1);
 			    }
-			    if(mtmp->mundetected &&
-			(is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
-				mtmp->mundetected = 0;
+			    if(!canspotmon(mtmp)) {
+				if (mtmp->mundetected &&
+				   (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
+					mtmp->mundetected = 0;
 				newsym(x,y);
 				goto find;
 			    }
-			    if (!canspotmon(mtmp))
-				goto find;
 			}
 
 			/* see if an invisible monster has moved--if Blind,
diff -Naurd ../nethack-3.3.1/src/dig.c ./src/dig.c
--- ../nethack-3.3.1/src/dig.c Mon Jul 17 01:16:27 2000
+++ ./src/dig.c Fri Mar 22 14:40:55 2002
@@ -134,6 +134,15 @@
 		IS_TREE(levl[x][y].typ) ? 4: 0);
 }
 
+boolean
+is_digging()
+{
+	if (occupation == dig) {
+	    return TRUE;
+	}
+	return FALSE;
+}
+
 #define BY_YOU		(&youmonst)
 #define BY_OBJECT	((struct monst *)0)
 
@@ -211,24 +220,34 @@
 	    }
 	}
 	if(Fumbling && !rn2(3)) {
-		switch(rn2(3)) {
-		case 0:  if(!welded(uwep)) {
-			     You("fumble and drop your %s.", xname(uwep));
-			     dropx(uwep);
-			     setuwep((struct obj *)0);
-			 } else {
-			     pline("Ouch!  Your %s bounces and hits you!",
-				xname(uwep));
-			     set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
-			 }
-			 break;
-		case 1:  pline("Bang!  You hit with the broad side of %s!",
-			       the(xname(uwep)));
-			 break;
-		default: Your("swing misses its mark.");
-			 break;
+	    switch(rn2(3)) {
+	    case 0:
+		if(!welded(uwep)) {
+		    You("fumble and drop your %s.", xname(uwep));
+		    dropx(uwep);
+		} else {
+#ifdef STEED
+		    if (u.usteed)
+			Your("%s %s and %s %s!",
+			     xname(uwep),
+			     otense(uwep, "bounce"), otense(uwep, "hit"),
+			     mon_nam(u.usteed));
+		    else
+#endif
+			pline("Ouch!  Your %s %s and %s you!",
+			      xname(uwep),
+			      otense(uwep, "bounce"), otense(uwep, "hit"));
+		    set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
 		}
-		return(0);
+		break;
+	    case 1:
+		pline("Bang!  You hit with the broad side of %s!",
+		      the(xname(uwep)));
+		break;
+	    default: Your("swing misses its mark.");
+		break;
+	    }
+	    return(0);
 	}
 
 	digging.effort += 10 + rn2(5) + abon() +
@@ -276,7 +295,14 @@
 				 */
 				digtxt = (char *)0;
 		} else if ((obj = sobj_at(BOULDER, dpx, dpy)) != 0) {
+			struct obj *bobj;
+
 			fracture_rock(obj);
+			if ((bobj = sobj_at(BOULDER, dpx, dpy)) != 0) {
+			    /* another boulder here, restack it to the top */
+			    obj_extract_self(bobj);
+			    place_object(bobj, dpx, dpy);
+			}
 			digtxt = "The boulder falls apart.";
 		} else if (lev->typ == STONE || lev->typ == SCORR ||
 				IS_TREE(lev->typ)) {
@@ -326,12 +352,13 @@
 				lev->doormask = D_BROKEN;
 		} else return(0); /* statue or boulder got taken */
 
-		unblock_point(dpx,dpy);	/* vision:  can see through */
+		if(!does_block(dpx,dpy,&levl[dpx][dpy]))
+		    unblock_point(dpx,dpy);	/* vision:  can see through */
 		if(Blind)
 		    feel_location(dpx, dpy);
 		else
 		    newsym(dpx, dpy);
-		if(digtxt) pline(digtxt);	/* after newsym */
+		if(digtxt && !digging.quiet) pline(digtxt); /* after newsym */
 		if(dmgtxt)
 		    pay_for_damage(dmgtxt);
 
@@ -356,6 +383,8 @@
 			newsym(dpx, dpy);
 		}
 cleanup:
+		digging.lastdigtime = moves;
+		digging.quiet = FALSE;
 		digging.level.dnum = 0;
 		digging.level.dlevel = -1;
 		return(0);
@@ -462,7 +491,12 @@
 	    ttyp = PIT;
 	}
 
-	Strcpy(surface_type, surface(x,y));	/* maketrap() might change it */
+	/* maketrap() might change it, also, in this situation,
+	   surface() returns an inappropriate string for a grave */
+	if (IS_GRAVE(lev->typ))
+	    Strcpy(surface_type, "grave");
+	else
+	    Strcpy(surface_type, surface(x,y));
 	shopdoor = IS_DOOR(lev->typ) && *in_rooms(x, y, SHOPBASE);
 	oldobjs = level.objects[x][y];
 	ttmp = maketrap(x, y, ttyp);
@@ -746,9 +780,8 @@
 	char dirsyms[12];
 	char qbuf[QBUFSZ];
 	register char *dsp = dirsyms;
-	register struct rm *lev;
 	register int rx, ry;
-	int dig_target, res = 0;
+	int res = 0;
 	register const char *sdp;
 	if(iflags.num_pad) sdp = ndir; else sdp = sdir;	/* DICE workaround */
 
@@ -777,6 +810,22 @@
 	Sprintf(qbuf, "In what direction do you want to dig? [%s]", dirsyms);
 	if(!getdir(qbuf))
 		return(res);
+
+	return(use_pick_axe2(obj));
+}
+
+/* MRKR: use_pick_axe() is split in two to allow autodig to bypass */
+/*       the "In what direction do you want to dig?" query.        */
+/*       use_pick_axe2() uses the existing u.dx, u.dy and u.dz    */
+
+int
+use_pick_axe2(obj) 
+struct obj *obj;
+{
+	register int rx, ry;
+	register struct rm *lev;
+	int dig_target;
+
 	if (u.uswallow && attack(u.ustuck)) {
 		;  /* return(1) */
 	} else if (Underwater) {
@@ -793,10 +842,9 @@
 		dam = rnd(2) + dbon() + obj->spe;
 		if (dam <= 0) dam = 1;
 		You("hit yourself with %s.", yname(uwep));
-		/* self_pronoun() won't work twice in a sentence */
-		Strcpy(buf, self_pronoun("killed %sself with %%s pick-axe",
-			"him"));
-		losehp(dam, self_pronoun(buf, "his"), NO_KILLER_PREFIX);
+		Sprintf(buf, "%s own %s", uhis(),
+				OBJ_NAME(objects[obj->otyp]));
+		losehp(dam, buf, KILLED_BY);
 		flags.botl=1;
 		return(1);
 	} else if(u.dz == 0) {
@@ -820,8 +868,8 @@
 				seetrap(trap);
 				There("is a spider web there!");
 			    }
-			    Your("%s becomes entangled in the web.",
-				aobjnam(obj, (char *)0));
+			    Your("%s entangled in the web.",
+				aobjnam(obj, "become"));
 			    /* you ought to be able to let go; tough luck */
 			    /* (maybe `move_into_trap()' would be better) */
 			    nomul(-d(2,2));
@@ -837,20 +885,32 @@
 						"chopping at the door",
 						"cutting the tree"
 			};
+			did_dig_msg = FALSE;
+			digging.quiet = FALSE;
 			if (digging.pos.x != rx || digging.pos.y != ry ||
 			    !on_level(&digging.level, &u.uz) || digging.down) {
+			    if (flags.autodig && !dig_target && !digging.down &&
+				digging.pos.x == u.ux &&
+				digging.pos.y == u.uy &&
+				(moves <= digging.lastdigtime+2 &&
+				 moves >= digging.lastdigtime)) {
+				/* avoid messages if repeated autodigging */
+				did_dig_msg = TRUE;
+				digging.quiet = TRUE;
+			    }
 			    digging.down = digging.chew = FALSE;
+			    digging.warned = FALSE;
 			    digging.pos.x = rx;
 			    digging.pos.y = ry;
 			    assign_level(&digging.level, &u.uz);
 			    digging.effort = 0;
-			    You("start %s.", d_action[dig_target]);
+			    if (!digging.quiet)
+				You("start %s.", d_action[dig_target]);
 			} else {
 			    You("%s %s.", digging.chew ? "begin" : "continue",
 					d_action[dig_target]);
 			    digging.chew = FALSE;
 			}
-			did_dig_msg = FALSE;
 			set_occupation(dig, "digging", 0);
 		}
 	} else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
@@ -867,6 +927,7 @@
 			!on_level(&digging.level, &u.uz) || !digging.down) {
 		    digging.chew = FALSE;
 		    digging.down = TRUE;
+		    digging.warned = FALSE;
 		    digging.pos.x = u.ux;
 		    digging.pos.y = u.uy;
 		    assign_level(&digging.level, &u.uz);
@@ -881,6 +942,56 @@
 	return(1);
 }
 
+/*
+ * Town Watchmen frown on damage to the town walls or fountains.
+ * It's OK to dig holes in the ground, however.
+ * If mtmp is assumed to be a watchman, a watchman is found if mtmp == 0
+ * zap == TRUE if wand/spell of digging, FALSE otherwise (chewing)
+ */
+void
+watch_dig(mtmp, x, y, zap)
+    struct monst *mtmp;
+    xchar x, y;
+    boolean zap;
+{
+	s_level *slev = Is_special(&u.uz);
+	struct rm *lev = &levl[x][y];
+
+	if (slev && slev->flags.town &&
+	    (closed_door(x, y) || lev->typ == SDOOR ||
+	     IS_WALL(lev->typ) || IS_FOUNTAIN(lev->typ))) {
+	    if (!mtmp) {
+		for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+		    if (DEADMONSTER(mtmp)) continue;
+		    if ((mtmp->data == &mons[PM_WATCHMAN] ||
+			 mtmp->data == &mons[PM_WATCH_CAPTAIN]) &&
+			mtmp->mcansee && m_canseeu(mtmp) &&
+			couldsee(mtmp->mx, mtmp->my) && mtmp->mpeaceful)
+			break;
+		}
+	    }
+
+	    if (mtmp) {
+		if(zap || digging.warned) {
+		    verbalize("Halt, vandal!  You're under arrest!");
+		    (void) angry_guards(!(flags.soundok));
+		} else {
+		    char *str;
+		    if (IS_DOOR(lev->typ))
+			str = "door";
+		    else if (IS_WALL(lev->typ))
+			str = "wall";
+		    else
+			str = "fountain";
+		    verbalize("Hey, stop damaging that %s!", str);
+		    digging.warned = TRUE;
+		}
+		if (is_digging())
+		    stop_occupation();
+	    }
+	}
+}
+
 #endif /* OVLB */
 #ifdef OVL0
 
@@ -949,7 +1060,7 @@
 
 	if (pile && pile < 5)   /* leave behind some rocks? */
 	    (void) mksobj_at((pile == 1) ? BOULDER : ROCK,
-			     mtmp->mx, mtmp->my, TRUE);
+			     mtmp->mx, mtmp->my, TRUE, FALSE);
 	newsym(mtmp->mx, mtmp->my);
 	if (!sobj_at(BOULDER, mtmp->mx, mtmp->my))
 	    unblock_point(mtmp->mx, mtmp->my);	/* vision */
@@ -983,7 +1094,8 @@
 
 	    if (!is_whirly(mtmp->data)) {
 		if (is_animal(mtmp->data))
-		    You("pierce %s stomach wall!", s_suffix(mon_nam(mtmp)));
+		    You("pierce %s %s wall!",
+			s_suffix(mon_nam(mtmp)), mbodypart(mtmp, STOMACH));
 		mtmp->mhp = 1;		/* almost dead */
 		expels(mtmp, mtmp->data, !is_animal(mtmp->data));
 	    }
@@ -1001,12 +1113,14 @@
 		    pline("It falls on your %s!", body_part(HEAD));
 		    losehp(rnd((uarmh && is_metallic(uarmh)) ? 2 : 6),
 			   "falling rock", KILLED_BY_AN);
-		    if ((otmp = mksobj_at(ROCK, u.ux, u.uy, FALSE)) != 0) {
+		    otmp = mksobj_at(ROCK, u.ux, u.uy, FALSE, FALSE);
+		    if (otmp) {
 			(void)xname(otmp);	/* set dknown, maybe bknown */
 			stackobj(otmp);
 		    }
 		    if (Invisible) newsym(u.ux, u.uy);
 		} else {
+		    watch_dig((struct monst *)0, u.ux, u.uy, TRUE);
 		    (void) dighole(FALSE);
 		}
 	    }
@@ -1034,6 +1148,7 @@
 		    room->typ = DOOR;
 		else if (cansee(zx, zy))
 		    pline_The("door is razed!");
+		watch_dig((struct monst *)0, zx, zy, TRUE);
 		room->doormask = D_NODOOR;
 		unblock_point(zx,zy); /* vision */
 		digdepth -= 2;
@@ -1065,6 +1180,7 @@
 			add_damage(zx, zy, 200L);
 			shopwall = TRUE;
 		    }
+		    watch_dig((struct monst *)0, zx, zy, TRUE);
 		    if (level.flags.is_cavernous_lev) {
 			room->typ = CORR;
 		    } else {
@@ -1232,10 +1348,12 @@
 	    x = obj->ox;
 	    y = obj->oy;
 	} else if (in_invent) {
-	    if (flags.verbose)
-		Your("%s%s rot%s away%c",
-		     obj == uwep ? "wielded " : "", corpse_xname(obj, FALSE),
-		     obj->quan == 1L ? "s" : "", obj == uwep ? '!' : '.');
+	    if (flags.verbose) {
+		char *cname = corpse_xname(obj, FALSE);
+		Your("%s%s %s away%c",
+		     obj == uwep ? "wielded " : nul, cname,
+		     vtense(cname, "rot"), obj == uwep ? '!' : '.');
+	    }
 	    if (obj == uwep) {
 		uwepgone();	/* now bare handed */
 		stop_occupation();
@@ -1248,7 +1366,7 @@
 	    }
 	} else if (obj->where == OBJ_MINVENT && obj->owornmask) {
 	    if (obj == MON_WEP(obj->ocarry)) {
-		obj->owornmask &= ~W_WEP;
+		setmnotwielded(obj->ocarry,obj);
 		MON_NOWEP(obj->ocarry);
 	    }
 	}
diff -Naurd ../nethack-3.3.1/src/display.c ./src/display.c
--- ../nethack-3.3.1/src/display.c Thu Aug 3 20:50:51 2000
+++ ./src/display.c Fri Mar 22 14:40:55 2002
@@ -337,6 +337,9 @@
     _map_location(x,y,show);
 }
 
+#define DETECTED 	2
+#define PHYSICALLY_SEEN 1
+#define is_worm_tail(mon)	((mon) && ((x != (mon)->mx)  || (y != (mon)->my)))
 
 /*
  * display_monster()
@@ -350,10 +353,11 @@
  *
  */
 STATIC_OVL void
-display_monster(x, y, mon, in_sight, worm_tail)
+display_monster(x, y, mon, sightflags, worm_tail)
     register xchar x, y;	/* display position */
     register struct monst *mon;	/* monster to display */
-    int in_sight;		/* TRUE if the monster is physically seen */
+    int sightflags;		/* 1 if the monster is physically seen */
+    				/* 2 if detected using Detect_monsters */
     register xchar worm_tail;	/* mon is actually a worm tail */
 {
     register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING);
@@ -366,7 +370,7 @@
      * the mimic was mimicing.
      */
 
-    if (mon_mimic && in_sight) {
+    if (mon_mimic && sightflags) {
 	switch (mon->m_ap_type) {
 	    default:
 		impossible("display_monster:  bad m_ap_type value [ = %d ]",
@@ -403,7 +407,7 @@
 	    }
 
 	    case M_AP_MONSTER:
-		show_glyph(x,y, monnum_to_glyph(what_mon(mon->mappearance)));
+		show_glyph(x,y, monnum_to_glyph(what_mon((int)mon->mappearance)));
 		break;
 	}
 	
@@ -413,7 +417,10 @@
     if (!mon_mimic || sensed) {
 	int num;
 
-	if (Detect_monsters) {
+	/* [ALI] Only use detected glyphs when monster wouldn't be
+	 * visible by any other means.
+	 */
+	if (sightflags == DETECTED) {
 	    if (worm_tail)
 		num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL));
 	    else
@@ -589,7 +596,9 @@
     }
     /* draw monster on top if we can sense it */
     if ((x != u.ux || y != u.uy) && (mon = m_at(x,y)) && sensemon(mon))
-	display_monster(x,y,mon,1,((x != mon->mx)  || (y != mon->my)));
+	display_monster(x, y, mon,
+		(tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)) ? PHYSICALLY_SEEN : DETECTED,
+		is_worm_tail(mon));
 }
 
 /*
@@ -653,15 +662,27 @@
 	}
 	else {
 	    mon = m_at(x,y);
-	    worm_tail = mon && ((x != mon->mx)  || (y != mon->my));
-	    if (mon &&
-		 ((see_it = (worm_tail
-			? (!mon->minvis || See_invisible)
-			: (mon_visible(mon)) || sensemon(mon))))) {
+	    worm_tail = is_worm_tail(mon);
+	    see_it = mon && (worm_tail
+		? (!mon->minvis || See_invisible)
+		: (mon_visible(mon)) || tp_sensemon(mon) || MATCH_WARN_OF_MON(mon));
+	    if (mon && (see_it || (!worm_tail && Detect_monsters))) {
+		if (mon->mtrapped) {
+		    struct trap *trap = t_at(x, y);
+		    int tt = trap ? trap->ttyp : NO_TRAP;
+
+		    /* if monster is in a physical trap, you see the trap too */
+		    if (tt == BEAR_TRAP || tt == PIT ||
+			tt == SPIKED_PIT ||tt == WEB) {
+			trap->tseen = TRUE;
+		    }
+		}
 		_map_location(x,y,0);	/* map under the monster */
 		/* also gets rid of any invisibility glyph */
-		display_monster(x,y,mon,see_it,worm_tail);
+		display_monster(x, y, mon, see_it ? PHYSICALLY_SEEN : DETECTED, worm_tail);
 	    }
+	    else if (mon && mon_warning(mon) && !is_worm_tail(mon))
+	        display_warning(mon);
 	    else if (glyph_is_invisible(levl[x][y].glyph))
 		map_invisible(x, y);
 	    else
@@ -677,15 +698,16 @@
 	    if (canseeself()) display_self();
 	}
 	else if ((mon = m_at(x,y))
-		&& (sensemon(mon)
-		    || (see_with_infrared(mon) && mon_visible(mon)))
-		&& !((x != mon->mx) || (y != mon->my))) {
+		&& ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)
+		    		|| (see_with_infrared(mon) && mon_visible(mon))))
+		    || Detect_monsters)
+		&& !is_worm_tail(mon)) {
 	    /* Monsters are printed every time. */
 	    /* This also gets rid of any invisibility glyph */
-	    display_monster(x,y,mon,0,0);
+	    display_monster(x, y, mon, see_it ? 0 : DETECTED, 0);
 	}
 	else if ((mon = m_at(x,y)) && mon_warning(mon) &&
-		 !((x != mon->mx) || (y != mon->my))) {
+		 !is_worm_tail(mon)) {
 	        display_warning(mon);
 	}		
 
@@ -725,6 +747,7 @@
     }
 }
 
+#undef is_worm_tail
 
 /*
  * shieldeff()
@@ -738,6 +761,7 @@
 {
     register int i;
 
+    if (!flags.sparkle) return;
     if (cansee(x,y)) {	/* Don't see anything if can't see the location */
 	for (i = 0; i < SHIELD_COUNT; i++) {
 	    show_glyph(x, y, cmap_to_glyph(shield_static[i]));
@@ -1183,6 +1207,8 @@
 	    text = "swallow border";	offset = glyph - GLYPH_SWALLOW_OFF;
 	} else if (glyph >= GLYPH_ZAP_OFF) {		/* zap beam */
 	    text = "zap beam";		offset = glyph - GLYPH_ZAP_OFF;
+	} else if (glyph >= GLYPH_EXPLODE_OFF) {	/* explosion */
+	    text = "explosion";		offset = glyph - GLYPH_EXPLODE_OFF;
 	} else if (glyph >= GLYPH_CMAP_OFF) {		/* cmap */
 	    text = "cmap_index";	offset = glyph - GLYPH_CMAP_OFF;
 	} else if (glyph >= GLYPH_OBJ_OFF) {		/* object */
diff -Naurd ../nethack-3.3.1/src/do.c ./src/do.c
--- ../nethack-3.3.1/src/do.c Mon Jul 17 01:15:55 2000
+++ ./src/do.c Fri Mar 22 14:40:59 2002
@@ -50,9 +50,9 @@
 {
 	int result, i = (invent || u.ugold) ? 0 : (SIZE(drop_types) - 1);
 
-	if (*u.ushops) sellobj_state(TRUE);
+	if (*u.ushops) sellobj_state(SELL_DELIBERATE);
 	result = drop(getobj(&drop_types[i], "drop"));
-	if (*u.ushops) sellobj_state(FALSE);
+	if (*u.ushops) sellobj_state(SELL_NORMAL);
 	reset_occupations();
 
 	return result;
@@ -162,14 +162,16 @@
 		if (((mtmp = m_at(x, y)) && mtmp->mtrapped) ||
 			(u.utrap && u.ux == x && u.uy == y)) {
 		    if (*verb)
-			pline_The("boulder %ss into the pit%s.", verb,
+			pline_The("boulder %s into the pit%s.",
+				vtense((const char *)0, verb),
 				(mtmp) ? "" : " with you");
 		    if (mtmp) {
 			if (!passes_walls(mtmp->data) &&
 				!throws_rocks(mtmp->data)) {
-			    if (hmon(mtmp, obj, TRUE))
+			    if (hmon(mtmp, obj, TRUE) && !is_whirly(mtmp->data))
 				return FALSE;	/* still alive */
-			} else mtmp->mtrapped = 0;
+			}
+			mtmp->mtrapped = 0;
 		    } else {
 			if (!Passes_walls && !throws_rocks(youmonst.data)) {
 			    losehp(rnd(15), "squished under a boulder",
@@ -197,6 +199,8 @@
 		bury_objs(x, y);
 		newsym(x,y);
 		return TRUE;
+	} else if (is_lava(x, y)) {
+		return fire_damage(obj, FALSE, FALSE, x, y);
 	} else if (is_pool(x, y)) {
 		water_damage(obj, FALSE, FALSE);
 	}
@@ -210,20 +214,20 @@
 doaltarobj(obj)  /* obj is an object dropped on an altar */
 	register struct obj *obj;
 {
-	if (Blind) return;
+	if (Blind || obj->oclass == GOLD_CLASS)
+		return;
 
 	/* KMH, conduct */
 	u.uconduct.gnostic++;
 
 	if (obj->blessed || obj->cursed) {
-		There("is %s flash as %s hit%s the altar.",
+		There("is %s flash as %s %s the altar.",
 			an(hcolor(obj->blessed ? amber : Black)),
-			doname(obj),
-			(obj->quan == 1L) ? "s" : "");
+			doname(obj), otense(obj, "hit"));
 		if (!Hallucination) obj->bknown = 1;
 	} else {
-		pline("%s land%s on the altar.", Doname2(obj),
-			(obj->quan == 1L) ? "s" : "");
+		pline("%s %s on the altar.", Doname2(obj),
+			otense(obj, "land"));
 		obj->bknown = 1;
 	}
 }
@@ -302,8 +306,8 @@
 		    if (otmp != uball && otmp != uchain &&
 			    !obj_resists(otmp, 1, 99)) {
 			if (!Blind) {
-			    pline("Suddenly, %s vanishes from the sink!",
-							doname(otmp));
+			    pline("Suddenly, %s %s from the sink!",
+				  doname(otmp), otense(otmp, "vanish"));
 			    ideed = TRUE;
 			}
 			delobj(otmp);
@@ -446,7 +450,6 @@
 			return(0);
 		}
 		setuwep((struct obj *)0);
-		if(uwep) return 0; /* lifesaved and rewielded */
 	}
 	if(obj == uquiver) {
 		setuqwep((struct obj *)0);
@@ -458,10 +461,14 @@
 	if (u.uswallow) {
 		/* barrier between you and the floor */
 		if(flags.verbose)
-			You("drop %s into %s %s.", doname(obj),
-				s_suffix(mon_nam(u.ustuck)),
-				is_animal(u.ustuck->data) ?
-				"stomach" : "interior");
+		{
+			char buf[BUFSZ];
+
+			/* doname can call s_suffix, reusing its buffer */
+			Strcpy(buf, s_suffix(mon_nam(u.ustuck)));
+			You("drop %s into %s %s.", doname(obj), buf,
+				mbodypart(u.ustuck, STOMACH));
+		}
 	} else {
 #ifdef SINKS
 	    if((obj->oclass == RING_CLASS || obj->otyp == MEAT_RING) &&
@@ -491,7 +498,6 @@
 dropx(obj)
 register struct obj *obj;
 {
-	/* Money is usually not in our inventory */
 	if (obj->oclass != GOLD_CLASS || obj == invent) freeinv(obj);
 	if (!u.uswallow && ship_object(obj, u.ux, u.uy, FALSE)) return;
 	dropy(obj);
@@ -501,27 +507,59 @@
 dropy(obj)
 register struct obj *obj;
 {
+	if (obj == uwep) setuwep((struct obj *)0);
+	if (obj == uquiver) setuqwep((struct obj *)0);
+	if (obj == uswapwep) setuswapwep((struct obj *)0);
+
 	if (!u.uswallow && flooreffects(obj,u.ux,u.uy,"drop")) return;
-	/* KMH -- Fixed crysknives have only 10% chance of reverting */
-	if (obj->otyp == CRYSKNIFE && (!obj->oerodeproof || !rn2(10))) {
-		obj->otyp = WORM_TOOTH;
-		obj->oerodeproof = 0;
-	}
 	/* uswallow check done by GAN 01/29/87 */
 	if(u.uswallow) {
-		if (obj != uball) {		/* mon doesn't pick up ball */
-		    (void) mpickobj(u.ustuck,obj);
+	    boolean could_petrify;
+	    if (obj != uball) {		/* mon doesn't pick up ball */
+		could_petrify = obj->otyp == CORPSE &&
+		    touch_petrifies(&mons[obj->corpsenm]);
+		(void) mpickobj(u.ustuck,obj);
+		if (could_petrify && is_animal(u.ustuck->data)) {
+		    minstapetrify(u.ustuck, TRUE);
+		    /* Don't leave a cockatrice corpse available in a statue */
+		    if (!u.uswallow) delobj(obj);
 		}
+	    }
 	} else  {
-		place_object(obj, u.ux, u.uy);
-		if (obj == uball)
-		    drop_ball(u.ux,u.uy);
-		else
-		    sellobj(obj, u.ux, u.uy);
-		stackobj(obj);
-		if(Blind && Levitation)
-		    map_object(obj, 0);
-		newsym(u.ux,u.uy);	/* remap location under self */
+	    place_object(obj, u.ux, u.uy);
+	    if (obj == uball)
+		drop_ball(u.ux,u.uy);
+	    else
+		sellobj(obj, u.ux, u.uy);
+	    stackobj(obj);
+	    if(Blind && Levitation)
+		map_object(obj, 0);
+	    newsym(u.ux,u.uy);	/* remap location under self */
+	}
+}
+
+/* things that must change when not held; recurse into containers.
+   Called for both player and monsters */
+void
+obj_no_longer_held(obj)
+struct obj *obj;
+{
+	if (!obj) {
+	    return;
+	} else if ((Is_container(obj) || obj->otyp == STATUE) && obj->cobj) {
+	    struct obj *contents;
+	    for(contents=obj->cobj; contents; contents=contents->nobj)
+		obj_no_longer_held(contents);
+	}
+	switch(obj->otyp) {
+	case CRYSKNIFE:
+	    /* KMH -- Fixed crysknives have only 10% chance of reverting */
+	    /* only changes when not held by player or monster */
+	    if (!obj->oerodeproof || !rn2(10)) {
+		obj->otyp = WORM_TOOTH;
+		obj->oerodeproof = 0;
+	    }
+	    break;
 	}
 }
 
@@ -532,11 +570,11 @@
 	int result = 0;
 
 	add_valid_menu_class(0); /* clear any classes already there */
-	if (*u.ushops) sellobj_state(TRUE);
+	if (*u.ushops) sellobj_state(SELL_DELIBERATE);
 	if (flags.menu_style != MENU_TRADITIONAL ||
-		(result = ggetobj("drop", drop, 0, FALSE)) < -1)
+		(result = ggetobj("drop", drop, 0, FALSE, (unsigned *)0)) < -1)
 	    result = menu_drop(result);
-	if (*u.ushops) sellobj_state(FALSE);
+	if (*u.ushops) sellobj_state(SELL_NORMAL);
 	reset_occupations();
 
 	return result;
@@ -549,7 +587,8 @@
 {
     int n, i, n_dropped = 0;
     long cnt;
-    struct obj *otmp, *otmp2, *u_gold = 0;
+    struct obj *otmp, *otmp2;
+    struct obj *u_gold = 0;
     menu_item *pick_list;
     boolean all_categories = TRUE;
     boolean drop_everything = FALSE;
@@ -563,14 +602,14 @@
 	u_gold->nobj = invent;
 	invent = u_gold;
     }
-
     if (retry) {
 	all_categories = (retry == -2);
     } else if (flags.menu_style == MENU_FULL) {
 	all_categories = FALSE;
 	n = query_category("Drop what type of items?",
 			invent,
-			UNPAID_TYPES | ALL_TYPES | CHOOSE_ALL,
+			UNPAID_TYPES | ALL_TYPES | CHOOSE_ALL |
+			BUC_BLESSED | BUC_CURSED | BUC_UNCURSED | BUC_UNKNOWN,
 			&pick_list, PICK_ANY);
 	if (!n) goto drop_done;
 	for (i = 0; i < n; i++) {
@@ -583,10 +622,15 @@
 	}
 	free((genericptr_t) pick_list);
     } else if (flags.menu_style == MENU_COMBINATION) {
+	unsigned ggoresults = 0;
 	all_categories = FALSE;
 	/* Gather valid classes via traditional NetHack method */
-	i = ggetobj("drop", drop, 0, TRUE);
+	i = ggetobj("drop", drop, 0, TRUE, &ggoresults);
 	if (i == -2) all_categories = TRUE;
+	if (ggoresults & ALL_FINISHED) {
+		n_dropped = i;
+		goto drop_done;
+	}
     }
 
     if (drop_everything) {
@@ -605,11 +649,10 @@
 		cnt = pick_list[i].count;
 		if (cnt < otmp->quan && !welded(otmp) &&
 			(!otmp->cursed || otmp->otyp != LOADSTONE)) {
-		    otmp2 = splitobj(otmp, cnt);
-		    /* assume other worn items aren't mergable */
-		    if (otmp == uwep) setuwep(otmp2);
-			if (otmp == uquiver) setuqwep(otmp2);
-			if (otmp == uswapwep) setuswapwep(otmp2);
+		    if (otmp->oclass == GOLD_CLASS)
+			(void) splitobj(otmp, otmp->quan - cnt);
+		    else
+		    otmp = splitobj(otmp, cnt);
 		}
 		n_dropped += drop(otmp);
 	    }
@@ -623,6 +666,7 @@
 	u_gold = invent;
 	invent = u_gold->nobj;
 	dealloc_obj(u_gold);
+	update_inventory();
     }
     return n_dropped;
 }
@@ -641,11 +685,33 @@
 		    (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up)),
 		ladder_down = (u.ux == xdnladder && u.uy == ydnladder);
 
+#ifdef STEED
+	if (u.usteed && !u.usteed->mcanmove) {
+		pline("%s won't move!", Monnam(u.usteed));
+		return(0);
+	} else if (u.usteed && u.usteed->meating) {
+		pline("%s is still eating.", Monnam(u.usteed));
+		return(0);
+	} else
+#endif
 	if (Levitation) {
 	    if ((HLevitation & I_SPECIAL) || (ELevitation & W_ARTI)) {
 		/* end controlled levitation */
-			if (float_down(I_SPECIAL|TIMEOUT, W_ARTI))
-			    return (1);   /* came down, so moved */
+		if (ELevitation & W_ARTI) {
+		    struct obj *obj;
+
+		    for(obj = invent; obj; obj = obj->nobj) {
+			if (obj->oartifact &&
+					artifact_has_invprop(obj,LEVITATION)) {
+			    if (obj->age < monstermoves)
+				obj->age = monstermoves + rnz(100);
+			    else
+				obj->age += rnz(100);
+			}
+		    }
+		}
+		if (float_down(I_SPECIAL|TIMEOUT, W_ARTI))
+		    return (1);   /* came down, so moved */
 	    }
 	    floating_above(stairs_down ? "stairs" : ladder_down ?
 			   "ladder" : surface(u.ux, u.uy));
@@ -660,7 +726,9 @@
 		}
 	}
 	if(u.ustuck) {
-		You("are being held, and cannot go down.");
+		You("are %s, and cannot go down.",
+			!u.uswallow ? "being held" : is_animal(u.ustuck->data) ?
+			"swallowed" : "engulfed");
 		return(1);
 	}
 	if (on_level(&valley_level, &u.uz) && !u.uevent.gehennom_entered) {
@@ -702,8 +770,19 @@
 		You_cant("go up here.");
 		return(0);
 	}
+#ifdef STEED
+	if (u.usteed && !u.usteed->mcanmove) {
+		pline("%s won't move!", Monnam(u.usteed));
+		return(0);
+	} else if (u.usteed && u.usteed->meating) {
+		pline("%s is still eating.", Monnam(u.usteed));
+		return(0);
+	} else
+#endif
 	if(u.ustuck) {
-		You("are being held, and cannot go up.");
+		You("are %s, and cannot go up.",
+			!u.uswallow ? "being held" : is_animal(u.ustuck->data) ?
+			"swallowed" : "engulfed");
 		return(1);
 	}
 	if(near_capacity() > SLT_ENCUMBER) {
@@ -1019,7 +1098,10 @@
 		    }
 		    losehp(rnd(3), "falling downstairs", KILLED_BY);
 #ifdef STEED
-		    if (u.usteed) dismount_steed(DISMOUNT_FELL);
+		    if (u.usteed) {
+			dismount_steed(DISMOUNT_FELL);
+			if (Punished) unplacebc();
+		    }
 #endif
 		    selftouch("Falling, you");
 		} else if (u.dz && at_ladder)
@@ -1127,22 +1209,27 @@
 	    static const char *fam_msgs[4] = {
 		"You have a sense of deja vu.",
 		"You feel like you've been here before.",
-		"This place looks familiar...",
+		"This place %s familiar...",
 		0	/* no message */
 	    };
 	    static const char *halu_fam_msgs[4] = {
-		"Whoa!  Everything looks different.",
+		"Whoa!  Everything %s different.",
 		"You are surrounded by twisty little passages, all alike.",
-		"Gee, this looks like uncle Conan's place...",
+		"Gee, this %s like uncle Conan's place...",
 		0	/* no message */
 	    };
 	    const char *mesg;
+	    char buf[BUFSZ];
 	    int which = rn2(4);
 
 	    if (Hallucination)
 		mesg = halu_fam_msgs[which];
 	    else
 		mesg = fam_msgs[which];
+	    if (mesg && index(mesg, '%')) {
+		Sprintf(buf, mesg, !Blind ? "looks" : "seems");
+		mesg = buf;
+	    }
 	    if (mesg) pline(mesg);
 	}
 
@@ -1158,7 +1245,7 @@
 
 	/* the message from your quest leader */
 	if (!In_quest(&u.uz0) && at_dgn_entrance("The Quest") &&
-		!(u.uevent.qexpelled || u.uevent.qcompleted || leaderless())) {
+		!(u.uevent.qexpelled || u.uevent.qcompleted || quest_status.leader_is_dead)) {
 
 		if (u.uevent.qcalled) {
 			com_pager(Role_if(PM_ROGUE) ? 4 : 3);
@@ -1324,12 +1411,22 @@
     boolean is_uwep, chewed;
     xchar where;
     char *cname, cname_buf[BUFSZ];
-
+    struct obj *container = (struct obj *)0;
+    int container_where = 0;
+    
     where = corpse->where;
     is_uwep = corpse == uwep;
     cname = eos(strcpy(cname_buf, "bite-covered "));
     Strcpy(cname, corpse_xname(corpse, TRUE));
     mcarry = (where == OBJ_MINVENT) ? corpse->ocarry : 0;
+
+    if (where == OBJ_CONTAINED) {
+    	struct monst *mtmp2 = (struct monst *)0;
+	container = corpse->ocontainer;
+    	mtmp2 = get_container_location(container, &container_where, (int *)0);
+	/* container_where is the outermost container's location even if nested */
+	if (container_where == OBJ_MINVENT && mtmp2) mcarry = mtmp2;
+    }
     mtmp = revive(corpse);	/* corpse is gone if successful */
 
     if (mtmp) {
@@ -1359,7 +1456,27 @@
 			      Adjmonnam(mtmp, "bite-covered") : Monnam(mtmp));
 		}
 		break;
-
+	   case OBJ_CONTAINED:
+	   	if (container_where == OBJ_MINVENT && cansee(mtmp->mx, mtmp->my) &&
+		    mcarry && canseemon(mcarry) && container) {
+		        char sackname[BUFSZ];
+		        Sprintf(sackname, "%s %s", s_suffix(mon_nam(mcarry)),
+				xname(container)); 
+	   		pline("%s writhes out of %s!", Amonnam(mtmp), sackname);
+	   	} else if (container_where == OBJ_INVENT && container) {
+		        char sackname[BUFSZ];
+		        Strcpy(sackname, an(xname(container)));
+	   		pline("%s %s out of %s in your pack!",
+	   			Blind ? Something : Amonnam(mtmp),
+				locomotion(mtmp->data,"writhes"),
+	   			sackname);
+	   	} else if (container_where == OBJ_FLOOR && container &&
+		            cansee(mtmp->mx, mtmp->my)) {
+		        char sackname[BUFSZ];
+		        Strcpy(sackname, an(xname(container)));
+			pline("%s escapes from %s!", Amonnam(mtmp), sackname);
+		}
+		break;
 	    default:
 		/* we should be able to handle the other cases... */
 		impossible("revive_corpse: lost corpse @ %d", where);
diff -Naurd ../nethack-3.3.1/src/do_name.c ./src/do_name.c
--- ../nethack-3.3.1/src/do_name.c Wed Aug 9 16:46:17 2000
+++ ./src/do_name.c Fri Mar 22 14:40:55 2002
@@ -155,7 +155,7 @@
 			    }	/* column */
 			}	/* row */
 		    }		/* pass */
-		    pline("Can't find dungeon feature '%c'", c);
+		    pline("Can't find dungeon feature '%c'.", c);
 		    msg_given = TRUE;
 		    goto nxtc;
 		} else {
@@ -268,10 +268,7 @@
 		return(0);
 	}
 	/* special case similar to the one in lookat() */
-	if (mtmp->data != &mons[PM_HIGH_PRIEST])
-	    Strcpy(buf, x_monnam(mtmp, ARTICLE_THE, (char *)0, 0, TRUE));
-	else
-	    Sprintf(buf, "the high priest%s", mtmp->female ? "ess" : "");
+	(void) distant_monnam(mtmp, ARTICLE_THE, buf);
 	Sprintf(qbuf, "What do you want to call %s?", buf);
 	getlin(qbuf,buf);
 	if(!*buf || *buf == '\033') return(0);
@@ -299,7 +296,7 @@
 	short objtyp;
 
 	Sprintf(qbuf, "What do you want to name %s %s?",
-		(obj->quan > 1L) ? "these" : "this", xname(obj));
+		is_plural(obj) ? "these" : "this", xname(obj));
 	getlin(qbuf, buf);
 	if(!*buf || *buf == '\033')	return;
 	/* strip leading and trailing spaces; unnames item if all spaces */
@@ -580,6 +577,7 @@
 	struct permonst *mdat = mtmp->data;
 	boolean do_hallu, do_invis, do_it, do_saddle;
 	boolean name_at_start, has_adjectives;
+	char *bp;
 
 	if (program_state.gameover)
 	    suppress |= SUPPRESS_HALLUCINATION;
@@ -600,6 +598,12 @@
 
 	buf[0] = 0;
 
+	/* unseen monsters, etc.  Use "it" */
+	if (do_it) {
+	    Strcpy(buf, "it");
+	    return buf;
+	}
+
 	/* priests and minions: don't even use this function */
 	if (mtmp->ispriest || mtmp->isminion) {
 	    char priestnambuf[BUFSZ];
@@ -618,12 +622,6 @@
 	    return strcpy(buf, name);
 	}
 
-	/* unseen monsters, etc.  Use "it" */
-	if (do_it) {
-	    Strcpy(buf, "it");
-	    return buf;
-	}
-
 	/* Shopkeepers: use shopkeeper name.  For normal shopkeepers, just
 	 * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible
 	 * shopkeeper" or "Asidonhopo the blue dragon".  If hallucinating,
@@ -654,7 +652,8 @@
 	if (do_invis)
 	    Strcat(buf, "invisible ");
 #ifdef STEED
-	if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && !Blind)
+	if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) &&
+	    !Blind && !Hallucination)
 	    Strcat(buf, "saddled ");
 #endif
 	if (buf[0] != 0)
@@ -669,13 +668,25 @@
 	    name_at_start = FALSE;
 	} else if (mtmp->mnamelth) {
 	    char *name = NAME(mtmp);
-	    
+
 	    if (mdat == &mons[PM_GHOST]) {
 		Sprintf(eos(buf), "%s ghost", s_suffix(name));
 		name_at_start = TRUE;
 	    } else if (called) {
 		Sprintf(eos(buf), "%s called %s", mdat->mname, name);
 		name_at_start = (boolean)type_is_pname(mdat);
+	    } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) {
+		/* <name> the <adjective> <invisible> <saddled> <rank> */
+		char pbuf[BUFSZ];
+
+		Strcpy(pbuf, name);
+		pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */
+		if (has_adjectives)
+		    Strcat(pbuf, buf);
+		Strcat(pbuf, bp + 5);	/* append the rest of the name */
+		Strcpy(buf, pbuf);
+		article = ARTICLE_NONE;
+		name_at_start = TRUE;
 	    } else {
 		Strcat(buf, name);
 		name_at_start = TRUE;
@@ -697,6 +708,9 @@
 		article = ARTICLE_THE;
 	    else
 		article = ARTICLE_NONE;
+	} else if (mons[monsndx(mdat)].geno & G_UNIQ &&
+		   article == ARTICLE_A) {
+	    article = ARTICLE_THE;
 	}
 
 	{
@@ -827,6 +841,27 @@
 	return(bp);
 }
 
+/* used for monster ID by the '/', ';', and 'C' commands to block remote
+   identification of the endgame altars via their attending priests */
+char *
+distant_monnam(mon, article, outbuf)
+struct monst *mon;
+int article;	/* only ARTICLE_NONE and ARTICLE_THE are handled here */
+char *outbuf;
+{
+    /* high priest(ess)'s identity is concealed on the Astral Plane,
+       unless you're adjacent (overridden for hallucination which does
+       its own obfuscation) */
+    if (mon->data == &mons[PM_HIGH_PRIEST] && !Hallucination &&
+	    Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) {
+	Strcpy(outbuf, article == ARTICLE_THE ? "the " : "");
+	Strcat(outbuf, mon->female ? "high priestess" : "high priest");
+    } else {
+	Strcpy(outbuf, x_monnam(mon, article, (char *)0, 0, TRUE));
+    }
+    return outbuf;
+}
+
 static const char *bogusmons[] = {
 	"jumbo shrimp", "giant pigmy", "gnu", "killer penguin",
 	"giant cockroach", "giant slug", "maggot", "pterodactyl",
@@ -901,31 +936,6 @@
 	return mons[name].mname;
 }
 
-const char *pronoun_pairs[][2] = {
-	{"him", "her"}, {"Him", "Her"}, {"his", "her"}, {"His", "Her"},
-	{"he", "she"}, {"He", "She"},
-	{0, 0}
-};
-
-char *
-self_pronoun(str, pronoun)
-const char *str;
-const char *pronoun;
-{
-	static NEARDATA char buf[BUFSZ];
-	register int i;
-
-	for(i=0; pronoun_pairs[i][0]; i++) {
-		if(!strncmp(pronoun, pronoun_pairs[i][0], 3)) {
-			Sprintf(buf, str, pronoun_pairs[i][flags.female]);
-			return buf;
-		}
-	}
-	impossible("never heard of pronoun %s?", pronoun);
-	Sprintf(buf, str, pronoun_pairs[i][0]);
-	return buf;
-}
-
 #ifdef REINCARNATION
 const char *
 roguename() /* Name of a Rogue player */
@@ -986,14 +996,16 @@
 	"Nemesis Riduclii","Canis latrans"
 };
 	
-char *coyotename(buf)
+char *coyotename(mtmp, buf)
+struct monst *mtmp;
 char *buf;
 {
-	if (buf)
-		Sprintf(buf,
-			"coyote - %s",
-			coynames[rn2(SIZE(coynames)-1)]);
-	return buf;
+    if (mtmp && buf) {
+	Sprintf(buf, "%s - %s",
+	    x_monnam(mtmp, ARTICLE_NONE, (char *)0, 0, TRUE),
+	    mtmp->mcan ? coynames[SIZE(coynames)-1] : coynames[rn2(SIZE(coynames)-1)]);
+    }
+    return buf;
 }
 #endif /* OVL2 */
 
diff -Naurd ../nethack-3.3.1/src/do_wear.c ./src/do_wear.c
--- ../nethack-3.3.1/src/do_wear.c Thu Aug 3 20:44:47 2000
+++ ./src/do_wear.c Fri Mar 22 14:40:55 2002
@@ -13,6 +13,7 @@
 STATIC_OVL NEARDATA long takeoff_mask = 0L, taking_off = 0L;
 
 static NEARDATA int todelay;
+static boolean cancelled_don = FALSE;
 
 static NEARDATA const char see_yourself[] = "see yourself";
 static NEARDATA const char unknown_type[] = "Unknown type of %s (%d)";
@@ -67,9 +68,16 @@
 on_msg(otmp)
 register struct obj *otmp;
 {
-	if(flags.verbose)
-	    You("are now wearing %s.",
-		obj_is_pname(otmp) ? the(xname(otmp)) : an(xname(otmp)));
+	if (flags.verbose) {
+	    char how[BUFSZ];
+
+	    how[0] = '\0';
+	    if (otmp->otyp == TOWEL)
+		Sprintf(how, " around your %s", body_part(HEAD));
+	    You("are now wearing %s%s.",
+		obj_is_pname(otmp) ? the(xname(otmp)) : an(xname(otmp)),
+		how);
+	}
 }
 
 /*
@@ -81,8 +89,8 @@
 int
 Boots_on()
 {
-	long oldprop = u.uprops[objects[uarmf->otyp].oc_oprop].extrinsic & ~WORN_BOOTS;
-
+    long oldprop =
+	u.uprops[objects[uarmf->otyp].oc_oprop].extrinsic & ~WORN_BOOTS;
 
     switch(uarmf->otyp) {
 	case LOW_BOOTS:
@@ -117,6 +125,7 @@
 		if (!oldprop && !HLevitation) {
 			makeknown(uarmf->otyp);
 			float_up();
+			spoteffects(FALSE);
 		}
 		break;
 	default: impossible(unknown_type, c_boots, uarmf->otyp);
@@ -128,7 +137,7 @@
 Boots_off()
 {
     int otyp = uarmf->otyp;
-	long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_BOOTS;
+    long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_BOOTS;
 
 
 	/* For levitation, float_down() returns if Levitation, so we
@@ -137,22 +146,22 @@
     setworn((struct obj *)0, W_ARMF);
     switch (otyp) {
 	case SPEED_BOOTS:
-		if (!Very_fast) {
+		if (!Very_fast && !cancelled_don) {
 			makeknown(otyp);
 			You_feel("yourself slow down%s.",
 				Fast ? " a bit" : "");
 		}
 		break;
 	case WATER_WALKING_BOOTS:
-		if (is_pool(u.ux,u.uy) && !Levitation
-			    && !Flying && !is_clinger(youmonst.data)) {
+		if (is_pool(u.ux,u.uy) && !Levitation && !Flying &&
+		    !is_clinger(youmonst.data) && !cancelled_don) {
 			makeknown(otyp);
 			/* make boots known in case you survive the drowning */
 			spoteffects(TRUE);
 		}
 		break;
 	case ELVEN_BOOTS:
-		if (!oldprop && !HStealth && !BStealth) {
+		if (!oldprop && !HStealth && !BStealth && !cancelled_don) {
 			makeknown(otyp);
 			You("sure are noisy.");
 		}
@@ -162,7 +171,7 @@
 			HFumbling = EFumbling = 0;
 		break;
 	case LEVITATION_BOOTS:
-		if (!oldprop && !HLevitation) {
+		if (!oldprop && !HLevitation && !cancelled_don) {
 			(void) float_down(0L, 0L);
 			makeknown(otyp);
 		}
@@ -175,14 +184,15 @@
 		break;
 	default: impossible(unknown_type, c_boots, otyp);
     }
+    cancelled_don = FALSE;
     return 0;
 }
 
 STATIC_OVL int
 Cloak_on()
 {
-    long oldprop = u.uprops[objects[uarmc->otyp].oc_oprop].extrinsic & ~WORN_CLOAK;
-
+    long oldprop =
+	u.uprops[objects[uarmc->otyp].oc_oprop].extrinsic & ~WORN_CLOAK;
 
     switch(uarmc->otyp) {
 	case ELVEN_CLOAK:
@@ -194,6 +204,7 @@
 	case DWARVISH_CLOAK:
 	case CLOAK_OF_MAGIC_RESISTANCE:
 	case ROBE:
+	case LEATHER_CLOAK:
 		break;
 	case MUMMY_WRAPPING:
 		/* Note: it's already being worn, so we have to cheat here. */
@@ -215,7 +226,7 @@
 		}
 		break;
 	case OILSKIN_CLOAK:
-		pline("%s fits very tightly.",The(xname(uarmc)));
+		pline("%s very tightly.", Tobjnam(uarmc, "fit"));
 		break;
 	/* Alchemy smock gives poison _and_ acid resistance */
 	case ALCHEMY_SMOCK:
@@ -244,6 +255,7 @@
 	case CLOAK_OF_DISPLACEMENT:
 	case OILSKIN_CLOAK:
 	case ROBE:
+	case LEATHER_CLOAK:
 		break;
 	case MUMMY_WRAPPING:
 		if (Invis && !Blind) {
@@ -305,9 +317,11 @@
 		/*FALLTHRU*/
 	case DUNCE_CAP:
 		if (!uarmh->cursed) {
-		    pline("%s %s%s for a moment.", The(xname(uarmh)),
-			  Blind ? "vibrates" : "glows ",
-			  Blind ? (const char *)"" : hcolor(Black));
+		    if (Blind)
+			pline("%s for a moment.", Tobjnam(uarmh, "vibrate"));
+		    else
+			pline("%s %s for a moment.",
+			      Tobjnam(uarmh, "glow"), hcolor(Black));
 		    curse(uarmh);
 		}
 		flags.botl = 1;		/* reveal new alignment or INT & WIS */
@@ -337,30 +351,33 @@
 	case ELVEN_LEATHER_HELM:
 	case DWARVISH_IRON_HELM:
 	case ORCISH_HELM:
-		break;
+	    break;
 	case DUNCE_CAP:
-		flags.botl = 1;
-		break;
+	    flags.botl = 1;
+	    break;
 	case CORNUTHAUM:
+	    if (!cancelled_don) {
 		ABON(A_CHA) += (Role_if(PM_WIZARD) ? -1 : 1);
 		flags.botl = 1;
-		break;
+	    }
+	    break;
 	case HELM_OF_TELEPATHY:
-		/* need to update ability before calling see_monsters() */
-		setworn((struct obj *)0, W_ARMH);
-		see_monsters();
-		return 0;
+	    /* need to update ability before calling see_monsters() */
+	    setworn((struct obj *)0, W_ARMH);
+	    see_monsters();
+	    return 0;
 	case HELM_OF_BRILLIANCE:
-		adj_abon(uarmh, -uarmh->spe);
-		break;
+	    if (!cancelled_don) adj_abon(uarmh, -uarmh->spe);
+	    break;
 	case HELM_OF_OPPOSITE_ALIGNMENT:
-		u.ualign.type = u.ualignbase[A_CURRENT];
-		u.ublessed = 0; /* lose the other god's protection */
-		flags.botl = 1;
-		break;
+	    u.ualign.type = u.ualignbase[A_CURRENT];
+	    u.ublessed = 0; /* lose the other god's protection */
+	    flags.botl = 1;
+	    break;
 	default: impossible(unknown_type, c_helmet, uarmh->otyp);
     }
     setworn((struct obj *)0, W_ARMH);
+    cancelled_don = FALSE;
     return 0;
 }
 
@@ -369,8 +386,7 @@
 Gloves_on()
 {
     long oldprop =
-		u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
-
+	u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
 
     switch(uarmg->otyp) {
 	case LEATHER_GLOVES:
@@ -395,50 +411,51 @@
 Gloves_off()
 {
     long oldprop =
-		u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
-
+	u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES;
 
     switch(uarmg->otyp) {
 	case LEATHER_GLOVES:
-		break;
+	    break;
 	case GAUNTLETS_OF_FUMBLING:
-		if (!oldprop && !(HFumbling & ~TIMEOUT))
-			HFumbling = EFumbling = 0;
-		break;
+	    if (!oldprop && !(HFumbling & ~TIMEOUT))
+		HFumbling = EFumbling = 0;
+	    break;
 	case GAUNTLETS_OF_POWER:
-		makeknown(uarmg->otyp);
-		flags.botl = 1; /* taken care of in attrib.c */
-		break;
+	    makeknown(uarmg->otyp);
+	    flags.botl = 1; /* taken care of in attrib.c */
+	    break;
 	case GAUNTLETS_OF_DEXTERITY:
-		adj_abon(uarmg, -uarmg->spe);
-		break;
+	    if (!cancelled_don) adj_abon(uarmg, -uarmg->spe);
+	    break;
 	default: impossible(unknown_type, c_gloves, uarmg->otyp);
     }
     setworn((struct obj *)0, W_ARMG);
+    cancelled_don = FALSE;
 
     /* Prevent wielding cockatrice when not wearing gloves */
     if (uwep && uwep->otyp == CORPSE &&
 		touch_petrifies(&mons[uwep->corpsenm])) {
 	char kbuf[BUFSZ];
 
-	You("wield the %s corpse in your bare %s.",
-	    mons[uwep->corpsenm].mname, makeplural(body_part(HAND)));
-	Sprintf(kbuf, "%s corpse", an(mons[uwep->corpsenm].mname));
+	You("wield the %s in your bare %s.",
+	    corpse_xname(uwep, TRUE), makeplural(body_part(HAND)));
+	Strcpy(kbuf, an(corpse_xname(uwep, TRUE)));
 	instapetrify(kbuf);
 	uwepgone();  /* life-saved still doesn't allow touching cockatrice */
     }
-	/* KMH -- ...or your secondary weapon when you're wielding it */
-	if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE &&
-			touch_petrifies(&mons[uswapwep->corpsenm])) {
+
+    /* KMH -- ...or your secondary weapon when you're wielding it */
+    if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE &&
+	touch_petrifies(&mons[uswapwep->corpsenm])) {
 	char kbuf[BUFSZ];
 
-	You("wield the %s corpse in your bare %s.",
-		mons[uswapwep->corpsenm].mname, body_part(HAND));
+	You("wield the %s in your bare %s.",
+	    corpse_xname(uswapwep, TRUE), body_part(HAND));
 
-	Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname));
+	Strcpy(kbuf, an(corpse_xname(uswapwep, TRUE)));
 	instapetrify(kbuf);
-	uswapwepgone();  /* life-saved still doesn't allow touching cockatrice */
-	}
+	uswapwepgone();	/* lifesaved still doesn't allow touching cockatrice */
+    }
 
     return 0;
 }
@@ -497,6 +514,7 @@
 Armor_off()
 {
     setworn((struct obj *)0, W_ARM);
+    cancelled_don = FALSE;
     return 0;
 }
 
@@ -507,6 +525,7 @@
 Armor_gone()
 {
     setnotworn(uarm);
+    cancelled_don = FALSE;
     return 0;
 }
 
@@ -544,6 +563,10 @@
 		       changed the character's base sex */
 		    You("don't feel like yourself.");
 		pline_The("amulet disintegrates!");
+		if (orig_sex == poly_gender() && uamul->dknown &&
+			!objects[AMULET_OF_CHANGE].oc_name_known &&
+			!objects[AMULET_OF_CHANGE].oc_uname)
+		    docall(uamul);
 		useup(uamul);
 		break;
 	    }
@@ -578,13 +601,14 @@
 		break;
 	case AMULET_OF_MAGICAL_BREATHING:
 		if (Underwater) {
-		    if (!breathless(youmonst.data) && !amphibious(youmonst.data)
-						&& !Swimming)
-			You("suddenly inhale an unhealthy amount of water!");
 		    /* HMagical_breathing must be set off
-		       before calling drown() */
+			before calling drown() */
 		    setworn((struct obj *)0, W_AMUL);
-		    (void) drown();
+		    if (!breathless(youmonst.data) && !amphibious(youmonst.data)
+						&& !Swimming) {
+			You("suddenly inhale an unhealthy amount of water!");
+		    	(void) drown();
+		    }
 		    return;
 		}
 		break;
@@ -613,10 +637,9 @@
     long oldprop = u.uprops[objects[obj->otyp].oc_oprop].extrinsic;
     int old_attrib;
 
-
-	if (obj == uwep) setuwep((struct obj *) 0);
-	if (obj == uswapwep) setuswapwep((struct obj *) 0);
-	if (obj == uquiver) setuqwep((struct obj *) 0);
+    if (obj == uwep) setuwep((struct obj *) 0);
+    if (obj == uswapwep) setuswapwep((struct obj *) 0);
+    if (obj == uquiver) setuqwep((struct obj *) 0);
 
     /* only mask out W_RING when we don't have both
        left and right rings of the same type */
@@ -683,6 +706,7 @@
 			float_up();
 			makeknown(RIN_LEVITATION);
 			obj->known = TRUE;
+			spoteffects(FALSE);	/* for sinks */
 		}
 		break;
 	case RIN_GAIN_STRENGTH:
@@ -720,6 +744,7 @@
 		if (obj->spe || objects[RIN_PROTECTION].oc_name_known) {
 			makeknown(RIN_PROTECTION);
 			obj->known = TRUE;
+			update_inventory();
 		}
 		break;
     }
@@ -839,22 +864,30 @@
 Blindf_on(otmp)
 register struct obj *otmp;
 {
-	long already_blinded = Blinded;
+	boolean already_blind = Blind, changed = FALSE;
 
 	if (otmp == uwep)
 	    setuwep((struct obj *) 0);
 	if (otmp == uswapwep)
-		setuswapwep((struct obj *) 0);
+	    setuswapwep((struct obj *) 0);
 	if (otmp == uquiver)
-		setuqwep((struct obj *) 0);
+	    setuqwep((struct obj *) 0);
 	setworn(otmp, W_TOOL);
-	if (otmp->otyp == TOWEL && flags.verbose)
-	    You("wrap %s around your %s.", an(xname(otmp)), body_part(HEAD));
 	on_msg(otmp);
-	if (!already_blinded) {
-	    if (Punished) set_bc(0);	/* Set ball&chain variables before */
-					/* the hero goes blind.		   */
-	    if (Blind_telepat || Infravision) see_monsters(); /* sense monsters */
+
+	if (Blind && !already_blind) {
+	    changed = TRUE;
+	    if (flags.verbose) You_cant("see any more.");
+	    /* set ball&chain variables before the hero goes blind */
+	    if (Punished) set_bc(0);
+	} else if (already_blind && !Blind) {
+	    changed = TRUE;
+	    /* "You are now wearing the Eyes of the Overworld." */
+	    You("can see!");
+	}
+	if (changed) {
+	    /* blindness has just been toggled */
+	    if (Blind_telepat || Infravision) see_monsters();
 	    vision_full_recalc = 1;	/* recalc vision limits */
 	    flags.botl = 1;
 	}
@@ -864,23 +897,34 @@
 Blindf_off(otmp)
 register struct obj *otmp;
 {
-	long was_blind = Blind;	/* may still be able to see */
+	boolean was_blind = Blind, changed = FALSE;
 
 	setworn((struct obj *)0, otmp->owornmask);
 	off_msg(otmp);
 
 	if (Blind) {
-	    if (otmp->otyp == LENSES)
-		; /* "still cannot see" makes no sense for lenses; do nothing */
-	    else if (was_blind)
-		You("still cannot see.");
-	    else
-		You("cannot see anything now!");
-	} else if (!Blinded) {
+	    if (was_blind) {
+		/* "still cannot see" makes no sense when removing lenses
+		   since they can't have been the cause of your blindness */
+		if (otmp->otyp != LENSES)
+		    You("still cannot see.");
+	    } else {
+		changed = TRUE;	/* !was_blind */
+		/* "You were wearing the Eyes of the Overworld." */
+		You_cant("see anything now!");
+		/* set ball&chain variables before the hero goes blind */
+		if (Punished) set_bc(0);
+	    }
+	} else if (was_blind) {
+	    changed = TRUE;	/* !Blind */
+	    You("can see again.");
+	}
+	if (changed) {
+	    /* blindness has just been toggled */
 	    if (Blind_telepat || Infravision) see_monsters();
+	    vision_full_recalc = 1;	/* recalc vision limits */
+	    flags.botl = 1;
 	}
-	vision_full_recalc = 1;	/* recalc vision limits */
-	flags.botl = 1;
 }
 
 /* called in main to set intrinsics of worn start-up items */
@@ -912,6 +956,8 @@
 	 * wasting time on it (and don't dereference it when donning would
 	 * otherwise finish)
 	 */
+	cancelled_don = (afternmv == Boots_on || afternmv == Helmet_on ||
+			 afternmv == Gloves_on || afternmv == Armor_on);
 	afternmv = 0;
 	nomovemsg = (char *)0;
 	multi = 0;
@@ -975,6 +1021,16 @@
 		is_sword(uwep) ? c_sword : c_weapon);
 	    uwep->bknown = TRUE;
 	    return 0;
+	} else if (welded(uwep) && bimanual(uwep) &&
+		   (otmp == uarm
+#ifdef TOURIST
+		    || otmp == uarmu
+#endif
+		    )) {
+	    You("seem unable to take off %s while holding your %s.",
+		the(xname(otmp)), is_sword(uwep) ? c_sword : c_weapon);
+	    uwep->bknown = TRUE;
+	    return 0;
 	}
 	if (otmp == uarmg && Glib) {
 	    You_cant("remove the slippery gloves with your slippery fingers.");
@@ -1011,7 +1067,7 @@
 		pline("Not wearing any accessories.");
 		return(0);
 	}
-	if (Accessories != 1) otmp = getobj(accessories, "take off");
+	if (Accessories != 1) otmp = getobj(accessories, "remove");
 	if(!otmp) return(0);
 	if(!(otmp->owornmask & (W_RING | W_AMUL | W_TOOL))) {
 		You("are not wearing that.");
@@ -1171,6 +1227,18 @@
 	return 0;
     }
 
+    if (welded(uwep) && bimanual(uwep) &&
+	(otmp == uarm
+#ifdef TOURIST
+	 || otmp == uarmu
+#endif
+	 )) {
+	if (noisy)
+	    You("cannot do that while holding your %s.",
+		is_sword(uwep) ? c_sword : c_weapon);
+	return 0;
+    }
+
     if (is_helmet(otmp)) {
 	if (uarmh) {
 	    if (noisy) already_wearing(an(c_helmet));
@@ -1229,7 +1297,7 @@
 		if (noisy) already_wearing(an(c_shirt));
 	    } else {
 		if (noisy) You_cant("wear that over your %s.",
-				    (uarm && !uarmc) ? c_armor : c_cloak);
+			           (uarm && !uarmc) ? c_armor : cloak_simple_name(uarmc));
 	    }
 	    err++;
 	} else
@@ -1237,13 +1305,13 @@
 #endif
     } else if (is_cloak(otmp)) {
 	if (uarmc) {
-	    if (noisy) already_wearing(an(c_cloak));
+	    if (noisy) already_wearing(an(cloak_simple_name(uarmc)));
 	    err++;
 	} else
 	    *mask = W_ARMC;
     } else if (is_suit(otmp)) {
 	if (uarmc) {
-	    if (noisy) You("cannot wear armor over a cloak.");
+	    if (noisy) You("cannot wear armor over a %s.", cloak_simple_name(uarmc));
 	    err++;
 	} else if (uarm) {
 	    if (noisy) already_wearing("some armor");
@@ -1338,7 +1406,7 @@
 			ublindf->otyp==LENSES ? "some lenses" : "a blindfold");
 		return(0);
 	}
-	otmp = getobj(accessories, "wear");
+	otmp = getobj(accessories, "put on");
 	if(!otmp) return(0);
 	if(otmp->owornmask & (W_RING | W_AMUL | W_TOOL)) {
 		already_wearing(c_that_);
@@ -1350,6 +1418,10 @@
 	}
 	if(otmp == uwep)
 		setuwep((struct obj *)0);
+	if(otmp == uswapwep)
+		setuswapwep((struct obj *) 0);
+	if(otmp == uquiver)
+		setuqwep((struct obj *) 0);
 	if(otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) {
 		if(nolimbs(youmonst.data)) {
 			You("cannot make the ring stick to your body.");
@@ -1438,7 +1510,8 @@
 		Blindf_on(otmp);
 		return(1);
 	}
-	prinv((char *)0, otmp, 0L);
+	if (is_worn(otmp))
+	    prinv((char *)0, otmp, 0L);
 	return(1);
 }
 
@@ -1464,6 +1537,7 @@
 	if(uright && uright->otyp == RIN_PROTECTION) uac -= uright->spe;
 	if (HProtection & INTRINSIC) uac -= u.ublessed;
 	uac -= u.uspellprot;
+	if (uac < -128) uac = -128;	/* u.uac is an schar */
 	if(uac != u.uac){
 		u.uac = uac;
 		flags.botl = 1;
@@ -1552,60 +1626,17 @@
 	return(otmph);
 }
 
+/* erode some arbitrary armor worn by the victim */
 void
-erode_armor(victim,acid_dmg)
+erode_armor(victim, acid_dmg)
 struct monst *victim;
 boolean acid_dmg;
 {
-	register struct obj *otmph = some_armor(victim);
-	int erosion;
-	boolean vismon = (victim != &youmonst) && canseemon(victim);
+	struct obj *otmph = some_armor(victim);
 
-	if (!otmph) return;
-	erosion = acid_dmg  ? otmph->oeroded2 : otmph->oeroded;
-	if (otmph != uarmf) {
-	    if (otmph->greased) {
-		grease_protect(otmph,(char *)0,FALSE,victim);
-		return;
-	    }
-	    if (otmph->oerodeproof ||
-		(acid_dmg ? !is_corrodeable(otmph) : !is_rustprone(otmph))) {
-		if (flags.verbose || !(otmph->oerodeproof && otmph->rknown)) {
-		    if (victim == &youmonst)
-			Your("%s not affected.", aobjnam(otmph, "are"));
-		    else if (vismon)
-			pline("%s's %s not affected.", Monnam(victim),
-			    aobjnam(otmph, "are"));
-		}
-		if (otmph->oerodeproof) otmph->rknown = TRUE;
-		return;
-	    }
-	    if (erosion < MAX_ERODE) {
-		if (victim == &youmonst)
-		    Your("%s%s!", aobjnam(otmph, acid_dmg ? "corrode" : "rust"),
-			erosion+1 == MAX_ERODE ? " completely" :
-			erosion ? " further" : "");
-		else if (vismon)
-		    pline("%s's %s%s!", Monnam(victim),
-			aobjnam(otmph, acid_dmg ? "corrode" : "rust"),
-			erosion+1 == MAX_ERODE ? " completely" :
-			erosion ? " further" : "");
-		if (acid_dmg)
-		    otmph->oeroded2++;
-		else
-		    otmph->oeroded++;
-		return;
-	    }
-	    if (flags.verbose) {
-		if (victim == &youmonst)
-		    Your("%s completely %s.",
-			 aobjnam(otmph, Blind ? "feel" : "look"),
-			 acid_dmg ? "corroded" : "rusty");
-		else if (vismon)
-		    pline("%s's %s completely %s.", Monnam(victim),
-			 aobjnam(otmph, "look"),
-			 acid_dmg ? "corroded" : "rusty");
-	    }
+	if (otmph && (otmph != uarmf)) {
+	    erode_obj(otmph, acid_dmg, FALSE);
+	    if (carried(otmph)) update_inventory();
 	}
 }
 
@@ -1614,16 +1645,21 @@
 select_off(otmp)
 register struct obj *otmp;
 {
+	char buf[BUFSZ];
+
 	if(!otmp) return(0);
 	if(cursed(otmp)) return(0);
 	if((otmp->oclass==RING_CLASS || otmp->otyp == MEAT_RING)
 				&& nolimbs(youmonst.data))
 		return(0);
 	if(welded(uwep) && (otmp==uarmg || otmp==uright || (otmp==uleft
-			&& bimanual(uwep))))
+			&& bimanual(uwep)))) {
+		You("cannot free a weapon hand to take off the ring.");
 		return(0);
+	}
 	if(uarmg && uarmg->cursed && (otmp==uright || otmp==uleft)) {
 		uarmg->bknown = TRUE;
+		You("cannot remove your gloves to take off the ring.");
 		return(0);
 	}
 	if(otmp == uarmf && u.utrap && (u.utraptype == TT_BEARTRAP ||
@@ -1635,11 +1671,15 @@
 			|| otmp==uarmu
 #endif
 					) && uarmc && uarmc->cursed) {
+		Strcpy(buf, the(xname(uarmc)));
+		You("cannot remove %s to take off %s.", buf, the(xname(otmp)));
 		uarmc->bknown = TRUE;
 		return(0);
 	}
 #ifdef TOURIST
 	if(otmp==uarmu && uarm && uarm->cursed) {
+		Strcpy(buf, the(xname(uarm)));
+		You("cannot remove %s to take off %s.", buf, the(xname(otmp)));
 		uarm->bknown = TRUE;
 		return(0);
 	}
@@ -1719,11 +1759,7 @@
 	  otmp = uright;
 	  if(!cursed(otmp)) Ring_off(uright);
 	} else if (taking_off == WORN_BLINDF) {
-	  if(!cursed(ublindf)) {
-	    setworn((struct obj *)0, ublindf->owornmask);
-	    if(!Blinded) make_blinded(1L,FALSE); /* See on next move */
-	    else	 You("still cannot see.");
-	  }
+	  if (!cursed(ublindf)) Blindf_off(ublindf);
 	} else impossible("do_takeoff: taking off %lx", taking_off);
 
 	return(otmp);
@@ -1844,7 +1880,7 @@
 
     add_valid_menu_class(0); /* reset */
     if (flags.menu_style != MENU_TRADITIONAL ||
-	    (result = ggetobj("take off", select_off, 0, FALSE)) < -1)
+	    (result = ggetobj("take off", select_off, 0, FALSE, (unsigned *)0)) < -1)
 	result = menu_remarm(result);
 
     if (takeoff_mask)
@@ -1880,7 +1916,7 @@
 	free((genericptr_t) pick_list);
     } else if (flags.menu_style == MENU_COMBINATION) {
 	all_worn_categories = FALSE;
-	if (ggetobj("take off", select_off, 0, TRUE) == -2)
+	if (ggetobj("take off", select_off, 0, TRUE, (unsigned *)0) == -2)
 	    all_worn_categories = TRUE;
     }
 
@@ -1892,7 +1928,7 @@
 	for (i = 0; i < n; i++)
 	    (void) select_off(pick_list[i].item.a_obj);
 	free((genericptr_t) pick_list);
-    } else if (n < 0) {
+    } else if (n < 0 && flags.menu_style != MENU_COMBINATION) {
 	There("is nothing else you can remove or unwield.");
     }
     return 0;
@@ -1908,7 +1944,7 @@
 			(!obj_resists(otmp, 0, 90)))
 
 	if (DESTROY_ARM(uarmc)) {
-		Your("cloak crumbles and turns to dust!");
+		Your("%s crumbles and turns to dust!", cloak_simple_name(uarmc));
 		(void) Cloak_off();
 		useup(otmp);
 	} else if (DESTROY_ARM(uarm)) {
diff -Naurd ../nethack-3.3.1/src/dog.c ./src/dog.c
--- ../nethack-3.3.1/src/dog.c Sun Jul 16 23:15:49 2000
+++ ./src/dog.c Fri Mar 22 14:40:55 2002
@@ -15,6 +15,7 @@
 {
 	mtmp->mtame = is_domestic(mtmp->data) ? 10 : 5;
 	mtmp->mpeaceful = 1;
+	mtmp->mavenge = 0;
 	set_malign(mtmp); /* recalc alignment now that it's tamed */
 	mtmp->mleashed = 0;
 	mtmp->meating = 0;
@@ -90,6 +91,7 @@
 		    if (!quietly)
 		       You("get a bad feeling about this.");
 		    mtmp->mpeaceful = 0;
+		    set_malign(mtmp);
 		}
 	    }
 	    /* if figurine has been named, give same name to the monster */
@@ -118,6 +120,8 @@
 	int   pettype;
 	static int petname_used = 0;
 
+	if (preferred_pet == 'n') return((struct monst *) 0);
+
 	pettype = pet_type();
 	if (pettype == PM_LITTLE_DOG)
 		petname = dogname;
@@ -410,6 +414,13 @@
 		mtmp->mtame = mtmp->mpeaceful = 0;
 	}
 
+	if (!mtmp->mtame && mtmp->mleashed) {
+	    /* leashed monsters should always be with hero, consequently
+	       never losing any time to be accounted for later */
+	    impossible("catching up for leashed monster?");
+	    m_unleash(mtmp, FALSE);
+	}
+
 	/* recover lost hit points */
 	if (!regenerates(mtmp->data)) imv /= 20;
 	if (mtmp->mhp + imv >= mtmp->mhpmax)
@@ -442,7 +453,9 @@
 		   the amulet; if you don't have it, will chase you
 		   only if in range. -3. */
 			(u.uhave.amulet && mtmp->iswiz))
-			&& !mtmp->msleeping && mtmp->mcanmove) {
+		&& !mtmp->msleeping && mtmp->mcanmove
+		/* monster won't follow if it hasn't noticed you yet */
+		&& !(mtmp->mstrategy & STRAT_WAITFORU)) {
 		stay_behind = FALSE;
 		if (mtmp->mtame && mtmp->meating) {
 			if (canseemon(mtmp))
@@ -453,18 +466,21 @@
 			    pline("%s seems very disoriented for a moment.",
 				Monnam(mtmp));
 			stay_behind = TRUE;
+		} else if (mtmp->mtame && mtmp->mtrapped) {
+			if (canseemon(mtmp))
+			    pline("%s is still trapped.", Monnam(mtmp));
+			stay_behind = TRUE;
 		}
-		if (stay_behind
 #ifdef STEED
-				&& mtmp != u.usteed
+		if (mtmp == u.usteed) stay_behind = FALSE;
 #endif
-				) {
+		if (stay_behind) {
 			if (mtmp->mleashed) {
 				pline("%s leash suddenly comes loose.",
 					humanoid(mtmp->data)
 					    ? (mtmp->female ? "Her" : "His")
 					    : "Its");
-				m_unleash(mtmp);
+				m_unleash(mtmp, FALSE);
 			}
 			continue;
 		}
@@ -503,7 +519,7 @@
 		/* this can happen if your quest leader ejects you from the
 		   "home" level while a leashed pet isn't next to you */
 		pline("%s leash goes slack.", s_suffix(Monnam(mtmp)));
-		m_unleash(mtmp);
+		m_unleash(mtmp, FALSE);
 	    }
 	}
 }
@@ -541,14 +557,13 @@
 	    obj->no_charge = 0;
 	}
 
+	if (mtmp->mleashed) {
+		mtmp->mtame--;
+		m_unleash(mtmp, TRUE);
+	}
 	relmon(mtmp);
 	mtmp->nmon = migrating_mons;
 	migrating_mons = mtmp;
-	if (mtmp->mleashed)  {
-		m_unleash(mtmp);
-		mtmp->mtame--;
-		pline_The("leash comes off!");
-	}
 	newsym(mtmp->mx,mtmp->my);
 
 	new_lev.dnum = ledger_to_dnum((xchar)tolev);
@@ -648,10 +663,11 @@
 		return(TABU);
 	    if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj))
 		return(ACCFOOD);
-	    if (metallivorous(mon->data) && is_metallic(obj))
+	    if (metallivorous(mon->data) && is_metallic(obj) && (is_rustprone(obj) || mon->data != &mons[PM_RUST_MONSTER])) {
 		/* Non-rustproofed ferrous based metals are preferred. */
-		return(objects[obj->otyp].oc_material == IRON &&
-		       !obj->oerodeproof ? DOGFOOD : ACCFOOD);
+		return((is_rustprone(obj) && !obj->oerodeproof) ? DOGFOOD :
+			ACCFOOD);
+	    }
 	    if(!obj->cursed && obj->oclass != BALL_CLASS &&
 						obj->oclass != CHAIN_CLASS)
 		return(APPORT);
@@ -711,7 +727,7 @@
 			  Monnam(mtmp), the(xname(obj)),
 			  !big_corpse ? "." : ", or vice versa!");
 		} else if (cansee(mtmp->mx,mtmp->my))
-		    pline("%s stops.", The(xname(obj)));
+		    pline("%s.", Tobjnam(obj, "stop"));
 		/* dog_eat expects a floor object */
 		place_object(obj, mtmp->mx, mtmp->my);
 		(void) dog_eat(mtmp, obj, mtmp->mx, mtmp->my, FALSE);
@@ -730,6 +746,9 @@
 	    (is_demon(mtmp->data) && !is_demon(youmonst.data)) ||
 	    (obj && dogfood(mtmp, obj) >= MANFOOD)) return (struct monst *)0;
 
+	if (mtmp->m_id == quest_status.leader_m_id)
+	    return((struct monst *)0);
+
 	/* make a new monster which has the pet extension */
 	mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
 	*mtmp2 = *mtmp;
@@ -796,6 +815,13 @@
     		mtmp->mpeaceful = mtmp->mtame = 0;
     	}
     }
+    if (!mtmp->mtame) {
+	newsym(mtmp->mx, mtmp->my);
+	/* a life-saved monster might be leashed;
+	   don't leave it that way if it's no longer tame */
+	if (mtmp->mleashed) m_unleash(mtmp, TRUE);
+    }
+
     /* if its still a pet, start a clean pet-slate now */
     if (has_edog && mtmp->mtame) {
 	EDOG(mtmp)->revivals++;
@@ -814,12 +840,19 @@
 	else mtmp->mtame--;
 
 	if (mtmp->mtame && !mtmp->isminion)
-		EDOG(mtmp)->abuse++;
+	    EDOG(mtmp)->abuse++;
 
-	if (mtmp->mtame && rn2(mtmp->mtame)) yelp(mtmp);
-	else growl(mtmp);	/* give them a moment's worry */
+	if (!mtmp->mtame && mtmp->mleashed)
+	    m_unleash(mtmp, TRUE);
+
+	/* don't make a sound if pet is in the middle of leaving the level */
+	/* newsym isn't necessary in this case either */
+	if (mtmp->mx != 0) {
+	    if (mtmp->mtame && rn2(mtmp->mtame)) yelp(mtmp);
+	    else growl(mtmp);	/* give them a moment's worry */
 	
-	if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my);
+	    if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my);
+	}
 }
 
 #endif /* OVLB */
diff -Naurd ../nethack-3.3.1/src/dogmove.c ./src/dogmove.c
--- ../nethack-3.3.1/src/dogmove.c Sun Jul 16 02:53:14 2000
+++ ./src/dogmove.c Fri Mar 22 14:40:59 2002
@@ -16,6 +16,8 @@
 STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int));
 
 STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *));
+STATIC_DCL boolean FDECL(can_reach_food,(struct monst *,XCHAR_P,XCHAR_P,XCHAR_P,
+    XCHAR_P));
 
 STATIC_OVL struct obj *
 DROPPABLES(mon)
@@ -156,7 +158,7 @@
 	} else
 	/* hack: observe the action if either new or old location is in view */
 	if (cansee(x, y) || cansee(mtmp->mx, mtmp->my))
-	    pline("%s %s %s.", Monnam(mtmp),
+	    pline("%s %s %s.", noit_Monnam(mtmp),
 		  devour ? "devours" : "eats",
 		  (obj->oclass == FOOD_CLASS) ?
 			singular(obj, doname) : doname(obj));
@@ -182,9 +184,10 @@
 	    delobj(obj);
 	} else if (obj == uchain)
 	    unpunish();
-	else if (obj->quan > 1L && obj->oclass == FOOD_CLASS)
+	else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) {
 	    obj->quan--;
-	else
+	    obj->owt = weight(obj);
+	} else
 	    delobj(obj);
 
 	if (poly) {
@@ -198,7 +201,7 @@
 #ifdef STEED
 	    mtmp->misc_worn_check = mw;
 #endif
-	    if (newcham(mtmp, (struct permonst *)0) &&
+	    if (newcham(mtmp, (struct permonst *)0, FALSE) &&
 			cansee(mtmp->mx, mtmp->my)) {
 		uchar save_mnamelth = mtmp->mnamelth;
 		mtmp->mnamelth = 0;
@@ -242,6 +245,7 @@
 		    beg(mtmp);
 		else
 		    You_feel("worried about %s.", y_monnam(mtmp));
+		stop_occupation();
 	    } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
 	    dog_died:
 		if (mtmp->mleashed)
@@ -375,7 +379,8 @@
 			continue;
 		    if (cursed_object_at(nx, ny))
 			continue;
-		    if (otyp < MANFOOD) {
+		    if (otyp < MANFOOD &&
+			    can_reach_food(mtmp, mtmp->mx, mtmp->my, nx, ny)) {
 			if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) {
 			    gx = nx;
 			    gy = ny;
@@ -556,6 +561,11 @@
 
 	    }
 	}
+	if (!Conflict && !mtmp->mconf &&
+	    mtmp == u.ustuck && !sticks(youmonst.data)) {
+	    unstuck(mtmp);	/* swallowed case handled above */
+	    You("get released!");
+	}
 	if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
 		allowflags |= OPENDOOR;
 		if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR;
@@ -702,8 +712,8 @@
 		if (info[chi] & ALLOW_U) {
 			if (mtmp->mleashed) { /* play it safe */
 				pline("%s breaks loose of %s leash!",
-				      Monnam(mtmp), his[pronoun_gender(mtmp)]);
-				m_unleash(mtmp);
+				      Monnam(mtmp), mhis(mtmp));
+				m_unleash(mtmp, FALSE);
 			}
 			(void) mattacku(mtmp);
 			return(0);
@@ -767,6 +777,50 @@
 	return(1);
 }
 
+/* Hack to prevent a dog from being endlessly stuck near a piece of food that
+ * it can't reach, such as caught in a teleport scroll niche.  It recursively
+ * checks to see if the squares inbetween are good.  The checking could be a
+ * little smarter; a full check would probably be useful in m_move() too.
+ * Since the maximum food distance is 5, this should never be more than 5 calls
+ * deep.
+ */
+STATIC_OVL  boolean
+can_reach_food(mon, mx, my, fx, fy)
+struct monst *mon;
+xchar mx, my, fx, fy;
+{
+    int i, j;
+    int dist;
+
+    if (mx == fx && my == fy) return TRUE;
+    if (!isok(mx, my)) return FALSE; /* should not happen */
+    
+    dist = dist2(mx, my, fx, fy);
+    for(i=mx-1; i<=mx+1; i++) {
+	for(j=my-1; j<=my+1; j++) {
+	    if (!isok(i, j))
+		continue;
+	    if (dist2(i, j, fx, fy) >= dist)
+		continue;
+	    if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data) &&
+				    (!may_dig(i,j) || !tunnels(mon->data)))
+		continue;
+	    if (IS_DOOR(levl[i][j].typ) &&
+				(levl[i][j].doormask & (D_CLOSED | D_LOCKED)))
+		continue;
+	    if (is_pool(i, j) && !is_swimmer(mon->data))
+		continue;
+	    if (is_lava(i, j) && !likes_lava(mon->data))
+		continue;
+	    if (sobj_at(BOULDER,i,j) && !throws_rocks(mon->data))
+		continue;
+	    if (can_reach_food(mon, i, j, fx, fy))
+		return TRUE;
+	}
+    }
+    return FALSE;
+}
+
 #endif /* OVL0 */
 #ifdef OVLB
 
diff -Naurd ../nethack-3.3.1/src/dokick.c ./src/dokick.c
--- ../nethack-3.3.1/src/dokick.c Tue Jul 18 03:54:18 2000
+++ ./src/dokick.c Fri Mar 22 14:40:59 2002
@@ -23,8 +23,6 @@
 
 static NEARDATA struct obj *kickobj;
 
-#define IS_SHOP(x)	(rooms[x].rtype >= SHOPBASE)
-
 static const char kick_passes_thru[] = "kick passes harmlessly through";
 
 STATIC_OVL void
@@ -66,7 +64,8 @@
 	/* it is unchivalrous to attack the defenseless or from behind */
 	if (Role_if(PM_KNIGHT) &&
 		u.ualign.type == A_LAWFUL && u.ualign.record > -10 &&
-		(!mon->mcanmove || mon->msleeping || mon->mflee)) {
+		(!mon->mcanmove || mon->msleeping ||
+		(mon->mflee && !mon->mavenge))) {
 	    You_feel("like a caitiff!");
 	    adjalign(-1);
 	}
@@ -74,12 +73,10 @@
 	/* squeeze some guilt feelings... */
 	if(mon->mtame) {
 	    abuse_dog(mon);
-	    mon->mflee = mon->mtame ? 1 : 0;
-#ifdef HISX
-	    mon->mfleetim = mon->mfleetim + (dmg ? rnd(dmg) : 1);
-#else
-	    mon->mfleetim += (dmg ? rnd(dmg) : 1);
-#endif
+	    if (mon->mtame)
+		monflee(mon, (dmg ? rnd(dmg) : 1), FALSE, FALSE);
+	    else
+		mon->mflee = 0;
 	}
 
 	if (dmg > 0) {
@@ -97,19 +94,20 @@
 	dmg += u.udaminc;	/* add ring(s) of increase damage */
 	if (dmg > 0)
 		mon->mhp -= dmg;
-	if(mon->mhp > 0 && martial() && !bigmonst(mon->data) && !rn2(3)
-			&& mon->mcanmove && mon != u.ustuck) {
+	if (mon->mhp > 0 && martial() && !bigmonst(mon->data) && !rn2(3) &&
+	    mon->mcanmove && mon != u.ustuck && !mon->mtrapped) {
 		/* see if the monster has a place to move into */
 		mdx = mon->mx + u.dx;
 		mdy = mon->my + u.dy;
 		if(goodpos(mdx, mdy, mon)) {
 			pline("%s reels from the blow.", Monnam(mon));
-			if (!m_in_out_region(mon, mdx, mdy)) {
+			if (m_in_out_region(mon, mdx, mdy)) {
 			    remove_monster(mon->mx, mon->my);
 			    newsym(mon->mx, mon->my);
 			    place_monster(mon, mdx, mdy);
 			    newsym(mon->mx, mon->my);
 			    set_apparxy(mon);
+			    (void) mintrap(mon);
 			}
 		}
 	}
@@ -165,6 +163,8 @@
 		    You("kick %s.", mon_nam(mon));
 		    sum = damageum(mon, uattk);
 		    (void)passive(mon, (boolean)(sum > 0), (sum != 2), AT_KICK);
+		    if (sum == 2)
+			break;		/* Defender died */
 		} else {
 		    missum(mon, uattk);
 		    (void)passive(mon, 0, 1, AT_KICK);
@@ -217,7 +217,7 @@
 			pline("%s %s, %s evading your %skick.", Monnam(mon),
 				(can_teleport(mon->data) ? "teleports" :
 				 is_floater(mon->data) ? "floats" :
-				 is_flyer(mon->data) ? "flutters" :
+				 is_flyer(mon->data) ? "swoops" :
 				 (nolimbs(mon->data) || slithy(mon->data)) ?
 					"slides" : "jumps"),
 				clumsy ? "easily" : "nimbly",
@@ -263,16 +263,16 @@
 				if (robbed < 0) robbed = 0;
 				pline_The("amount %scovers %s recent losses.",
 				      !robbed ? "" : "partially ",
-				      his[mtmp->female]);
+				      mhis(mtmp));
 				ESHK(mtmp)->robbed = robbed;
 				if(!robbed)
 					make_happy_shk(mtmp, FALSE);
 			} else {
 				if(mtmp->mpeaceful) {
 				    ESHK(mtmp)->credit += gold->quan;
-				    You("have %ld zorkmid%s in credit.",
+				    You("have %ld %s in credit.",
 					ESHK(mtmp)->credit,
-					plur(ESHK(mtmp)->credit));
+					currency(ESHK(mtmp)->credit));
 				} else verbalize("Thanks, scum!");
 			}
 		} else if (mtmp->ispriest) {
@@ -346,16 +346,16 @@
 
 	if(kickobj->otyp == CORPSE && touch_petrifies(&mons[kickobj->corpsenm])
 			&& !Stone_resistance && !uarmf) {
-		char kbuf[BUFSZ];
+	    char kbuf[BUFSZ];
 
-		You("kick the %s corpse with your bare %s.",
-				mons[kickobj->corpsenm].mname, makeplural(body_part(FOOT)));
+	    You("kick the %s with your bare %s.",
+		corpse_xname(kickobj, TRUE), makeplural(body_part(FOOT)));
 	    if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
 		You("turn to stone...");
 		killer_format = KILLED_BY;
 		/* KMH -- otmp should be kickobj */
-		Sprintf(kbuf, "kicking a %s corpse without boots",
-			mons[kickobj->corpsenm].mname);
+		Sprintf(kbuf, "kicking %s without boots",
+			an(corpse_xname(kickobj, TRUE)));
 		killer = kbuf;
 		done(STONING);
 	    }
@@ -407,6 +407,13 @@
 				result = "cracking";
 			}
 			if (result) {
+				if (otmp->otyp == MIRROR)
+				    change_luck(-2);
+				/* eggs laid by you */
+				/* penalty is -1 per egg, max 5, but it's always
+				   exactly 1 that breaks */
+				if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM)
+				    change_luck(-1);
 				You_hear("a muffled %s.",result);
 				if(costly) loss += stolen_value(otmp, x, y,
 					    (boolean)shkp->mpeaceful, TRUE);
@@ -420,11 +427,12 @@
 		}
 		if(costly && loss) {
 		    if(!insider) {
-			You("caused %ld zorkmids worth of damage!", loss);
+			You("caused %ld %s worth of damage!",
+			    loss, currency(loss));
 			make_angry_shk(shkp, x, y);
 		    } else {
-			You("owe %s %ld zorkmids for objects destroyed.",
-			    mon_nam(shkp), loss);
+			You("owe %s %ld %s for objects destroyed.",
+			    mon_nam(shkp), loss, currency(loss));
 		    }
 		}
 
@@ -455,15 +463,15 @@
 				 || IS_ROCK(levl[u.ux][u.uy].typ)
 				 || closed_door(u.ux, u.uy)) {
 			if (Blind) pline("It doesn't come loose.");
-			else pline("%s do%sn't come loose.",
+			else pline("%s %sn't come loose.",
 				The(distant_name(kickobj, xname)),
-				(kickobj->quan == 1L) ? "es" : "");
+				otense(kickobj, "do"));
 			return(!rn2(3) || martial());
 		}
 		if (Blind) pline("It comes loose.");
-		else pline("%s come%s loose.",
+		else pline("%s %s loose.",
 			   The(distant_name(kickobj, xname)),
-			   (kickobj->quan == 1L) ? "s" : "");
+			   otense(kickobj, "come"));
 		obj_extract_self(kickobj);
 		newsym(x, y);
 		if (costly && (!costly_spot(u.ux, u.uy)
@@ -488,12 +496,11 @@
 	    return(!rn2(3) || martial());
 	}
 
-	if (kickobj->quan > 1L && !isgold) (void) splitobj(kickobj, 1L);
+	if (kickobj->quan > 1L && !isgold) kickobj = splitobj(kickobj, 1L);
 
 	if (slide && !Blind)
-	    pline("Whee!  %s slide%s across the %s.", Doname2(kickobj),
-		kickobj->quan > 1L ? "" : "s",
-		surface(x,y));
+	    pline("Whee!  %s %s across the %s.", Doname2(kickobj),
+		  otense(kickobj, "slide"), surface(x,y));
 
 	obj_extract_self(kickobj);
 	(void) snuff_candle(kickobj);
@@ -541,12 +548,12 @@
 
 	if (kickobj) what = distant_name(kickobj,doname);
 	else if (IS_DOOR(maploc->typ)) what = "a door";
+	else if (IS_TREE(maploc->typ)) what = "a tree";
 	else if (IS_STWALL(maploc->typ)) what = "a wall";
 	else if (IS_ROCK(maploc->typ)) what = "a rock";
 	else if (IS_THRONE(maploc->typ)) what = "a throne";
 	else if (IS_FOUNTAIN(maploc->typ)) what = "a fountain";
 	else if (IS_GRAVE(maploc->typ)) what = "a headstone";
-	else if (IS_TREE(maploc->typ)) what = "a tree";
 #ifdef SINKS
 	else if (IS_SINK(maploc->typ)) what = "a sink";
 #endif
@@ -830,10 +837,10 @@
 		    if (!rn2(2) && !(maploc->looted & TREE_LOOTED) &&
 			  (treefruit = rnd_treefruit_at(x, y))) {
 			treefruit->quan = (long)(8 - rnl(8));
-			if (treefruit->quan > 1L)
-				pline("Some %s fall from the tree!", xname(treefruit));
+			if (is_plural(treefruit))
+			    pline("Some %s fall from the tree!", xname(treefruit));
 			else
-				pline("%s falls from the tree!", An(xname(treefruit)));
+			    pline("%s falls from the tree!", An(xname(treefruit)));
 			scatter(x,y,2,MAY_HIT,treefruit);
 			exercise(A_DEX, TRUE);
 			exercise(A_WIS, TRUE);	/* discovered a new food source! */
@@ -844,7 +851,7 @@
 		    	int cnt = rnl(5);
 		    	coord mm;
 		    	mm.x = x; mm.y = y;
-			pline("You've disturbed the occupants!");
+			pline("You've attracted the tree's former occupants!");
 			while (cnt--)
 			    if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE]))
 				(void) makemon(&mons[PM_KILLER_BEE],
@@ -991,7 +998,10 @@
 			mtmp->data == &mons[PM_WATCH_CAPTAIN]) &&
 			couldsee(mtmp->mx, mtmp->my) &&
 			mtmp->mpeaceful) {
-			pline("%s yells:", Amonnam(mtmp));
+			if (canspotmon(mtmp))
+			    pline("%s yells:", Amonnam(mtmp));
+			else
+			    You_hear("someone yell:");
 			verbalize("Halt, thief!  You're under arrest!");
 			(void) angry_guards(FALSE);
 			break;
@@ -1007,7 +1017,10 @@
 		    if ((mtmp->data == &mons[PM_WATCHMAN] ||
 				mtmp->data == &mons[PM_WATCH_CAPTAIN]) &&
 			    mtmp->mpeaceful && couldsee(mtmp->mx, mtmp->my)) {
-			pline("%s yells:", Amonnam(mtmp));
+			if (canspotmon(mtmp))
+			    pline("%s yells:", Amonnam(mtmp));
+			else
+			    You_hear("someone yell:");
 			if(levl[x][y].looted & D_WARNED) {
 			    verbalize("Halt, vandal!  You're under arrest!");
 			    (void) angry_guards(FALSE);
@@ -1150,7 +1163,7 @@
 
 	if(costly && shkp && price) {
 		if(ESHK(shkp)->robbed > robbed) {
-		    You("removed %ld zorkmids worth of goods!", price);
+		    You("removed %ld %s worth of goods!", price, currency(price));
 		    if(cansee(shkp->mx, shkp->my)) {
 			if(ESHK(shkp)->customer[0] == 0)
 			    (void) strncpy(ESHK(shkp)->customer,
@@ -1163,10 +1176,12 @@
 		    (void) angry_guards(FALSE);
 		    return;
 		}
-		if(ESHK(shkp)->debit > debit)
-		    You("owe %s %ld zorkmids for goods lost.",
+		if(ESHK(shkp)->debit > debit) {
+		    long amt = (ESHK(shkp)->debit - debit);
+		    You("owe %s %ld %s for goods lost.",
 			Monnam(shkp),
-			(ESHK(shkp)->debit - debit));
+			amt, currency(amt));
+		}
 	}
 
 }
@@ -1243,6 +1258,33 @@
 		otmp->no_charge = 0;
 	}
 
+	if (otmp == uwep) setuwep((struct obj *)0);
+	if (otmp == uquiver) setuqwep((struct obj *)0);
+	if (otmp == uswapwep) setuswapwep((struct obj *)0);
+
+	/* some things break rather than ship */
+	if (breaktest(otmp)) {
+	    char *result;
+	    if (objects[otmp->otyp].oc_material == GLASS
+#ifdef TOURIST
+		|| otmp->otyp == EXPENSIVE_CAMERA
+#endif
+		) {
+		if (otmp->otyp == MIRROR)
+		    change_luck(-2);
+		result = "crash";
+	    } else {
+		/* penalty for breaking eggs laid by you */
+		if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM)
+		    change_luck((schar) -min(otmp->quan, 5L));
+		result = "splat";
+	    }
+	    You_hear("a muffled %s.",result);
+	    obj_extract_self(otmp);
+	    obfree(otmp, (struct obj *) 0);
+	    return TRUE;
+	}
+
 	add_to_migration(otmp);
 	otmp->ox = cc.x;
 	otmp->oy = cc.y;
@@ -1319,19 +1361,18 @@
 		 xname(otmp));
 
 	if(num) { /* means: other objects are impacted */
-	    Sprintf(eos(obuf), " hit%s %s object%s",
-		      otmp->quan == 1L ? "s" : "",
-		      num == 1L ? "another" : "other",
-		      num > 1L ? "s" : "");
+	    Sprintf(eos(obuf), " %s %s object%s",
+		    otense(otmp, "hit"),
+		    num == 1L ? "another" : "other",
+		    num > 1L ? "s" : "");
 	    if(nodrop)
 		Sprintf(eos(obuf), ".");
 	    else
-		Sprintf(eos(obuf), " and fall%s %s.",
-			otmp->quan == 1L ? "s" : "", gate_str);
+		Sprintf(eos(obuf), " and %s %s.",
+			otense(otmp, "fall"), gate_str);
 	    pline("%s", obuf);
 	} else if(!nodrop)
-	    pline("%s fall%s %s.", obuf,
-		  otmp->quan == 1L ? "s" : "", gate_str);
+	    pline("%s %s %s.", obuf, otense(otmp, "fall"), gate_str);
 }
 
 /* migration destination for objects which fall down to next level */
diff -Naurd ../nethack-3.3.1/src/dothrow.c ./src/dothrow.c
--- ../nethack-3.3.1/src/dothrow.c Sun Jul 16 02:42:57 2000
+++ ./src/dothrow.c Fri Mar 22 14:41:01 2002
@@ -9,6 +9,7 @@
 STATIC_DCL int FDECL(throw_obj, (struct obj *,int));
 STATIC_DCL void NDECL(autoquiver);
 STATIC_DCL int FDECL(gem_accept, (struct monst *, struct obj *));
+STATIC_DCL void FDECL(tmiss, (struct obj *, struct monst *));
 STATIC_DCL int FDECL(throw_gold, (struct obj *));
 STATIC_DCL void FDECL(check_shop_obj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P));
 STATIC_DCL void FDECL(breakobj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P,BOOLEAN_P));
@@ -16,6 +17,7 @@
 STATIC_DCL boolean FDECL(toss_up,(struct obj *, BOOLEAN_P));
 STATIC_DCL boolean FDECL(throwing_weapon, (struct obj *));
 STATIC_DCL void FDECL(sho_obj_return_to_u, (struct obj *obj));
+STATIC_DCL boolean FDECL(mhurtle_step, (genericptr_t,int,int));
 
 
 static NEARDATA const char toss_objs[] =
@@ -37,6 +39,7 @@
 	int multishot = 1;
 	schar skill;
 	long wep_mask;
+	boolean twoweap;
 
 	/* ask "in what direction?" */
 	if (!getdir((char *)0)) {
@@ -66,6 +69,7 @@
 		You("cannot throw an object at yourself.");
 		return(0);
 	}
+	u_wipe_engr(2);
 	if (!uarmg && !Stone_resistance && (obj->otyp == CORPSE &&
 		    touch_petrifies(&mons[obj->corpsenm]))) {
 		You("throw the %s corpse with your bare %s.",
@@ -73,7 +77,10 @@
 		Sprintf(killer_buf, "%s corpse", an(mons[obj->corpsenm].mname));
 		instapetrify(killer_buf);
 	}
-	u_wipe_engr(2);
+	if (welded(obj)) {
+		weldmsg(obj);
+		return 1;
+	}
 
 	/* Multishot calculations
 	 */
@@ -116,43 +123,42 @@
 	    }
 	}
 
-	if (obj->quan < multishot) multishot = (int)obj->quan;
+	if ((long)multishot > obj->quan) multishot = (int)obj->quan;
 	multishot = rnd(multishot);
 	if (shotlimit > 0 && multishot > shotlimit) multishot = shotlimit;
 
-	while (obj && multishot-- > 0) {
-		wep_mask = obj->owornmask;
-		/* Split this object off from its slot */
-		otmp = (struct obj *)0;
-		if (obj == uquiver) {
-			if(obj->quan > 1L)
-				setuqwep(otmp = splitobj(obj, 1L));
-			else
-				setuqwep((struct obj *)0);
-		} else if (obj == uswapwep) {
-			if(obj->quan > 1L)
-				setuswapwep(otmp = splitobj(obj, 1L));
-			else
-				setuswapwep((struct obj *)0);
-		} else if (obj == uwep) {
-		    if (welded(obj)) {
-			weldmsg(obj);
-			return(1);
-		    }
-		    if (obj->quan > 1L)
-			setworn(otmp = splitobj(obj, 1L), W_WEP);
-			/* not setuwep; do not change unweapon */
-		    else {
-			setuwep((struct obj *)0);
-			if (uwep) return(1); /* unwielded, died, rewielded */
-		    }
-		} else if(obj->quan > 1L)
-			otmp = splitobj(obj, 1L);
-		freeinv(obj);
-		throwit(obj, wep_mask);
-		obj = otmp;
-	}	/* while (multishot) */
-	return(1);
+	m_shot.s = ammo_and_launcher(obj,uwep) ? TRUE : FALSE;
+	/* give a message if shooting more than one, or if player
+	   attempted to specify a count */
+	if (multishot > 1 || shotlimit > 0) {
+	    /* "You shoot N arrows." or "You throw N daggers." */
+	    You("%s %d %s.",
+		m_shot.s ? "shoot" : "throw",
+		multishot,	/* (might be 1 if player gave shotlimit) */
+		(multishot == 1) ? singular(obj, xname) :  xname(obj));
+	}
+
+	wep_mask = obj->owornmask;
+	m_shot.o = obj->otyp;
+	m_shot.n = multishot;
+	for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
+	    twoweap = u.twoweap;
+	    /* split this object off from its slot if necessary */
+	    if (obj->quan > 1L) {
+		otmp = splitobj(obj, 1L);
+	    } else {
+		otmp = obj;
+		if (otmp->owornmask && otmp != uball)
+		    remove_worn_item(otmp);
+	    }
+	    freeinv(otmp);
+	    throwit(otmp, wep_mask, twoweap);
+	}
+	m_shot.n = m_shot.i = 0;
+	m_shot.o = STRANGE_OBJECT;
+	m_shot.s = FALSE;
+
+	return 1;
 }
 
 
@@ -193,49 +199,53 @@
 	register struct obj *otmp, *oammo = 0, *omissile = 0, *omisc = 0;
 
 	if (uquiver)
-		return;
+	    return;
 
 	/* Scan through the inventory */
 	for (otmp = invent; otmp; otmp = otmp->nobj) {
-		if (otmp->owornmask || otmp->oartifact || !otmp->dknown) {
-			;	/* Skip it */
-		} else if (otmp->otyp == ROCK ||
+	    if (otmp->owornmask || otmp->oartifact || !otmp->dknown) {
+		;	/* Skip it */
+	    } else if (otmp->otyp == ROCK ||
 			/* seen rocks or known flint or known glass */
 			(objects[otmp->otyp].oc_name_known &&
 			 otmp->otyp == FLINT) ||
 			(objects[otmp->otyp].oc_name_known &&
 			 otmp->oclass == GEM_CLASS &&
 			 objects[otmp->otyp].oc_material == GLASS)) {
-			if (uslinging())
-			    oammo = otmp;
-			else if (!omisc)
-			    omisc = otmp;
-		} else if (otmp->oclass == GEM_CLASS) {
-			;	/* skip non-rock gems--they're ammo but
-				   player has to select them explicitly */
-		} else if (is_ammo(otmp)) {
-			if (ammo_and_launcher(otmp, uwep))
-				/* Ammo matched with launcher (bow and arrow, crossbow and bolt) */
-				oammo = otmp;
-			else
-				/* Mismatched ammo (no better than an ordinary weapon) */
-				omisc = otmp;
-		} else if (is_missile(otmp)) {
-			/* Missile (dart, shuriken, etc.) */
-			omissile = otmp;
-		} else if (otmp->oclass == WEAPON_CLASS && !is_launcher(otmp)) {
-			/* Ordinary weapon */
-			omisc = otmp;
-		}
+		if (uslinging())
+		    oammo = otmp;
+		else if (!omisc)
+		    omisc = otmp;
+	    } else if (otmp->oclass == GEM_CLASS) {
+		;	/* skip non-rock gems--they're ammo but
+			   player has to select them explicitly */
+	    } else if (is_ammo(otmp)) {
+		if (ammo_and_launcher(otmp, uwep))
+		    /* Ammo matched with launcher (bow and arrow, crossbow and bolt) */
+		    oammo = otmp;
+		else
+		    /* Mismatched ammo (no better than an ordinary weapon) */
+		    omisc = otmp;
+	    } else if (is_missile(otmp)) {
+		/* Missile (dart, shuriken, etc.) */
+		omissile = otmp;
+	    } else if (otmp->oclass == WEAPON_CLASS && throwing_weapon(otmp)) {
+		/* Ordinary weapon */
+		if (objects[otmp->otyp].oc_skill == P_DAGGER
+			&& !omissile) 
+		    omissile = otmp;
+		else
+		    omisc = otmp;
+	    }
 	}
 
 	/* Pick the best choice */
 	if (oammo)
-		setuqwep(oammo);
+	    setuqwep(oammo);
 	else if (omissile)
-		setuqwep(omissile);
+	    setuqwep(omissile);
 	else if (omisc)
-		setuqwep(omisc);
+	    setuqwep(omisc);
 
 	return;
 }
@@ -380,7 +390,7 @@
  * Single step for the hero flying through the air from jumping, flying,
  * etc.  Called from hurtle() and jump() via walk_path().  We expect the
  * argument to be a pointer to an integer -- the range -- which is
- * used in the calculation of points off it we hit something.
+ * used in the calculation of points off if we hit something.
  *
  * Bumping into monsters won't cause damage but will wake them and make
  * them angry.  Auto-pickup isn't done, since you don't have control over
@@ -403,7 +413,8 @@
     struct obj *obj;
     struct monst *mon;
     boolean may_pass = TRUE;
-
+    struct trap *ttmp;
+    
     if (!isok(x,y)) {
 	You_feel("the spirits holding you back.");
 	return FALSE;
@@ -411,9 +422,16 @@
 
     if (!Passes_walls || !(may_pass = may_passwall(x, y))) {
 	if (IS_ROCK(levl[x][y].typ) || closed_door(x,y)) {
+	    char *s;
+
 	    pline("Ouch!");
-	    losehp(rnd(2+*range), IS_ROCK(levl[x][y].typ) ?
-		   "bumping into a wall" : "bumping into a door", KILLED_BY);
+	    if (IS_TREE(levl[x][y].typ))
+		s = "bumping into a tree";
+	    else if (IS_ROCK(levl[x][y].typ))
+		s = "bumping into a wall";
+	    else
+		s = "bumping into a door";
+	    losehp(rnd(2+*range), s, KILLED_BY);
 	    return FALSE;
 	}
 	if (levl[x][y].typ == IRONBARS) {
@@ -439,6 +457,14 @@
 	wakeup(mon);
 	return FALSE;
     }
+    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)) {
+	    You("come to an abrupt halt!");
+	    return FALSE;
+	}
+    }
 
     ox = u.ux;
     oy = u.uy;
@@ -447,6 +473,33 @@
     newsym(ox, oy);		/* update old position */
     vision_recalc(1);		/* update for new position */
     flush_screen(1);
+    /* FIXME:
+     * Each trap should really trigger on the recoil if
+     * it would trigger during normal movement. However,
+     * not all the possible side-effects of this are
+     * tested [as of 3.4.0] so we trigger those that
+     * we have tested, and offer a message for the
+     * ones that we have not yet tested.
+     */
+    if ((ttmp = t_at(x, y)) != 0) {
+    	if (ttmp->ttyp == MAGIC_PORTAL) {
+    		dotrap(ttmp,0);
+    		return FALSE;
+	} else if (ttmp->ttyp == FIRE_TRAP) {
+    		dotrap(ttmp,0);
+	} else if ((ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT ||
+		    ttmp->ttyp == HOLE || ttmp->ttyp == TRAPDOOR) &&
+		   In_sokoban(&u.uz)) {
+		/* Air currents overcome the recoil */
+    		dotrap(ttmp,0);
+		return FALSE;
+    	} else {
+		if (ttmp->tseen)
+		    You("pass right over %s %s.",
+		    	(ttmp->ttyp == ARROW_TRAP) ? "an" : "a",
+		    	defsyms[trap_to_defsym(ttmp->ttyp)].explanation);
+    	}
+    }
     if (--*range < 0)		/* make sure our range never goes negative */
 	*range = 0;
     if (*range != 0)
@@ -454,6 +507,28 @@
     return TRUE;
 }
 
+STATIC_OVL boolean
+mhurtle_step(arg, x, y)
+    genericptr_t arg;
+    int x, y;
+{
+	struct monst *mon = (struct monst *)arg;
+
+	/* TODO: Treat walls, doors, iron bars, pools, lava, etc. specially
+	 * rather than just stopping before.
+	 */
+	if (goodpos(x, y, mon) && m_in_out_region(mon, x, y)) {
+	    remove_monster(mon->mx, mon->my);
+	    newsym(mon->mx, mon->my);
+	    place_monster(mon, x, y);
+	    newsym(mon->mx, mon->my);
+	    set_apparxy(mon);
+	    (void) mintrap(mon);
+	    return TRUE;
+	}
+	return FALSE;
+}
+
 /*
  * The player moves through the air for a few squares as a result of
  * throwing or kicking something.
@@ -497,6 +572,13 @@
     nomul(-range);
     if (verbose)
 	You("%s in the opposite direction.", range > 1 ? "hurtle" : "float");
+    /* if we're in the midst of shooting multiple projectiles, stop */
+    if (m_shot.i < m_shot.n) {
+	/* last message before hurtling was "you shoot N arrows" */
+	You("stop %sing after the first %s.",
+	    m_shot.s ? "shoot" : "throw", m_shot.s ? "shot" : "toss");
+	m_shot.n = m_shot.i;	/* make current shot be the last */
+    }
     if (In_sokoban(&u.uz))
 	change_luck(-1);	/* Sokoban guilt */
     uc.x = u.ux;
@@ -507,6 +589,39 @@
     (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t)&range);
 }
 
+/* Move a monster through the air for a few squares.
+ */
+void
+mhurtle(mon, dx, dy, range)
+	struct monst *mon;
+	int dx, dy, range;
+{
+    coord mc, cc;
+
+	/* At the very least, debilitate the monster */
+	mon->movement = 0;
+	mon->mstun = 1;
+
+	/* Is the monster stuck or too heavy to push?
+	 * (very large monsters have too much inertia, even floaters and flyers)
+	 */
+	if (mon->data->msize >= MZ_HUGE || mon == u.ustuck || mon->mtrapped)
+	    return;
+
+    /* Make sure dx and dy are [-1,0,1] */
+    dx = sgn(dx);
+    dy = sgn(dy);
+    if(!range || (!dx && !dy)) return; /* paranoia */
+
+	/* Send the monster along the path */
+	mc.x = mon->mx;
+	mc.y = mon->my;
+	cc.x = mon->mx + (dx * range);
+	cc.y = mon->my + (dy * range);
+	(void) walk_path(&mc, &cc, mhurtle_step, (genericptr_t)mon);
+	return;
+}
+
 STATIC_OVL void
 check_shop_obj(obj, x, y, broken)
 register struct obj *obj;
@@ -578,6 +693,11 @@
 	int otyp = obj->otyp, ocorpsenm = obj->corpsenm;
 	int blindinc;
 
+	/* need to check for blindness result prior to destroying obj */
+	blindinc = (otyp == CREAM_PIE || otyp == BLINDING_VENOM) &&
+		   /* AT_WEAP is ok here even if attack type was AT_SPIT */
+		   can_blnd(&youmonst, &youmonst, AT_WEAP, obj) ? rnd(25) : 0;
+
 	breakmsg(obj, !Blind);
 	breakobj(obj, u.ux, u.uy, TRUE, TRUE);
 	obj = 0;	/* it's now gone */
@@ -590,13 +710,12 @@
 	case CREAM_PIE:
 	case BLINDING_VENOM:
 		pline("You've got it all over your %s!", body_part(FACE));
-		blindinc = rnd(25);
-		if (blindinc && !Blindfolded) {
-		    if (otyp != BLINDING_VENOM)
-			u.ucreamed += blindinc;
-		    else if (!Blind)
+		if (blindinc) {
+		    if (otyp == BLINDING_VENOM && !Blind)
 			pline("It blinds you!");
-		    make_blinded(Blinded + blindinc, FALSE);
+		    u.ucreamed += blindinc;
+		    make_blinded(Blinded + (long)blindinc, FALSE);
+		    if (!Blind) Your(vision_clears);
 		}
 		break;
 	default:
@@ -604,9 +723,14 @@
 	}
 	return FALSE;
     } else {		/* neither potion nor other breaking object */
-	boolean less_damage = uarmh && is_metallic(uarmh);
+	boolean less_damage = uarmh && is_metallic(uarmh), artimsg = FALSE;
 	int dmg = dmgval(obj, &youmonst);
 
+	if (obj->oartifact)
+	    /* need a fake die roll here; rn1(18,2) avoids 1 and 20 */
+	    artimsg = artifact_hit((struct monst *)0, &youmonst,
+				   obj, &dmg, rn1(18,2));
+
 	if (!dmg) {	/* probably wasn't a weapon; base damage on weight */
 	    dmg = (int) obj->owt / 100;
 	    if (dmg < 1) dmg = 1;
@@ -618,11 +742,13 @@
 	if (dmg > 1 && less_damage) dmg = 1;
 	if (dmg > 0) dmg += u.udaminc;
 	if (dmg < 0) dmg = 0;	/* beware negative rings of increase damage */
+	if (Half_physical_damage) dmg = (dmg + 1) / 2;
 
 	if (uarmh) {
-	    if (less_damage && dmg < (Upolyd ? u.mh : u.uhp))
-		pline("Fortunately, you are wearing a hard helmet.");
-	    else if (flags.verbose &&
+	    if (less_damage && dmg < (Upolyd ? u.mh : u.uhp)) {
+		if (!artimsg)
+		    pline("Fortunately, you are wearing a hard helmet.");
+	    } else if (flags.verbose &&
 		    !(obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])))
 		Your("%s does not protect you.", xname(uarmh));
 	} else if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) {
@@ -675,9 +801,10 @@
 }
 
 void
-throwit(obj, wep_mask)
+throwit(obj, wep_mask, twoweap)
 register struct obj *obj;
 long wep_mask;	/* used to re-equip returning boomerang */
+boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
 {
 	register struct monst *mon;
 	register int range, urange;
@@ -687,13 +814,13 @@
 	if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
 	    boolean slipok = TRUE;
 	    if (ammo_and_launcher(obj, uwep))
-		pline("%s misfires!", The(xname(obj)));
+		pline("%s!", Tobjnam(obj, "misfire"));
 	    else {
 		/* only slip if it's greased or meant to be thrown */
 		if (obj->greased || throwing_weapon(obj))
 		    /* BUG: this message is grammatically incorrect if obj has
 		       a plural name; greased gloves or boots for instance. */
-		    pline("%s slips as you throw it!", The(xname(obj)));
+		    pline("%s as you throw it!", Tobjnam(obj, "slip"));
 		else slipok = FALSE;
 	    }
 	    if (slipok) {
@@ -704,6 +831,19 @@
 	    }
 	}
 
+	if ((u.dx || u.dy || (u.dz < 1)) &&
+	    calc_capacity((int)obj->owt) > SLT_ENCUMBER &&
+	    (Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
+	     : (u.uhp < 10 && u.uhp != u.uhpmax)) &&
+	    obj->owt > (unsigned)((Upolyd ? u.mh : u.uhp) * 2) &&
+	    !Is_airlevel(&u.uz)) {
+	    You("have so little stamina, %s drops from your grasp.",
+		the(xname(obj)));
+	    exercise(A_CON, FALSE);
+	    u.dx = u.dy = 0;
+	    u.dz = 1;
+	}
+
 	if(u.uswallow) {
 		mon = u.ustuck;
 		bhitpos.x = mon->mx;
@@ -711,11 +851,12 @@
 	} else if(u.dz) {
 	    if (u.dz < 0 && Role_if(PM_VALKYRIE) &&
 		    obj->oartifact == ART_MJOLLNIR && !impaired) {
-		pline("%s hits the %s and returns to your hand!",
-		      The(xname(obj)), ceiling(u.ux,u.uy));
+		pline("%s the %s and returns to your hand!",
+		      Tobjnam(obj, "hit"), ceiling(u.ux,u.uy));
 		obj = addinv(obj);
 		(void) encumber_msg();
 		setuwep(obj);
+		u.twoweap = twoweap;
 	    } else if (u.dz < 0 && !Is_airlevel(&u.uz) &&
 		    !Underwater && !Is_waterlevel(&u.uz)) {
 		(void) toss_up(obj, rn2(5));
@@ -732,8 +873,10 @@
 			exercise(A_DEX, TRUE);
 			obj = addinv(obj);
 			(void) encumber_msg();
-			if (wep_mask && !(obj->owornmask & wep_mask))
+			if (wep_mask && !(obj->owornmask & wep_mask)) {
 			    setworn(obj, wep_mask);
+			    u.twoweap = twoweap;
+			}
 			return;
 		}
 	} else {
@@ -756,7 +899,7 @@
 		if (is_ammo(obj)) {
 		    if (ammo_and_launcher(obj, uwep))
 			range++;
-		    else
+		    else if (obj->oclass != GEM_CLASS)
 			range /= 2;
 		}
 
@@ -819,25 +962,33 @@
 		    sho_obj_return_to_u(obj);	    /* display its flight */
 
 		    if (!impaired && rn2(100)) {
-			pline("%s returns to your hand!", The(xname(obj)));
+			pline("%s to your hand!", Tobjnam(obj, "return"));
 			obj = addinv(obj);
 			(void) encumber_msg();
 			setuwep(obj);
+			u.twoweap = twoweap;
 			if(cansee(bhitpos.x, bhitpos.y))
 			    newsym(bhitpos.x,bhitpos.y);
 		    } else {
-			int dmg = rnd(4);
-			if (Blind)
-			    pline("%s hits your %s!",
-				  The(xname(obj)), body_part(ARM));
-			else
-			    pline("%s flies back toward you, hitting your %s!",
-				  The(xname(obj)), body_part(ARM));
-			(void) artifact_hit((struct monst *) 0, &youmonst,
-					    obj, &dmg, 0);
-			losehp(dmg, xname(obj), KILLED_BY);
-			if(ship_object(obj, u.ux, u.uy, FALSE))
-		            return;
+			int dmg = rn2(2);
+			if (!dmg) {
+			    pline(Blind ? "%s lands %s your %s." :
+					"%s back to you, landing %s your %s.",
+				  Blind ? Something : Tobjnam(obj, "return"),
+				  Levitation ? "beneath" : "at",
+				  makeplural(body_part(FOOT)));
+			} else {
+			    dmg += rnd(3);
+			    pline(Blind ? "%s your %s!" :
+					"%s back toward you, hitting your %s!",
+				  Tobjnam(obj, Blind ? "hit" : "fly"),
+				  body_part(ARM));
+			    (void) artifact_hit((struct monst *)0,
+						&youmonst, obj, &dmg, 0);
+			    losehp(dmg, xname(obj), KILLED_BY);
+			}
+			if (ship_object(obj, u.ux, u.uy, FALSE))
+			    return;
 			dropy(obj);
 		    }
 		    return;
@@ -854,10 +1005,7 @@
 		    return;
 		}
 		if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return;
-		if (obj->otyp == CRYSKNIFE && (!obj->oerodeproof || !rn2(10))) {
-		    obj->otyp = WORM_TOOTH;
-		    obj->oerodeproof = 0;
-		}
+		obj_no_longer_held(obj);
 		if (mon && mon->isshk && is_pick(obj)) {
 		    if (cansee(bhitpos.x, bhitpos.y))
 			pline("%s snatches up %s.",
@@ -931,7 +1079,17 @@
 struct obj *obj;
 struct monst *mon;
 {
-    miss(xname(obj), mon);
+    const char *missile = mshot_xname(obj);
+
+    /* If the target can't be seen or doesn't look like a valid target,
+       avoid "the arrow misses it," or worse, "the arrows misses the mimic."
+       An attentive player will still notice that this is different from
+       an arrow just landing short of any target (no message in that case),
+       so will realize that there is a valid target here anyway. */
+    if (!canseemon(mon) || (mon->m_ap_type && mon->m_ap_type != M_AP_MONSTER))
+	pline("%s misses.", The(missile));
+    else
+	miss(missile, mon);
     if (!rn2(3)) wakeup(mon);
     return;
 }
@@ -1051,16 +1209,20 @@
 		} else {
 		    tmp += uwep->spe - greatest_erosion(uwep);
 		    tmp += weapon_hit_bonus(uwep);
+		    if (uwep->oartifact) tmp += spec_abon(uwep, mon);
 		    /*
 		     * Elves and Samurais are highly trained w/bows,
 		     * especially their own special types of bow.
 		     * Polymorphing won't make you a bow expert.
 		     */
 		    if ((Race_if(PM_ELF) || Role_if(PM_SAMURAI)) &&
+				(!Upolyd || your_race(youmonst.data)) &&
 				objects[uwep->otyp].oc_skill == P_BOW) {
 			tmp++;
-			if (is_elf(youmonst.data) && uwep->otyp == ELVEN_BOW) tmp++;
-			else if (Role_if(PM_SAMURAI) && uwep->otyp == YUMI) tmp++;
+			if (Race_if(PM_ELF) && uwep->otyp == ELVEN_BOW)
+			    tmp++;
+			else if (Role_if(PM_SAMURAI) && uwep->otyp == YUMI)
+			    tmp++;
 		    }
 		}
 	    } else {
@@ -1090,6 +1252,7 @@
 		    obfree(obj, (struct obj *)0);
 		    return 1;
 		}
+		passive_obj(mon, obj, (struct attack *)0);
 	    } else {
 		tmiss(obj, mon);
 	    }
@@ -1128,15 +1291,31 @@
 	    potionhit(mon, obj, TRUE);
 	    return 1;
 
-	} else if (obj->oclass == FOOD_CLASS &&
-		   is_domestic(mon->data) && tamedog(mon,obj)) {
-	    return 1;		/* food is gone */
+	} else if (befriend_with_obj(mon->data, obj)) {
+	    if (tamedog(mon, obj))
+		return 1;           	/* obj is gone */
+	    else {
+		/* not tmiss(), which angers non-tame monsters */
+		miss(xname(obj), mon);
+		mon->msleeping = 0;
+		mon->mstrategy &= ~STRAT_WAITMASK;
+	    }
 	} else if (guaranteed_hit) {
 	    /* this assumes that guaranteed_hit is due to swallowing */
-	    pline("%s vanishes into %s %s.",
-		The(xname(obj)), s_suffix(mon_nam(mon)),
-		is_animal(u.ustuck->data) ? "entrails" : "currents");
 	    wakeup(mon);
+	    if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) {
+		if (is_animal(u.ustuck->data)) {
+			minstapetrify(u.ustuck, TRUE);
+			/* Don't leave a cockatrice corpse available in a statue */
+			if (!u.uswallow) {
+				delobj(obj);
+				return 1;
+			}
+	    	}
+	    }
+	    pline("%s into %s %s.",
+		Tobjnam(obj, "vanish"), s_suffix(mon_nam(mon)),
+		is_animal(u.ustuck->data) ? "entrails" : "currents");
 	} else {
 	    tmiss(obj, mon);
 	}
@@ -1161,6 +1340,7 @@
 
 	Strcpy(buf,Monnam(mon));
 	mon->mpeaceful = 1;
+	mon->mavenge = 0;
 
 	/* object properly identified */
 	if(obj->dknown && objects[obj->otyp].oc_name_known) {
@@ -1300,10 +1480,21 @@
 			if (obj->otyp == POT_OIL && obj->lamplit) {
 			    splatter_burning_oil(x,y);
 			} else if (distu(x,y) <= 2) {
-			    /* [what about "familiar odor" when known?] */
-			    if (obj->otyp != POT_WATER)
-				You("smell a peculiar odor...");
-			    potionbreathe(obj);
+			    if (!breathless(youmonst.data) || haseyes(youmonst.data)) {
+				if (obj->otyp != POT_WATER) {
+					if (!breathless(youmonst.data))
+			    		     /* [what about "familiar odor" when known?] */
+					    You("smell a peculiar odor...");
+					else {
+					    int numeyes = eyecount(youmonst.data);
+					    Your("%s water%s.",
+						 (numeyes == 1) ? body_part(EYE) :
+							makeplural(body_part(EYE)),
+						 (numeyes == 1) ? "s" : "");
+					}
+				}
+				potionbreathe(obj);
+			    }
 			}
 			/* monster breathing isn't handled... [yet?] */
 			break;
@@ -1350,9 +1541,10 @@
 struct obj *obj;
 {
 	if (obj_resists(obj, 1, 99)) return 0;
+	if (objects[obj->otyp].oc_material == GLASS && !obj->oartifact &&
+		obj->oclass != GEM_CLASS)
+	    return 1;
 	switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
-		case MIRROR:
-		case CRYSTAL_BALL:
 #ifdef TOURIST
 		case EXPENSIVE_CAMERA:
 #endif
@@ -1376,6 +1568,11 @@
 
 	to_pieces = "";
 	switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
+		default: /* glass or crystal wand */
+		    if (obj->oclass != WAND_CLASS)
+			impossible("breaking odd object?");
+		case CRYSTAL_PLATE_MAIL:
+		case LENSES:
 		case MIRROR:
 		case CRYSTAL_BALL:
 #ifdef TOURIST
@@ -1414,6 +1611,10 @@
 	long zorks = obj->quan;
 	register struct monst *mon;
 
+	if(!u.dx && !u.dy && !u.dz) {
+		You("cannot throw gold at yourself.");
+		return(0);
+	}
 	if(u.uswallow) {
 		pline(is_animal(u.ustuck->data) ?
 			"%s in the %s's entrails." : "%s into %s.",
diff -Naurd ../nethack-3.3.1/src/drawing.c ./src/drawing.c
--- ../nethack-3.3.1/src/drawing.c Sat Jul 22 01:59:05 2000
+++ ./src/drawing.c Fri Mar 22 14:40:55 2002
@@ -269,7 +269,7 @@
 	{'"', "web",		C(CLR_GRAY)},	/* web */
 	{'^', "statue trap",	C(CLR_GRAY)},	/* trap */
 /*60*/	{'^', "magic trap",	C(HI_ZAP)},	/* trap */
-	{'^', "anti-magic trap field", C(HI_ZAP)},	/* trap */
+	{'^', "anti-magic field", C(HI_ZAP)},	/* trap */
 	{'^', "polymorph trap",	C(CLR_BRIGHT_GREEN)},	/* trap */
 	{'|', "wall",		C(CLR_GRAY)},	/* vbeam */
 	{'-', "wall",		C(CLR_GRAY)},	/* hbeam */
diff -Naurd ../nethack-3.3.1/src/dungeon.c ./src/dungeon.c
--- ../nethack-3.3.1/src/dungeon.c Wed May 17 20:05:09 2000
+++ ./src/dungeon.c Fri Mar 22 14:40:55 2002
@@ -380,7 +380,10 @@
     new_branch->next = (branch *) 0;
 
 /* Convert the branch into a unique number so we can sort them. */
-#define branch_val(bp) ((((long)(bp)->end1.dnum * (MAXLEVEL+1) + (long)(bp)->end1.dlevel) * (MAXDUNGEON+1) * (MAXLEVEL+1)) + ((long)(bp)->end2.dnum * (MAXLEVEL+1) + (long)(bp)->end2.dlevel))
+#define branch_val(bp) \
+	((((long)(bp)->end1.dnum * (MAXLEVEL+1) + \
+	  (long)(bp)->end1.dlevel) * (MAXDUNGEON+1) * (MAXLEVEL+1)) + \
+	 ((long)(bp)->end2.dnum * (MAXLEVEL+1) + (long)(bp)->end2.dlevel))
 
     /*
      * Insert the new branch into the correct place in the branch list.
@@ -661,6 +664,11 @@
 
 	/* validate the data's version against the program's version */
 	Fread((genericptr_t) &vers_info, sizeof vers_info, 1, dgn_file);
+	/* we'd better clear the screen now, since when error messages come from
+	 * check_version() they will be printed using pline(), which doesn't
+	 * mix with the raw messages that might be already on the screen
+	 */
+	if (iflags.window_inited) clear_nhwindow(WIN_MAP);
 	if (!check_version(&vers_info, DUNGEON_FILE, TRUE))
 	    panic("Dungeon description not valid.");
 
@@ -734,7 +742,7 @@
 	    } else if (pd.tmpdungeon[i].entry_lev > 0) {
 		dungeons[i].entry_lev = pd.tmpdungeon[i].entry_lev;
 		if (dungeons[i].entry_lev > dungeons[i].num_dunlevs)
-		    dungeons[i].entry_lev = pd.tmpdungeon[i].entry_lev;
+		    dungeons[i].entry_lev = dungeons[i].num_dunlevs;
 	    } else { /* default */
 		dungeons[i].entry_lev = 1;	/* defaults to top level */
 	    }
diff -Naurd ../nethack-3.3.1/src/eat.c ./src/eat.c
--- ../nethack-3.3.1/src/eat.c Sat Aug 5 00:31:40 2000
+++ ./src/eat.c Fri Mar 22 14:41:01 2002
@@ -35,11 +35,12 @@
 STATIC_DCL void FDECL(fprefx, (struct obj *));
 STATIC_DCL void FDECL(fpostfx, (struct obj *));
 STATIC_DCL int NDECL(bite);
+STATIC_DCL int FDECL(edibility_prompts, (struct obj *));
 
 STATIC_DCL int FDECL(rottenfood, (struct obj *));
 STATIC_DCL void NDECL(eatspecial);
 STATIC_DCL void FDECL(eataccessory, (struct obj *));
-STATIC_DCL const char * FDECL(foodword, (struct obj *));
+STATIC_DCL const char *FDECL(foodword, (struct obj *));
 
 char msgbuf[BUFSZ];
 
@@ -103,7 +104,8 @@
 	/* above also prevents the Amulet from being eaten, so we must never
 	   allow fake amulets to be eaten either [which is already the case] */
 
-	if (metallivorous(youmonst.data) && is_metallic(obj))
+	if (metallivorous(youmonst.data) && is_metallic(obj) &&
+	    (youmonst.data != &mons[PM_RUST_MONSTER] || is_rustprone(obj)))
 		return TRUE;
 	if (u.umonnum == PM_GELATINOUS_CUBE && is_organic(obj) &&
 		/* [g.cubes can eat containers and retain all contents
@@ -139,6 +141,7 @@
 	{"boiled",       50},
 	{"dried",        55},
 	{"szechuan",     70},
+#define FRENCH_FRIED_TIN 11
 	{"french fried", 40},
 	{"sauteed",      95},
 	{"broiled",      80},
@@ -223,7 +226,7 @@
 {
 	/* only happens if you were satiated */
 	if (u.uhs != SATIATED) {
-		if (food->otyp != AMULET_OF_STRANGULATION)
+		if (!food || food->otyp != AMULET_OF_STRANGULATION)
 			return;
 	} else if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL) {
 			adjalign(-1);		/* gluttony is unchivalrous */
@@ -234,12 +237,13 @@
 
 	if (Breathless || (!Strangled && !rn2(20))) {
 		/* choking by eating AoS doesn't involve stuffing yourself */
-		if (food->otyp == AMULET_OF_STRANGULATION) {
+		if (food && food->otyp == AMULET_OF_STRANGULATION) {
 			You("choke, but recover your composure.");
 			return;
 		}
 		You("stuff yourself and then vomit voluminously.");
 		morehungry(1000);	/* you just got *very* sick! */
+		nomovemsg = 0;
 		vomit();
 	} else {
 		killer_format = KILLED_BY_AN;
@@ -263,19 +267,18 @@
 	}
 }
 
+/* modify object wt. depending on time spent consuming it */
 STATIC_OVL void
-recalc_wt()	/* modify object wt. depending on time spent consuming it */
+recalc_wt()
 {
-	register struct obj *piece = victual.piece;
+	struct obj *piece = victual.piece;
 
 #ifdef DEBUG
 	debugpline("Old weight = %d", piece->owt);
 	debugpline("Used time = %d, Req'd time = %d",
 		victual.usedtime, victual.reqtime);
 #endif
-	/* weight(piece) = weight of full item */
-	if(victual.usedtime)
-	    piece->owt = eaten_stat(weight(piece), piece);
+	piece->owt = weight(piece);
 #ifdef DEBUG
 	debugpline("New weight = %d", piece->owt);
 #endif
@@ -302,9 +305,9 @@
 {
 	if (otmp->quan > 1L) {
 	    if(!carried(otmp))
-		(void) splitobj(otmp, 1L);
+		(void) splitobj(otmp, otmp->quan - 1L);
 	    else
-		otmp = splitobj(otmp, otmp->quan - 1L);
+		otmp = splitobj(otmp, 1L);
 #ifdef DEBUG
 	    debugpline("split object,");
 #endif
@@ -326,10 +329,15 @@
 
 	if (carried(otmp)) {
 	    freeinv(otmp);
-	    if (inv_cnt() >= 52 && !merge_choice(invent, otmp))
+	    if (inv_cnt() >= 52) {
+		sellobj_state(SELL_DONTSELL);
 		dropy(otmp);
-	    else
-		otmp = addinv(otmp); /* unlikely but a merge is possible */
+		sellobj_state(SELL_NORMAL);
+	    } else {
+		otmp->oxlth++;		/* hack to prevent merge */
+		otmp = addinv(otmp);
+		otmp->oxlth--;
+	    }
 	}
 	return(otmp);
 }
@@ -482,13 +490,16 @@
 		    return;
 		}
 	    case PM_GREEN_SLIME:
-	    	if (!Unchanging && youmonst.data != &mons[PM_FIRE_VORTEX] &&
-	    			youmonst.data != &mons[PM_FIRE_ELEMENTAL] &&
-	    			youmonst.data != &mons[PM_GREEN_SLIME]) {
-	    	    You("don't feel very well.");
-	    	    Slimed = 10L;
-	    	}
-	    	/* Fall through */
+		if (!Slimed && !Unchanging &&
+			youmonst.data != &mons[PM_FIRE_VORTEX] &&
+			youmonst.data != &mons[PM_FIRE_ELEMENTAL] &&
+			youmonst.data != &mons[PM_SALAMANDER] &&
+			youmonst.data != &mons[PM_GREEN_SLIME]) {
+		    You("don't feel very well.");
+		    Slimed = 10L;
+		    flags.botl = 1;
+		}
+		/* Fall through */
 	    default:
 		if (acidic(&mons[pm]) && Stoned)
 		    fix_petrification();
@@ -810,8 +821,11 @@
 		tmp += 20;
 		if (youmonst.data->mlet != S_MIMIC) {
 		    char buf[BUFSZ];
-
 		    You_cant("resist the temptation to mimic a pile of gold.");
+#ifdef STEED
+                    /* A pile of gold can't ride. */
+		    if (u.usteed) dismount_steed(DISMOUNT_FELL);
+#endif
 		    nomul(-tmp);
 		    Sprintf(buf, "You now prefer mimicking %s again.",
 			    an(Upolyd ? youmonst.data->mname : urace.noun));
@@ -846,7 +860,7 @@
 	 /* case PM_SANDESTIN: */
 		if (!Unchanging) {
 		    You_feel("a change coming over you.");
-		    polyself();
+		    polyself(FALSE);
 		}
 		break;
 	    case PM_MIND_FLAYER:
@@ -1003,11 +1017,18 @@
 	    tin.tin->dknown = tin.tin->known = TRUE;
 	    cprefx(tin.tin->corpsenm); cpostfx(tin.tin->corpsenm);
 
+	    if(((!carried(tin.tin) && costly_spot(tin.tin->ox, tin.tin->oy) &&
+		 !tin.tin->no_charge)
+		|| tin.tin->unpaid)) {
+		verbalize("You open it, you bought it!");
+		bill_dummy_object(tin.tin);
+	    }
+
 	    /* check for vomiting added by GAN 01/16/87 */
 	    if(tintxts[r].nut < 0) make_vomiting((long)rn1(15,10), FALSE);
 	    else lesshungry(tintxts[r].nut);
 
-	    if(r == 0) {			/* Deep Fried */
+	    if(r == 0 || r == FRENCH_FRIED_TIN) {
 	        /* Assume !Glib, because you can't open tins when Glib. */
 		incr_itimeout(&Glib, rnd(15));
 		pline("Eating deep fried food made your %s very slippery.",
@@ -1015,8 +1036,8 @@
 	    }
 	} else {
 	    if (tin.tin->cursed)
-		pline("It contains some decaying %s substance.",
-			hcolor(green));
+		pline("It contains some decaying%s%s substance.",
+			Blind ? "" : " ", Blind ? "" : hcolor(green));
 	    else
 		pline("It contains spinach.");
 
@@ -1027,6 +1048,15 @@
 		    You("discard the open tin.");
 		goto use_me;
 	    }
+
+	    tin.tin->dknown = tin.tin->known = TRUE;
+	    if(((!carried(tin.tin) && costly_spot(tin.tin->ox, tin.tin->oy) &&
+		 !tin.tin->no_charge)
+		|| tin.tin->unpaid)) {
+		verbalize("You open it, you bought it!");
+		bill_dummy_object(tin.tin);
+	    }
+
 	    if (!tin.tin->cursed)
 		pline("This makes you feel like %s!",
 		      Hallucination ? "Swee'pea" : "Popeye");
@@ -1034,7 +1064,6 @@
 	    gainstr(tin.tin, 0);
 	    u.uconduct.food++;
 	}
-	tin.tin->dknown = tin.tin->known = TRUE;
 use_me:
 	if (carried(tin.tin)) useup(tin.tin);
 	else useupf(tin.tin, 1L);
@@ -1086,11 +1115,7 @@
 			pline_The("tin slips from your %s.",
 			      makeplural(body_part(FINGER)));
 			if(otmp->quan > 1L) {
-				register struct obj *obj;
-				obj = splitobj(otmp, 1L);
-				if (otmp == uwep) setuwep(obj);
-				if (otmp == uswapwep) setuswapwep(obj);
-				if (otmp == uquiver) setuqwep(obj);
+			    otmp = splitobj(otmp, 1L);
 			}
 			if (carried(otmp)) dropx(otmp);
 			else stackobj(otmp);
@@ -1125,6 +1150,7 @@
 	} else if(!rn2(4) && !Blind) {
 		pline("Everything suddenly goes dark.");
 		make_blinded((long)d(2,10),FALSE);
+		if (!Blind) Your(vision_clears);
 	} else if(!rn2(3)) {
 		const char *what, *where;
 		if (!Blind)
@@ -1133,7 +1159,11 @@
 			 Is_waterlevel(&u.uz))
 		    what = "you lose control of",  where = "yourself";
 		else
-		    what = "you slap against the",  where = surface(u.ux,u.uy);
+		    what = "you slap against the", where =
+#ifdef STEED
+			   (u.usteed) ? "saddle" :
+#endif
+			   surface(u.ux,u.uy);
 		pline_The("world spins and %s %s.", what, where);
 		flags.soundok = 0;
 		nomul(-rnd(10));
@@ -1155,6 +1185,10 @@
 	boolean stoneable = (touch_petrifies(&mons[mnum]) && !Stone_resistance &&
 				!poly_when_stoned(youmonst.data));
 
+	/* KMH, conduct */
+	if (!vegan(&mons[mnum])) u.uconduct.unvegan++;
+	if (!vegetarian(&mons[mnum])) violated_vegetarian();
+
 	if (mnum != PM_LIZARD && mnum != PM_LICHEN) {
 		long age = peek_at_iced_corpse_age(otmp);
 
@@ -1185,19 +1219,12 @@
 				    s_suffix(mons[mnum].mname));
 			make_sick(sick_time, buf, TRUE, SICK_VOMITABLE);
 		}
-
-		/* KMH, conduct */
-		if (!vegan(&mons[mnum]))
-		     u.uconduct.unvegan++;
-		if (!vegetarian(&mons[mnum]))
-		     violated_vegetarian();
-
 		if (carried(otmp)) useup(otmp);
 		else useupf(otmp, 1L);
 		return(2);
 	} else if (acidic(&mons[mnum]) && !Acid_resistance) {
 		tp++;
-		You("have a very bad case of stomach acid.");
+		You("have a very bad case of stomach acid."); /* not body_part() */
 		losehp(rnd(15), "acidic corpse", KILLED_BY_AN);
 	} else if (poisonous(&mons[mnum]) && rn2(5)) {
 		tp++;
@@ -1223,22 +1250,27 @@
 		otmp->orotten = TRUE;
 		(void)touchfood(otmp);
 		retcode = 1;
-	    } else
-		otmp->oeaten >>= 2;
+	    }
+
+	    if (!mons[otmp->corpsenm].cnutrit) {
+		/* no nutrution: rots away, no message if you passed out */
+		if (!retcode) pline_The("corpse rots away completely.");
+		if (carried(otmp)) useup(otmp);
+		else useupf(otmp, 1L);
+		retcode = 2;
+	    }
+		    
+	    if (!retcode) consume_oeaten(otmp, 2);	/* oeaten >>= 2 */
 	} else {
 	    pline("%s%s %s!",
 		  !uniq ? "This " : !type_is_pname(&mons[mnum]) ? "The " : "",
 		  food_xname(otmp, FALSE),
-		  (carnivorous(youmonst.data) && !herbivorous(youmonst.data)) ?
-			"is delicious" : "tastes terrible");
+		  (vegan(&mons[mnum]) ?
+		   (!carnivorous(youmonst.data) && herbivorous(youmonst.data)) :
+		   (carnivorous(youmonst.data) && !herbivorous(youmonst.data)))
+		  ? "is delicious" : "tastes terrible");
 	}
 
-	/* KMH, conduct */
-	if (!vegan(&mons[mnum]))
-	     u.uconduct.unvegan++;
-	if (!vegetarian(&mons[mnum]))
-	     violated_vegetarian();
-
 	return(retcode);
 }
 
@@ -1284,23 +1316,24 @@
 	switch(otmp->otyp) {
 	    case FOOD_RATION:
 		if(u.uhunger <= 200)
-		    if (Hallucination) pline("Oh wow, like, superior, man!");
-		    else	       pline("That food really hit the spot!");
-		else if(u.uhunger <= 700) pline("That satiated your stomach!");
+		    pline(Hallucination ? "Oh wow, like, superior, man!" :
+			  "That food really hit the spot!");
+		else if(u.uhunger <= 700) pline("That satiated your %s!",
+						body_part(STOMACH));
 		break;
 	    case TRIPE_RATION:
 		if (carnivorous(youmonst.data) && !humanoid(youmonst.data))
 		    pline("That tripe ration was surprisingly good!");
+		else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)))
+		    pline(Hallucination ? "Tastes great! Less filling!" :
+			  "Mmm, tripe... not bad!");
 		else {
 		    pline("Yak - dog food!");
 		    more_experienced(1,0);
-		    flags.botl = 1;
-		}
-		if (rn2(2) &&
-		    (Upolyd ? (!carnivorous(youmonst.data) ||
-				(humanoid(youmonst.data) &&
-					!is_orc(youmonst.data)))
-			    : !CANNIBAL_ALLOWED())) {
+		    newexplevel();
+		    /* not cannibalism, but we use similar criteria
+		       for deciding whether to be sickened by this meal */
+		    if (rn2(2) && !CANNIBAL_ALLOWED())
 			make_vomiting((long)rn1(victual.reqtime, 14), FALSE);
 		}
 		break;
@@ -1390,11 +1423,11 @@
 struct obj *otmp;
 {
 	int typ = otmp->otyp;
-	int oldprop;
+	long oldprop;
 
 	/* Note: rings are not so common that this is unbalancing. */
 	/* (How often do you even _find_ 3 rings of polymorph in a game?) */
-	oldprop = !!(u.uprops[objects[typ].oc_oprop].intrinsic);
+	oldprop = u.uprops[objects[typ].oc_oprop].intrinsic;
 	if (otmp == uleft || otmp == uright) {
 	    Ring_gone(otmp);
 	    if (u.uhp <= 0) return; /* died from sink fall */
@@ -1416,14 +1449,14 @@
 		    set_mimic_blocking();
 		    see_monsters();
 		    if (Invis && !oldprop && !ESee_invisible &&
-		    		!perceives(youmonst.data) && !Blind) {
+				!perceives(youmonst.data) && !Blind) {
 			newsym(u.ux,u.uy);
 			pline("Suddenly you can see yourself.");
 			makeknown(typ);
 		    }
 		    break;
 		  case RIN_INVISIBILITY:
-			if (!oldprop && !EInvis && !BInvis &&
+		    if (!oldprop && !EInvis && !BInvis &&
 					!See_invisible && !Blind) {
 			newsym(u.ux,u.uy);
 			Your("body takes on a %s transparency...",
@@ -1435,6 +1468,8 @@
 		    rescham();
 		    break;
 		  case RIN_LEVITATION:
+		    /* undo the `.intrinsic |= FROMOUTSIDE' done above */
+		    u.uprops[LEVITATION].intrinsic = oldprop;
 		    if (!Levitation) {
 			float_up();
 			incr_itimeout(&HLevitation, d(10,20));
@@ -1570,7 +1605,7 @@
 		    you_unwere(TRUE);
 		break;
 	    case CARROT:
-		make_blinded(0L,TRUE);
+		make_blinded((long)u.ucreamed,TRUE);
 		break;
 	    case FORTUNE_COOKIE:
 		outrumor(bcsign(otmp), BY_COOKIE);
@@ -1621,13 +1656,147 @@
 	return;
 }
 
+/*
+ * return 0 if the food was not dangerous.
+ * return 1 if the food was dangerous and you chose to stop.
+ * return 2 if the food was dangerous and you chose to eat it anyway.
+ */
+STATIC_OVL int
+edibility_prompts(otmp)
+struct obj *otmp;
+{
+	/* blessed food detection granted you a one-use
+	   ability to detect food that is unfit for consumption
+	   or dangerous and avoid it. */
+
+	char buf[BUFSZ], foodsmell[BUFSZ];
+	char *eat_it_anyway = "Eat it anyway?";
+	boolean cadaver = (otmp->otyp == CORPSE);
+	boolean stoneorslime = FALSE;
+	int material = objects[otmp->otyp].oc_material;
+	long rotted = 0L;
+	int mnum;
+
+#ifdef GCC_WARN
+	mnum = 0;
+#endif
+
+	Strcpy(foodsmell, Tobjnam(otmp, "smell"));
+	if (cadaver || otmp->otyp == EGG || otmp->otyp == TIN) {
+		mnum = otmp->corpsenm;
+		/* These checks must match those in eatcorpse() */
+	  	stoneorslime = (touch_petrifies(&mons[mnum]) &&
+			    !Stone_resistance && !poly_when_stoned(youmonst.data));
+
+		if (mnum == PM_GREEN_SLIME)
+		    stoneorslime = (!Unchanging &&
+			youmonst.data != &mons[PM_FIRE_VORTEX] &&
+			youmonst.data != &mons[PM_FIRE_ELEMENTAL] &&
+			youmonst.data != &mons[PM_SALAMANDER] &&
+			youmonst.data != &mons[PM_GREEN_SLIME]);
+
+		if (cadaver && mnum != PM_LIZARD && mnum != PM_LICHEN) {
+			long age = peek_at_iced_corpse_age(otmp);
+			/* worst case rather than random
+			   in this calculation to force prompt */
+			rotted = (monstermoves - age)/(10L + 0 /* was rn2(20) */);
+			if (otmp->cursed) rotted += 2L;
+			else if (otmp->blessed) rotted -= 2L;
+		}
+	}
+
+	/*
+	 * These problems with food should be checked in
+	 * order from most detrimental to least detrimental.
+	 */
+
+	if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && !Sick_resistance) {
+		/* Tainted meat */
+		Sprintf(buf, "%s like it could be tainted! %s",
+		     foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (stoneorslime) {
+		Sprintf(buf, "%s like it could be something very dangerous! %s",
+		     foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (otmp->orotten || (cadaver && rotted > 3L)) {
+		/* Rotten */
+		Sprintf(buf, "%s like it could be rotten! %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (cadaver && poisonous(&mons[mnum]) && !Poison_resistance) {
+		/* poisonous */
+		Sprintf(buf, "%s like it might be poisonous! %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (cadaver && !vegetarian(&mons[mnum]) &&
+	    !u.uconduct.unvegetarian && Role_if(PM_MONK)) {
+		Sprintf(buf, "%s unhealthy. %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (cadaver && acidic(&mons[mnum]) && !Acid_resistance) {
+		Sprintf(buf, "%s rather acidic. %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (Upolyd &&
+	    (u.umonnum == PM_RUST_MONSTER && is_metallic(otmp) && otmp->oerodeproof)) {
+		Sprintf(buf, "%s disgusting to you right now. %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+
+	/*
+	 * Breaks conduct, but otherwise safe.
+	 */
+	 
+	if (!u.uconduct.unvegan &&
+	    ((material == LEATHER || material == BONE ||
+	      material == DRAGON_HIDE || material == WAX) ||
+	     (cadaver && !vegan(&mons[mnum])))) {
+		Sprintf(buf, "%s foul and unfamiliar to you. %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	if (!u.uconduct.unvegetarian &&
+	    ((material == LEATHER || material == BONE || material == DRAGON_HIDE) ||
+	     (cadaver && !vegetarian(&mons[mnum])))) {
+		Sprintf(buf, "%s unfamiliar to you. %s",
+			foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+
+	if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && Sick_resistance) {
+		/* Tainted meat with Sick_resistance */
+		Sprintf(buf, "%s like it could be tainted! %s",
+		     foodsmell, eat_it_anyway);
+		if (yn_function(buf,ynchars,'n')=='n') return 1;
+		else return 2;
+	}
+	return 0;
+}
+
 int
 doeat()		/* generic "eat" command funtion (see cmd.c) */
 {
 	register struct obj *otmp;
 	int basenutrit;			/* nutrition of full item */
 	boolean dont_start = FALSE;
-
+	
 	if (Strangled) {
 		pline("If you can't breathe air, how can you consume solids?");
 		return 0;
@@ -1635,6 +1804,16 @@
 	if (!(otmp = floorfood("eat", 0))) return 0;
 	if (check_capacity((char *)0)) return 0;
 
+	if (u.uedibility) {
+		int res = edibility_prompts(otmp);
+		if (res) {
+		    Your("%s stops tingling and your sense of smell returns to normal.",
+			body_part(NOSE));
+		    u.uedibility = 0;
+		    if (res == 1) return 0;
+		}
+	}
+
 	/* We have to make non-foods take 1 move to eat, unless we want to
 	 * do ridiculous amounts of coding to deal with partly eaten plate
 	 * mails, players who polymorph back to human in the middle of their
@@ -1656,10 +1835,10 @@
 	    u.umonnum == PM_RUST_MONSTER && otmp->oerodeproof) {
 	    	otmp->rknown = TRUE;
 		if (otmp->quan > 1L) {
-			if(!carried(otmp))
-				(void) splitobj(otmp, 1L);
-			else
-				otmp = splitobj(otmp, otmp->quan - 1L);
+		    if(!carried(otmp))
+			(void) splitobj(otmp, otmp->quan - 1L);
+		    else
+			otmp = splitobj(otmp, 1L);
 		}
 		pline("Ulch - That %s was rustproofed!", xname(otmp));
 		/* The regurgitated object's rustproofing is gone now */
@@ -1684,6 +1863,7 @@
 		return (1);
 	}
 	if (otmp->oclass != FOOD_CLASS) {
+	    int material;
 	    victual.reqtime = 1;
 	    victual.piece = otmp;
 		/* Don't split it, we don't need to if it's 1 move */
@@ -1701,6 +1881,14 @@
 	    victual.nmod = basenutrit;
 	    victual.eating = TRUE; /* needed for lesshungry() */
 
+	    material = objects[otmp->otyp].oc_material;
+	    if (material == LEATHER || material == BONE || material == DRAGON_HIDE) {
+	 		u.uconduct.unvegan++;
+	    		violated_vegetarian();
+	    } else if (material == WAX)
+			u.uconduct.unvegan++;
+	    u.uconduct.food++;
+	    
 	    if (otmp->cursed)
 		(void) rottenfood(otmp);
 
@@ -1716,7 +1904,6 @@
 		      otmp->oclass == GOLD_CLASS ? foodword(otmp) :
 		      singular(otmp, xname));
 
-	    u.uconduct.food++;
 	    eatspecial();
 	    return 1;
 	}
@@ -1728,10 +1915,7 @@
 	 * they shouldn't be able to choke now.
 	 */
 	    if (u.uhs != SATIATED) victual.canchoke = FALSE;
-	    if(!carried(victual.piece)) {
-		if(victual.piece->quan > 1L)
-			(void) splitobj(victual.piece, 1L);
-	    }
+	    victual.piece = touchfood(otmp);
 	    You("resume your meal.");
 	    start_eating(victual.piece);
 	    return(1);
@@ -1777,7 +1961,7 @@
 		    otmp->orotten = TRUE;
 		    dont_start = TRUE;
 		}
-		otmp->oeaten >>= 1;
+		consume_oeaten(otmp, 1);	/* oeaten >>= 1 */
 	    } else fprefx(otmp);
 	}
 
@@ -1830,10 +2014,10 @@
 	force_save_hs = TRUE;
 	if(victual.nmod < 0) {
 		lesshungry(-victual.nmod);
-		victual.piece->oeaten -= -victual.nmod;
+		consume_oeaten(victual.piece, victual.nmod); /* -= -nmod */
 	} else if(victual.nmod > 0 && (victual.usedtime % victual.nmod)) {
 		lesshungry(1);
-		victual.piece->oeaten--;
+		consume_oeaten(victual.piece, -1);		  /* -= 1 */
 	}
 	force_save_hs = FALSE;
 	recalc_wt();
@@ -1900,17 +2084,19 @@
 lesshungry(num)	/* called after eating (and after drinking fruit juice) */
 register int num;
 {
+	/* See comments in newuhs() for discussion on force_save_hs */
+	boolean iseating = (occupation == eatfood) || force_save_hs;
 #ifdef DEBUG
 	debugpline("lesshungry(%d)", num);
 #endif
 	u.uhunger += num;
 	if(u.uhunger >= 2000) {
-	    if (!victual.eating || victual.canchoke) {
-		if (victual.eating) {
-			choke(victual.piece);
-			reset_eat();
+	    if (!iseating || victual.canchoke) {
+		if (iseating) {
+		    choke(victual.piece);
+		    reset_eat();
 		} else
-			choke(tin.tin);	/* may be null */
+		    choke(occupation == opentin ? tin.tin : (struct obj *)0);
 		/* no reset_eat() */
 	    }
 	} else {
@@ -2083,10 +2269,11 @@
 				  "You still have the munchies." :
       "The munchies are interfering with your motor capabilities.");
 			else if (incr &&
-				(Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)))
+				(Role_if(PM_WIZARD) || Race_if(PM_ELF) ||
+				 Role_if(PM_VALKYRIE)))
 			    pline("%s needs food, badly!",
-			    		(Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ?
-			    		urole.name.m : "Elf");
+				  (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ?
+				  urole.name.m : "Elf");
 			else
 			    You((!incr) ? "feel weak now." :
 				  (u.uhunger < 45) ? "feel weak." :
@@ -2125,6 +2312,16 @@
 	char c;
 	boolean feeding = (!strcmp(verb, "eat"));
 
+	/* if we can't touch floor objects then use invent food only */
+	if (!can_reach_floor() ||
+#ifdef STEED
+		(feeding && u.usteed) || /* can't eat off floor while riding */
+#endif
+		((is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) &&
+		    (Wwalking || is_clinger(youmonst.data) ||
+			(Flying && !Breathless))))
+	    goto skipfloor;
+
 	if (feeding && metallivorous(youmonst.data)) {
 	    struct obj *gold;
 	    struct trap *ttmp = t_at(u.ux, u.uy);
@@ -2145,10 +2342,7 @@
 		}
 	    }
 
-	    if (
-#ifdef STEED
-	    	!u.usteed && 
-#endif
+	    if (youmonst.data != &mons[PM_RUST_MONSTER] &&
 		(gold = g_at(u.ux, u.uy)) != 0) {
 		if (gold->quan == 1L)
 		    Sprintf(qbuf, "There is 1 gold piece here; eat it?");
@@ -2165,19 +2359,13 @@
 	}
 
 	/* Is there some food (probably a heavy corpse) here on the ground? */
-	if (
-#ifdef STEED
-	    !u.usteed && 
-#endif
-	    !(Levitation && !Is_airlevel(&u.uz)  && !Is_waterlevel(&u.uz))
-	    && !u.uswallow) {
-	    for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
+	for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
 		if(corpsecheck ?
 		(otmp->otyp==CORPSE && (corpsecheck == 1 || tinnable(otmp))) :
 		    feeding ? (otmp->oclass != GOLD_CLASS && is_edible(otmp)) :
 						otmp->oclass==FOOD_CLASS) {
 			Sprintf(qbuf, "There %s %s here; %s %s?",
-				(otmp->quan == 1L) ? "is" : "are",
+				otense(otmp, "are"),
 				doname(otmp), verb,
 				(otmp->quan == 1L) ? "it" : "one");
 			if((c = yn_function(qbuf,ynqchars,'n')) == 'y')
@@ -2185,8 +2373,9 @@
 			else if(c == 'q')
 				return((struct obj *) 0);
 		}
-	    }
 	}
+
+ skipfloor:
 	/* We cannot use ALL_CLASSES since that causes getobj() to skip its
 	 * "ugly checks" and we need to check for inedible items.
 	 */
@@ -2219,11 +2408,85 @@
 	uneaten_amt = (long)obj->oeaten;
 	full_amount = (obj->otyp == CORPSE) ? (long)mons[obj->corpsenm].cnutrit
 					: (long)objects[obj->otyp].oc_nutrition;
+	if (uneaten_amt > full_amount) {
+	    impossible(
+	  "partly eaten food (%ld) more nutritious than untouched food (%ld)",
+		       uneaten_amt, full_amount);
+	    uneaten_amt = full_amount;
+	}
 
 	base = (int)(full_amount ? (long)base * uneaten_amt / full_amount : 0L);
 	return (base < 1) ? 1 : base;
 }
 
+/* reduce obj's oeaten field, making sure it never hits or passes 0 */
+void
+consume_oeaten(obj, amt)
+struct obj *obj;
+int amt;
+{
+    /*
+     * This is a hack to try to squelch several long standing mystery
+     * food bugs.  A better solution would be to rewrite the entire
+     * victual handling mechanism from scratch using a less complex
+     * model.  Alternatively, this routine could call done_eating()
+     * or food_disappears() but its callers would need revisions to
+     * cope with victual.piece unexpectedly going away.
+     *
+     * Multi-turn eating operates by setting the food's oeaten field
+     * to its full nutritional value and then running a counter which
+     * independently keeps track of whether there is any food left.
+     * The oeaten field can reach exactly zero on the last turn, and
+     * the object isn't removed from inventory until the next turn
+     * when the "you finish eating" message gets delivered, so the
+     * food would be restored to the status of untouched during that
+     * interval.  This resulted in unexpected encumbrance messages
+     * at the end of a meal (if near enough to a threshold) and would
+     * yield full food if there was an interruption on the critical
+     * turn.  Also, there have been reports over the years of food
+     * becoming massively heavy or producing unlimited satiation;
+     * this would occur if reducing oeaten via subtraction attempted
+     * to drop it below 0 since its unsigned type would produce a
+     * huge positive value instead.  So far, no one has figured out
+     * _why_ that inappropriate subtraction might sometimes happen.
+     */
+
+    if (amt > 0) {
+	/* bit shift to divide the remaining amount of food */
+	obj->oeaten >>= amt;
+    } else {
+	/* simple decrement; value is negative so we actually add it */
+	if ((int) obj->oeaten > -amt)
+	    obj->oeaten += amt;
+	else
+	    obj->oeaten = 0;
+    }
+
+    if (obj->oeaten == 0) {
+	if (obj == victual.piece)	/* always true unless wishing... */
+	    victual.reqtime = victual.usedtime;	/* no bites left */
+	obj->oeaten = 1;	/* smallest possible positive value */
+    }
+}
+
 #endif /* OVLB */
+#ifdef OVL1
+
+/* called when eatfood occupation has been interrupted,
+   or in the case of theft, is about to be interrupted */
+boolean
+maybe_finished_meal(stopping)
+boolean stopping;
+{
+	/* in case consume_oeaten() has decided that the food is all gone */
+	if (occupation == eatfood && victual.usedtime >= victual.reqtime) {
+	    if (stopping) occupation = 0;	/* for do_reset_eat */
+	    (void) eatfood(); /* calls done_eating() to use up victual.piece */
+	    return TRUE;
+	}
+	return FALSE;
+}
+
+#endif /* OVL1 */
 
 /*eat.c*/
diff -Naurd ../nethack-3.3.1/src/end.c ./src/end.c
--- ../nethack-3.3.1/src/end.c Mon Jul 3 21:59:32 2000
+++ ./src/end.c Fri Mar 22 14:41:01 2002
@@ -40,8 +40,9 @@
 STATIC_DCL void FDECL(add_artifact_score, (struct obj *));
 STATIC_DCL void FDECL(display_artifact_score, (struct obj *,winid));
 STATIC_DCL void FDECL(savelife, (int));
-STATIC_DCL void NDECL(list_vanquished);
-STATIC_DCL void NDECL(list_genocided);
+STATIC_DCL void FDECL(list_vanquished, (int, BOOLEAN_P));
+STATIC_DCL void FDECL(list_genocided, (int, BOOLEAN_P));
+STATIC_DCL boolean FDECL(should_query_disclose_option, (int, int*));
 
 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
 extern void FDECL(nethack_exit,(int));
@@ -186,7 +186,9 @@
 	You("die...");
 	mark_synch();	/* flush buffered screen output */
 	buf[0] = '\0';
-	if ((mtmp->data->geno & G_UNIQ) != 0) {
+	/* "killed by the high priest of Crom" is okay, "killed by the high
+	   priest" alone isn't */
+	if ((mtmp->data->geno & G_UNIQ) != 0 && !(mtmp->data == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
 	    if (!type_is_pname(mtmp->data))
 		Strcat(buf, "the ");
 	    killer_format = KILLED_BY;
@@ -226,6 +228,8 @@
 		u.ugrave_arise = urace.mummynum;
 	else if (mtmp->data->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
 		u.ugrave_arise = PM_VAMPIRE;
+	else if (mtmp->data == &mons[PM_GHOUL])
+		u.ugrave_arise = PM_GHOUL;
 	if (u.ugrave_arise >= LOW_PM &&
 				(mvitals[u.ugrave_arise].mvflags & G_GENOD))
 		u.ugrave_arise = NON_PM;
@@ -283,57 +287,101 @@
 	done(PANICKED);
 }
 
+STATIC_OVL boolean
+should_query_disclose_option(category, defquery)
+int category;
+int *defquery;
+{
+	int idx;
+	char *dop = index(disclosure_options, category);
+	if (dop && defquery) {
+		idx = (dop - disclosure_options) / sizeof(char);
+		if (idx < 0 || idx > (NUM_DISCLOSURE_OPTIONS - 1)) {
+			impossible(
+				"should_query_disclose_option: bad disclosure index %d %c",
+				idx, category);
+			*defquery = DISCLOSE_PROMPT_DEFAULT_YES;
+			return TRUE;
+		}
+		if (flags.end_disclose[idx] == DISCLOSE_YES_WITHOUT_PROMPT) {
+			*defquery = 'y';
+			return FALSE;
+		} else if (flags.end_disclose[idx] == DISCLOSE_NO_WITHOUT_PROMPT) {
+			*defquery = 'n';
+			return FALSE;
+		} else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_YES) {
+			*defquery = 'y';
+			return TRUE;
+		} else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_NO) {
+			*defquery = 'n';
+			return TRUE;
+		}
+	}
+	if (defquery)impossible("should_query_disclose_option: bad category %c", category);
+	else impossible("should_query_disclose_option: null defquery");
+	return TRUE;
+}
+
 STATIC_OVL void
 disclose(how,taken)
 int how;
 boolean taken;
 {
-	char	c;
+	char	c = 0;
 	char	qbuf[QBUFSZ];
+	int defquery;
+	boolean ask;
 
-	if (invent && !done_stopprint &&
-		(!flags.end_disclose[0] || index(flags.end_disclose, 'i'))) {
+	if (invent) {
 	    if(taken)
 		Sprintf(qbuf,"Do you want to see what you had when you %s?",
 			(how == QUIT) ? "quit" : "died");
 	    else
 		Strcpy(qbuf,"Do you want your possessions identified?");
-	    if ((c = yn_function(qbuf, ynqchars, 'y')) == 'y') {
-	    /* New dump format by maartenj@cs.vu.nl */
-		struct obj *obj;
 
-		for (obj = invent; obj; obj = obj->nobj) {
-		    makeknown(obj->otyp);
-		    obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
+	    ask = should_query_disclose_option('i', &defquery);
+	    if (!done_stopprint) {
+		if (ask)
+		   c = yn_function(qbuf, ynqchars, defquery);
+		if ((!ask && defquery == 'y') || (ask && c == 'y')) {
+			/* New dump format by maartenj@cs.vu.nl */
+			struct obj *obj;
+			
+			for (obj = invent; obj; obj = obj->nobj) {
+			    makeknown(obj->otyp);
+			    obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
+			}
+			(void) display_inventory((char *)0, TRUE);
+			container_contents(invent, TRUE, TRUE);
 		}
-		(void) display_inventory((char *)0, TRUE);
-		container_contents(invent, TRUE, TRUE);
+		if (ask && c == 'q')  done_stopprint++;
 	    }
-	    if (c == 'q')  done_stopprint++;
 	}
 
-	if (!done_stopprint &&
-		(!flags.end_disclose[0] || index(flags.end_disclose, 'a'))) {
-	    c = yn_function("Do you want to see your attributes?",ynqchars,'y');
-	    if (c == 'y') enlightenment(how >= PANICKED ? 1 : 2); /* final */
-	    if (c == 'q') done_stopprint++;
+ 	ask = should_query_disclose_option('a', &defquery);
+	if (!done_stopprint) {
+	    if (ask)
+		c = yn_function("Do you want to see your attributes?",ynqchars, defquery);
+	    if ((!ask && defquery == 'y') || (ask && c == 'y'))
+		enlightenment(how >= PANICKED ? 1 : 2); /* final */
+	    if (ask && c == 'q') done_stopprint++;
 	}
 
-	if (!done_stopprint &&
-		(!flags.end_disclose[0] || index(flags.end_disclose, 'v'))) {
-	    list_vanquished();
-	}
+	ask = should_query_disclose_option('v', &defquery);
+	if (!done_stopprint)
+	    list_vanquished(defquery, ask);
 
-	if (!done_stopprint &&
-		(!flags.end_disclose[0] || index(flags.end_disclose, 'g'))) {
-	    list_genocided();
-	}
+	ask = should_query_disclose_option('g', &defquery);
+	if (!done_stopprint)
+	    list_genocided(defquery, ask);
 
-	if (!done_stopprint &&
-		(!flags.end_disclose[0] || index(flags.end_disclose, 'c'))) {
-	    c = yn_function("Do you want to see your conduct?",ynqchars,'y');
-	    if (c == 'y') show_conduct(how >= PANICKED ? 1 : 2);
-	    if (c == 'q') done_stopprint++;
+	ask = should_query_disclose_option('c', &defquery);
+	if (!done_stopprint) {
+	    if (ask)
+		c = yn_function("Do you want to see your conduct?",ynqchars,defquery);
+	    if ((!ask && defquery == 'y') || (ask && c == 'y'))
+		show_conduct(how >= PANICKED ? 1 : 2);
+	    if (ask && c == 'q') done_stopprint++;
 	}
 }
 
@@ -348,6 +396,11 @@
 	    u.uhunger = 500;
 	    newuhs(FALSE);
 	}
+	/* cure impending doom of sickness hero won't have time to fix */
+	if ((Sick & TIMEOUT) == 1) {
+	    u.usick_type = 0;
+	    Sick = 0;
+	}
 	if (how == CHOKING) init_uhunger();
 	nomovemsg = "You survived that attempt on your life.";
 	flags.move = 0;
@@ -370,18 +423,19 @@
     register struct obj *obj;
     register int i;
 
-    /* find amulets and gems, ignoring artifacts except for the AoY. */
+    /* find amulets and gems, ignoring all artifacts */
     for (obj = list; obj; obj = obj->nobj)
 	if (Has_contents(obj)) {
 	    get_valuables(obj->cobj);
+	} else if (obj->oartifact) {
+	    continue;
 	} else if (obj->oclass == AMULET_CLASS) {
 	    i = obj->otyp - FIRST_AMULET;
 	    if (!amulets[i].count) {
 		amulets[i].count = obj->quan;
 		amulets[i].typ = obj->otyp;
 	    } else amulets[i].count += obj->quan; /* always adds one */
-	} else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE &&
-		!obj->oartifact) {
+	} else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
 	    i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
 	    if (!gems[i].count) {
 		gems[i].count = obj->quan;
@@ -428,8 +482,7 @@
 			otmp->otyp == BELL_OF_OPENING ||
 			otmp->otyp == SPE_BOOK_OF_THE_DEAD ||
 			otmp->otyp == CANDELABRUM_OF_INVOCATION) {
-	    /* shopkeepers charge 100x; 250x is arbitrary */
-	    u.urexp += 250L * (long)objects[otmp->otyp].oc_cost;
+	    u.urexp += (arti_cost(otmp) * 5 / 2);
 	if (Has_contents(otmp))
 	    add_artifact_score(otmp->cobj);
     }
@@ -454,11 +507,12 @@
 	    otmp->known = otmp->bknown = otmp->dknown =
 		otmp->rknown = 1;
 	    /* assumes artifacts don't have quan>1 */
-	    Sprintf(pbuf, "%s (worth %ld zorkmids and %ld points)",
+	    Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
+		the_unique_obj(otmp) ? "The " : "",
 		otmp->oartifact ? artifact_name(xname(otmp), &dummy) :
 			OBJ_NAME(objects[otmp->otyp]),
-		100L * (long)objects[otmp->otyp].oc_cost,
-		250L * (long)objects[otmp->otyp].oc_cost);
+		arti_cost(otmp), currency(2L), 
+		arti_cost(otmp) * 5 / 2);
 	    putstr(endwin, 0, pbuf);
 	}
 	if (Has_contents(otmp))
@@ -476,13 +530,14 @@
 	winid endwin = WIN_ERR;
 	boolean bones_ok, have_windows = iflags.window_inited;
 	struct obj *corpse = (struct obj *)0;
+	long umoney;
 
 	/* kilbuf: used to copy killer in case it comes from something like
 	 *	xname(), which would otherwise get overwritten when we call
 	 *	xname() when listing possessions
 	 * pbuf: holds Sprintf'd output for raw_print and putstr
 	 */
-	if (how == ASCENDED)
+	if (how == ASCENDED || (!killer && how == GENOCIDED))
 		killer_format = NO_KILLER_PREFIX;
 	/* Avoid killed by "a" burning or "a" starvation */
 	if (!killer && (how == STARVING || how == BURNING))
@@ -540,8 +595,8 @@
 	program_state.gameover = 1;
 	/* in case of a subsequent panic(), there's no point trying to save */
 	program_state.something_worth_saving = 0;
-	/* turn off vision subsystem */
-	vision_recalc(2);
+	/* render vision subsystem inoperative */
+	iflags.vision_inited = 0;
 	/* might have been killed while using a disposable item, so make sure
 	   it's gone prior to inventory disclosure and creation of bones data */
 	inven_inuse(TRUE);
@@ -551,7 +606,7 @@
 	 * smiling... :-)  -3.
 	 */
 	if (moves <= 1 && how < PANICKED)	/* You die... --More-- */
-	    pline("Do not pass go.  Do not collect 200 zorkmids.");
+	    pline("Do not pass go.  Do not collect 200 %s.", currency(200L));
 
 	if (have_windows) wait_synch();	/* flush screen output */
 #ifndef NO_SIGNAL
@@ -619,8 +673,11 @@
 	    long tmp;
 	    int deepest = deepest_lev_reached(FALSE);
 
-	    u.ugold += hidden_gold();	/* accumulate gold from containers */
-	    tmp = u.ugold - u.ugold0;
+	    umoney = u.ugold;
+	    tmp = u.ugold0;
+	    umoney += hidden_gold();	/* accumulate gold from containers */
+	    tmp = umoney - tmp;		/* net gain */
+
 	    if (tmp < 0L)
 		tmp = 0L;
 	    if (how < PANICKED)
@@ -642,6 +699,10 @@
 	    corpse = (struct obj *)0;
 	}
 
+	/* update gold for the rip output, which can't use hidden_gold()
+	   (containers will be gone by then if bones just got saved...) */
+	u.ugold = umoney;
+
 	/* clean up unneeded windows */
 	if (have_windows) {
 	    wait_synch();
@@ -744,9 +805,9 @@
 			otmp->dknown = 1;	/* seen it (blindness fix) */
 			otmp->onamelth = 0;
 			otmp->quan = count;
-			Sprintf(pbuf, "%8ld %s (worth %ld zorkmids),",
+			Sprintf(pbuf, "%8ld %s (worth %ld %s),",
 				count, xname(otmp),
-				count * (long)objects[typ].oc_cost);
+				count * (long)objects[typ].oc_cost, currency(2L));
 			obfree(otmp, (struct obj *)0);
 		    } else {
 			Sprintf(pbuf,
@@ -782,7 +843,7 @@
 
 	if (!done_stopprint) {
 	    Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.",
-		    u.ugold, plur(u.ugold), moves, plur(moves));
+		    umoney, plur(umoney), moves, plur(moves));
 	    putstr(endwin, 0, pbuf);
 	}
 	if (!done_stopprint) {
@@ -842,7 +903,7 @@
 		    if (all_containers)
 			container_contents(box->cobj, identified, TRUE);
 		} else {
-		    pline("%s is empty.", The(xname(box)));
+		    pline("%s empty.", Tobjnam(box, "are"));
 		    display_nhwindow(WIN_MESSAGE, FALSE);
 		}
 	    }
@@ -871,7 +932,9 @@
 }
 
 STATIC_OVL void
-list_vanquished()
+list_vanquished(defquery, ask)
+int defquery;
+boolean ask;
 {
     register int i, lev;
     int ntypes = 0, max_lev = 0, nkilled;
@@ -891,10 +954,11 @@
      * includes all dead monsters, not just those killed by the player
      */
     if (ntypes != 0) {
-	c = yn_function("Do you want an account of creatures vanquished?",
-			ynqchars, 'n');
-	if (c == 'q') done_stopprint++;
-	if (c == 'y') {
+    	if (ask)
+	    c = yn_function("Do you want an account of creatures vanquished?",
+				ynqchars, defquery);
+	if (ask && c == 'q') done_stopprint++;
+	if ((!ask && defquery == 'y') || (ask && c == 'y')) {
 	    klwin = create_nhwindow(NHW_MENU);
 	    putstr(klwin, 0, "Vanquished creatures:");
 	    putstr(klwin, 0, "");
@@ -907,9 +971,15 @@
 			Sprintf(buf, "%s%s",
 				!type_is_pname(&mons[i]) ? "The " : "",
 				mons[i].mname);
-			if (nkilled > 1)
-			    Sprintf(eos(buf)," (%d time%s)",
-				    nkilled, plur(nkilled));
+			if (nkilled > 1) {
+			    switch (nkilled) {
+	    			case 2:  Sprintf(eos(buf)," (twice)");  break;
+	    			case 3:  Sprintf(eos(buf)," (thrice)");  break;
+	    			default: Sprintf(eos(buf)," (%d time%s)",
+				    		nkilled, plur(nkilled));
+				break;
+			    }
+			}
 		    } else {
 			/* trolls or undead might have come back,
 			   but we don't keep track of that */
@@ -949,7 +1019,9 @@
 }
 
 STATIC_OVL void
-list_genocided()
+list_genocided(defquery, ask)
+int defquery;
+boolean ask;
 {
     register int i;
     int ngenocided;
@@ -961,10 +1033,11 @@
 
     /* genocided species list */
     if (ngenocided != 0) {
-	c = yn_function("Do you want a list of species genocided?",
-			ynqchars, 'n');
-	if (c == 'q') done_stopprint++;
-	if (c == 'y') {
+    	if (ask)
+	    c = yn_function("Do you want a list of species genocided?",
+				ynqchars, defquery);
+	if (ask && c == 'q') done_stopprint++;
+	if ((!ask && defquery == 'y') || (ask && c == 'y')) {
 	    klwin = create_nhwindow(NHW_MENU);
 	    putstr(klwin, 0, "Genocided species:");
 	    putstr(klwin, 0, "");
diff -Naurd ../nethack-3.3.1/src/engrave.c ./src/engrave.c
--- ../nethack-3.3.1/src/engrave.c Sat Aug 5 00:32:10 2000
+++ ./src/engrave.c Fri Mar 22 14:40:55 2002
@@ -153,7 +153,7 @@
 	if ((x == u.ux) && (y == u.uy) && u.uswallow &&
 		is_animal(u.ustuck->data))
 	    return "maw";
-	else if (IS_AIR(lev->typ))
+	else if (IS_AIR(lev->typ) && Is_airlevel(&u.uz))
 	    return "air";
 	else if (is_pool(x,y))
 	    return "water";
@@ -336,7 +336,7 @@
 	    	char *et;
 	    	unsigned maxelen = BUFSZ - sizeof("You feel the words: \"\". ");
 	    	if (strlen(ep->engr_txt) > maxelen) {
-	    		strncpy(buf,  ep->engr_txt, maxelen);
+	    		(void) strncpy(buf,  ep->engr_txt, (int)maxelen);
 			buf[maxelen] = '\0';
 			et = buf;
 		} else
@@ -369,7 +369,8 @@
 	ep->engr_y = y;
 	ep->engr_txt = (char *)(ep + 1);
 	Strcpy(ep->engr_txt, s);
-	if(strcmp(s, "Elbereth")) exercise(A_WIS, TRUE);
+	/* engraving Elbereth shows wisdom */
+	if(!strcmp(s, "Elbereth")) exercise(A_WIS, TRUE);
 	ep->engr_time = e_time;
 	ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE-1);
 	ep->engr_lth = strlen(s) + 1;
@@ -450,16 +451,14 @@
 	char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */
 	const char *everb;	/* Present tense of engraving type */
 	const char *eloc;	/* Where the engraving is (ie dust/floor/...) */
-	register char *sp;	/* Place holder for space count of engr text */
-	register int len;	/* # of nonspace chars of new engraving text */
-	register int maxelen;	/* Max allowable length of new engraving text */
-	register int spct;	/* # of spaces in new engraving text */
-	register struct engr *oep = engr_at(u.ux,u.uy);
+	char *sp;		/* Place holder for space count of engr text */
+	int len;		/* # of nonspace chars of new engraving text */
+	int maxelen;		/* Max allowable length of engraving text */
+	struct engr *oep = engr_at(u.ux,u.uy);
 				/* The current engraving */
-	register struct obj *otmp; /* Object selected with which to engrave */
+	struct obj *otmp;	/* Object selected with which to engrave */
 	char *writer;
 
-
 	multi = 0;		/* moves consumed */
 	nomovemsg = (char *)0;	/* occupation end message */
 
@@ -531,10 +530,13 @@
 		return(0);
 	}
 	if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
+	    if (!levl[u.ux][u.uy].disturbed) {
 		You("disturb the undead!");
+		levl[u.ux][u.uy].disturbed = 1;
 		(void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS);
 		exercise(A_WIS, FALSE);
-		return(0);
+		return(1);
+	    }
 	}
 
 	/* SPFX for items */
@@ -676,7 +678,7 @@
 			    break;
 		    case WAN_CANCELLATION:
 		    case WAN_MAKE_INVISIBLE:
-			if(oep) {
+			if (oep && oep->engr_type != HEADSTONE) {
 			    if (!Blind)
 				pline_The("engraving on the %s vanishes!",
 					surface(u.ux,u.uy));
@@ -684,7 +686,7 @@
 			}
 			break;
 		    case WAN_TELEPORTATION:
-			if (oep) {
+			if (oep && oep->engr_type != HEADSTONE) {
 			    if (!Blind)
 				pline_The("engraving on the %s vanishes!",
 					surface(u.ux,u.uy));
@@ -783,15 +785,16 @@
 				if (!Blind)
 				    You("wipe out the message here.");
 				else
-				    Your("%s gets %s.", xname(otmp),
-					  is_ice(u.ux,u.uy) ?
-					  "frosty" : "dusty");
+				    Your("%s %s %s.", xname(otmp),
+					 otense(otmp, "get"),
+					 is_ice(u.ux,u.uy) ?
+					 "frosty" : "dusty");
 				dengr = TRUE;
 			    } else
 				Your("%s can't wipe out this engraving.",
 				     xname(otmp));
 			else
-			    Your("%s gets %s.", xname(otmp),
+			    Your("%s %s %s.", xname(otmp), otense(otmp, "get"),
 				  is_ice(u.ux,u.uy) ? "frosty" : "dusty");
 			break;
 		    default:
@@ -811,6 +814,18 @@
 		break;
 	}
 
+	if (IS_GRAVE(levl[u.ux][u.uy].typ)) {
+	    if (type == ENGRAVE || type == 0)
+		type = HEADSTONE;
+	    else {
+		/* ensures the "cannot wipe out" case */
+		type = DUST;
+		dengr = FALSE;
+		teleengr = FALSE;
+		buf[0] = (char)0;
+	    }
+	}
+
 	/* End of implement setup */
 
 	/* Identify stylus */
@@ -839,8 +854,9 @@
 	if (zapwand && (otmp->spe < 0)) {
 	    pline("%s %sturns to dust.",
 		  The(xname(otmp)), Blind ? "" : "glows violently, then ");
- You("are not going to get anywhere trying to write in the %s with your dust.",
-		is_ice(u.ux,u.uy) ? "frost" : "dust");
+	    if (!IS_GRAVE(levl[u.ux][u.uy].typ))
+		You("are not going to get anywhere trying to write in the %s with your dust.",
+		    is_ice(u.ux,u.uy) ? "frost" : "dust");
 	    useup(otmp);
 	    ptext = FALSE;
 	}
@@ -860,7 +876,10 @@
 
 	    /* Give player the choice to add to engraving. */
 
-	    if ( (type == oep->engr_type) && (!Blind ||
+	    if (type == HEADSTONE) {
+		/* no choice, only append */
+		c = 'y';
+	    } else if ( (type == oep->engr_type) && (!Blind ||
 		 (oep->engr_type == BURN) || (oep->engr_type == ENGRAVE)) ) {
 		c = yn_function("Do you want to add to the current engraving?",
 				ynqchars, 'y');
@@ -898,7 +917,7 @@
 				You("will overwrite the current message.");
 			    eow = TRUE;
 			}
-	}
+	    }
 	}
 
 	eloc = surface(u.ux,u.uy);
@@ -943,23 +962,15 @@
 	Sprintf(qbuf,"What do you want to %s the %s here?", everb, eloc);
 	getlin(qbuf, ebuf);
 
-	/* Mix up engraving if surface or state of mind is unsound.  */
-	/* Original kludge by stewr 870708.  modified by njm 910722. */
-	for (sp = ebuf; *sp; sp++)
-	    if ( ((type == DUST || type == ENGR_BLOOD) && !rn2(25)) ||
-		 (Blind   && !rn2(9)) || (Confusion     && !rn2(12)) ||
-		 (Stunned && !rn2(4)) || (Hallucination && !rn2(1)) )
-		 *sp = '!' + rn2(93); /* ASCII-code only */
-
 	/* Count the actual # of chars engraved not including spaces */
 	len = strlen(ebuf);
+	for (sp = ebuf; *sp; sp++) if (isspace(*sp)) len -= 1;
 
-	for (sp = ebuf, spct = 0; *sp; sp++) if (isspace(*sp)) spct++;
-
-	if ( (len == spct) || index(ebuf, '\033') ) {
+	if (len == 0 || index(ebuf, '\033')) {
 	    if (zapwand) {
 		if (!Blind)
-		    pline("%s glows, then fades.", The(xname(otmp)));
+		    pline("%s, then %s.",
+			  Tobjnam(otmp, "glow"), otense(otmp, "fade"));
 		return(1);
 	    } else {
 		pline(Never_mind);
@@ -967,12 +978,21 @@
 	    }
 	}
 
-	len -= spct;
-
 	/* A single `x' is the traditional signature of an illiterate person */
 	if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X')))
 	    u.uconduct.literate++;
 
+	/* Mix up engraving if surface or state of mind is unsound.
+	   Note: this won't add or remove any spaces. */
+	for (sp = ebuf; *sp; sp++) {
+	    if (isspace(*sp)) continue;
+	    if (((type == DUST || type == ENGR_BLOOD) && !rn2(25)) ||
+		    (Blind && !rn2(11)) || (Confusion && !rn2(7)) ||
+		    (Stunned && !rn2(4)) || (Hallucination && !rn2(2)))
+		*sp = ' ' + rnd(96 - 2);	/* ASCII '!' thru '~'
+						   (excludes ' ' and DEL) */
+	}
+
 	/* Previous engraving is overwritten */
 	if (eow) {
 	    del_engr(oep);
@@ -1004,12 +1024,19 @@
 			 *	 "ere", then "th".
 			 */
 		    Your("%s dull.", aobjnam(otmp, "get"));
+		    if (otmp->unpaid) {
+			struct monst *shkp = shop_keeper(*u.ushops);
+			if (shkp) {
+			    You("damage it, you pay for it!");
+			    bill_dummy_object(otmp);
+			}
+		    }
 		    if (len > maxelen) {
 			multi = -maxelen;
 			otmp->spe = -3;
-		    } else
-			if (len > 1) otmp->spe -= len >> 1;
-			else otmp->spe -= 1; /* Prevent infinite engraving */
+		    } else if (len > 1)
+			otmp->spe -= len >> 1;
+		    else otmp->spe -= 1; /* Prevent infinite engraving */
 		} else
 		    if ( (otmp->oclass == RING_CLASS) ||
 			 (otmp->oclass == GEM_CLASS) )
@@ -1067,6 +1094,7 @@
 	if (doblind && !resists_blnd(&youmonst)) {
 	    You("are blinded by the flash!");
 	    make_blinded((long)rnd(50),FALSE);
+	    if (!Blind) Your(vision_clears);
 	}
 
 	return(1);
diff -Naurd ../nethack-3.3.1/src/exper.c ./src/exper.c
--- ../nethack-3.3.1/src/exper.c Thu Aug 3 20:26:36 2000
+++ ./src/exper.c Fri Mar 22 14:40:55 2002
@@ -39,7 +39,7 @@
 experience(mtmp, nk)	/* return # of exp points for mtmp after nk killed */
 	register struct	monst *mtmp;
 	register int	nk;
-#if defined(applec)
+#if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
 # pragma unused(nk)
 #endif
 {
@@ -139,11 +139,11 @@
 	else if (u.uhp > u.uhpmax) u.uhp = u.uhpmax;
 
 	if (u.ulevel < urole.xlev)
-	    num = rn1(u.ulevel/2 + urole.enadv.lornd + urace.enadv.lornd,
-			urole.enadv.lofix + urace.enadv.lofix);
+	    num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.lornd + urace.enadv.lornd,
+	    		urole.enadv.lofix + urace.enadv.lofix);
 	else
-	    num = rn1(u.ulevel/2 + urole.enadv.hirnd + urace.enadv.hirnd,
-			urole.enadv.hifix + urace.enadv.hifix);
+	    num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.hirnd + urace.enadv.hirnd,
+	    		urole.enadv.hifix + urace.enadv.hifix);
 	num = enermod(num);		/* M. Stephenson */
 	u.uenmax -= num;
 	if (u.uenmax < 0) u.uenmax = 0;
@@ -179,12 +179,17 @@
 	num = newhp();
 	u.uhpmax += num;
 	u.uhp += num;
+	if (Upolyd) {
+	    num = rnd(8);
+	    u.mhmax += num;
+	    u.mh += num;
+	}
 	if (u.ulevel < urole.xlev)
 	    num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.lornd + urace.enadv.lornd,
-	    		urole.enadv.lofix + urace.enadv.lofix);
+			urole.enadv.lofix + urace.enadv.lofix);
 	else
 	    num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.hirnd + urace.enadv.hirnd,
-	    		urole.enadv.hifix + urace.enadv.hifix);
+			urole.enadv.hifix + urace.enadv.hifix);
 	num = enermod(num);	/* M. Stephenson */
 	u.uenmax += num;
 	u.uen += num;
diff -Naurd ../nethack-3.3.1/src/explode.c ./src/explode.c
--- ../nethack-3.3.1/src/explode.c Tue Jul 18 03:54:19 2000
+++ ./src/explode.c Fri Mar 22 14:40:55 2002
@@ -22,11 +22,12 @@
  * these disadvantages....
  */
 void
-explode(x, y, type, dam, olet)
+explode(x, y, type, dam, olet, expltype)
 int x, y;
 int type; /* the same as in zap.c */
 int dam;
 char olet;
+int expltype;
 {
 	int i, j, k, damu = dam;
 	boolean starting = 1;
@@ -39,6 +40,7 @@
 	int explmask[3][3];
 		/* 0=normal explosion, 1=do shieldeff, 2=do nothing */
 	boolean shopdamage = FALSE;
+	boolean generic = FALSE;
 
 	if (olet == WAND_CLASS)		/* retributive strike */
 		switch (Role_switch) {
@@ -180,7 +182,7 @@
 		for (i=0; i<3; i++) for (j=0; j<3; j++) {
 			if (explmask[i][j] == 2) continue;
 			tmp_at(starting ? DISP_BEAM : DISP_CHANGE,
-					    cmap_to_glyph(expl[i][j]));
+				explosion_to_glyph(expltype,expl[i][j]));
 			tmp_at(i+x-1, j+y-1);
 			starting = 0;
 		}
@@ -205,7 +207,8 @@
 		    /* Cover last shield glyph with blast symbol. */
 		    for (i=0; i<3; i++) for (j=0; j<3; j++) {
 			if (explmask[i][j] == 1)
-			    show_glyph(i+x-1,j+y-1,cmap_to_glyph(expl[i][j]));
+			    show_glyph(i+x-1,j+y-1,
+					explosion_to_glyph(expltype, expl[i][j]));
 		    }
 
 		} else {		/* delay a little bit. */
@@ -215,7 +218,11 @@
 
 		tmp_at(DISP_END, 0); /* clear the explosion */
 	} else {
-		if (flags.soundok) You_hear("a blast.");
+	    if (olet == MON_EXPLODE) {
+		str = "explosion";
+		generic = TRUE;
+	    }
+	    if (flags.soundok) You_hear("a blast.");
 	}
 
     if (dam)
@@ -258,7 +265,7 @@
 				      (adtyp == AD_ACID) ? "burned" :
 				       "fried");
 		} else if (cansee(i+x-1, j+y-1))
-		pline("%s is caught in the %s!", Monnam(mtmp), str);
+		    pline("%s is caught in the %s!", Monnam(mtmp), str);
 
 		idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int) adtyp);
 		idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int) adtyp);
@@ -277,9 +284,9 @@
 			int mdam = dam;
 
 			if (resist(mtmp, olet, 0, FALSE)) {
-				if (cansee(i+x-1,j+y-1))
-				    pline("%s resists the %s!", Monnam(mtmp), str);
-				mdam = dam/2;
+			    if (cansee(i+x-1,j+y-1))
+				pline("%s resists the %s!", Monnam(mtmp), str);
+			    mdam = dam/2;
 			}
 			if (mtmp == u.ustuck)
 				mdam *= 2;
@@ -294,7 +301,7 @@
 			/* KMH -- Don't blame the player for pets killing gas spores */
 			if (!flags.mon_moving) killed(mtmp);
 			else monkilled(mtmp, "", (int)adtyp);
-		}
+		} else if (!flags.mon_moving) setmangry(mtmp);
 	}
 
 	/* Do your injury last */
@@ -316,18 +323,27 @@
 		destroy_item(WAND_CLASS, (int) adtyp);
 
 		ugolemeffects((int) adtyp, damu);
-		if (uhurt == 2) u.uhp -= damu, flags.botl = 1;
+		if (uhurt == 2) {
+		    if (Upolyd)
+		    	u.mh  -= damu;
+		    else
+			u.uhp -= damu;
+		    flags.botl = 1;
+		}
 
-		if (u.uhp <= 0) {
+		if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) {
+		    if (Upolyd) {
+			rehumanize();
+		    } else {
 			if (olet == MON_EXPLODE) {
 			    /* killer handled by caller */
-			    if (str != killer_buf)
+			    if (str != killer_buf && !generic)
 				Strcpy(killer_buf, str);
 			    killer_format = KILLED_BY_AN;
 			} else if (type >= 0 && olet != SCROLL_CLASS) {
 			    killer_format = NO_KILLER_PREFIX;
 			    Sprintf(killer_buf, "caught %sself in %s own %s",
-				    him[flags.female], his[flags.female], str);
+				    uhim(), uhis(), str);
 			} else {
 			    killer_format = KILLED_BY;
 			    Strcpy(killer_buf, str);
@@ -336,6 +352,7 @@
 			/* Known BUG: BURNING suppresses corpse in bones data,
 			   but done does not handle killer reason correctly */
 			done((adtyp == AD_FIRE) ? BURNING : DIED);
+		    }
 		}
 		exercise(A_STR, FALSE);
 	}
@@ -345,6 +362,11 @@
 			       adtyp == AD_COLD ? "shatter" :
 			       adtyp == AD_DISN ? "disintegrate" : "destroy");
 	}
+
+	/* explosions are noisy */
+	i = dam * dam;
+	if (i < 50) i = 50;	/* in case random damage is very small */
+	wake_nearto(x, y, i);
 }
 #endif /* OVL0 */
 #ifdef OVL1
@@ -394,20 +416,18 @@
 		qtmp = otmp->quan - 1;
 		if (qtmp > LARGEST_INT) qtmp = LARGEST_INT;
 		qtmp = (long)rnd((int)qtmp);
-		(void) splitobj(otmp, qtmp);
-		if (qtmp < otmp->quan)
-			split_up = TRUE;
+		otmp = splitobj(otmp, qtmp);
+		if (rn2(qtmp))
+		    split_up = TRUE;
 		else
-			split_up = FALSE;
-	    }
+		    split_up = FALSE;
+	    } else
+		split_up = FALSE;
 	    if (individual_object) {
-		if (split_up) {
-			if (otmp->where == OBJ_FLOOR)
-				obj = otmp->nexthere;
-			else
-				obj = otmp->nobj;
+ 		if (split_up) {
+		    obj = otmp;
 		} else
-			obj = (struct obj *)0;
+		    obj = (struct obj *)0;
 	    }
 	    obj_extract_self(otmp);
 	    used_up = FALSE;
@@ -417,15 +437,20 @@
 			&& ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE))
 			&& rn2(10)) {
 		if (otmp->otyp == BOULDER) {
-		    pline("%s breaks apart.",The(xname(otmp)));
+		    pline("%s apart.", Tobjnam(otmp, "break"));
 		    fracture_rock(otmp);
 		    place_object(otmp, sx, sy);	/* put fragments on floor */
+		    if ((otmp = sobj_at(BOULDER, sx, sy)) != 0) {
+			/* another boulder here, restack it to the top */
+			obj_extract_self(otmp);
+			place_object(otmp, sx, sy);
+		    }
 		} else {
 		    struct trap *trap;
 
 		    if ((trap = t_at(sx,sy)) && trap->ttyp == STATUE_TRAP)
 			    deltrap(trap);
-		    pline("%s crumbles.",The(xname(otmp)));
+		    pline("%s.", Tobjnam(otmp, "crumble"));
 		    (void) break_statue(otmp);
 		    place_object(otmp, sx, sy);	/* put fragments on floor */
 		}
@@ -543,7 +568,7 @@
 {
 /* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */
 #define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */
-    explode(x, y, ZT_SPELL_O_FIRE, d(4,4), BURNING_OIL);
+    explode(x, y, ZT_SPELL_O_FIRE, d(4,4), BURNING_OIL, EXPL_FIERY);
 }
 
 #endif /* OVL1 */
diff -Naurd ../nethack-3.3.1/src/extralev.c ./src/extralev.c
--- ../nethack-3.3.1/src/extralev.c Mon Nov 29 02:27:55 1999
+++ ./src/extralev.c Fri Mar 22 14:40:55 2002
@@ -302,42 +302,42 @@
 	ghost = christen_monst(ghost, roguename());
 
 	if (rn2(4)) {
-		ghostobj = mksobj_at(FOOD_RATION,x,y,FALSE);
+		ghostobj = mksobj_at(FOOD_RATION, x, y, FALSE, FALSE);
 		ghostobj->quan = (long) rnd(7);
 		ghostobj->owt = weight(ghostobj);
 	}
 	if (rn2(2)) {
-		ghostobj = mksobj_at(MACE,x,y,FALSE);
+		ghostobj = mksobj_at(MACE, x, y, FALSE, FALSE);
 		ghostobj->spe = rnd(3);
 		if (rn2(4)) curse(ghostobj);
 	} else {
-		ghostobj = mksobj_at(TWO_HANDED_SWORD,x,y,FALSE);
+		ghostobj = mksobj_at(TWO_HANDED_SWORD, x, y, FALSE, FALSE);
 		ghostobj->spe = rnd(5) - 2;
 		if (rn2(4)) curse(ghostobj);
 	}
-	ghostobj = mksobj_at(BOW,x,y,FALSE);
+	ghostobj = mksobj_at(BOW, x, y, FALSE, FALSE);
 	ghostobj->spe = 1;
 	if (rn2(4)) curse(ghostobj);
 
-	ghostobj = mksobj_at(ARROW,x,y,FALSE);
+	ghostobj = mksobj_at(ARROW, x, y, FALSE, FALSE);
 	ghostobj->spe = 0;
 	ghostobj->quan = (long) rn1(10,25);
 	ghostobj->owt = weight(ghostobj);
 	if (rn2(4)) curse(ghostobj);
 
 	if (rn2(2)) {
-		ghostobj = mksobj_at(RING_MAIL,x,y,FALSE);
+		ghostobj = mksobj_at(RING_MAIL, x, y, FALSE, FALSE);
 		ghostobj->spe = rn2(3);
 		if (!rn2(3)) ghostobj->oerodeproof = TRUE;
 		if (rn2(4)) curse(ghostobj);
 	} else {
-		ghostobj = mksobj_at(PLATE_MAIL,x,y,FALSE);
+		ghostobj = mksobj_at(PLATE_MAIL, x, y, FALSE, FALSE);
 		ghostobj->spe = rnd(5) - 2;
 		if (!rn2(3)) ghostobj->oerodeproof = TRUE;
 		if (rn2(4)) curse(ghostobj);
 	}
 	if (rn2(2)) {
-		ghostobj = mksobj_at(FAKE_AMULET_OF_YENDOR,x,y,TRUE);
+		ghostobj = mksobj_at(FAKE_AMULET_OF_YENDOR, x, y, TRUE, FALSE);
 		ghostobj->known = TRUE;
 	}
 }
diff -Naurd ../nethack-3.3.1/src/files.c ./src/files.c
--- ../nethack-3.3.1/src/files.c Wed Aug 9 18:38:07 2000
+++ ./src/files.c Fri Mar 22 14:40:55 2002
@@ -1,10 +1,14 @@
-/*	SCCS Id: @(#)files.c	3.3	2000/04/27	*/
+/*	SCCS Id: @(#)files.c	3.4	2002/02/23	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 #include "dlb.h"
 
+#ifdef TTY_GRAPHICS
+#include "wintty.h" /* more() */
+#endif
+
 #include <ctype.h>
 
 #if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)
@@ -71,6 +75,12 @@
 char SAVEP[SAVESIZE];	/* holds path of directory for save file */
 #endif
 
+#ifdef WIZARD
+#define WIZKIT_MAX 128
+static char wizkit[WIZKIT_MAX];
+STATIC_DCL FILE *NDECL(fopen_wizkit_file);
+#endif
+
 #ifdef AMIGA
 extern char PATH[];	/* see sys/amiga/amidos.c */
 extern char bbs_id[];
@@ -80,6 +90,7 @@
 # endif
 
 #include <libraries/dos.h>
+extern void FDECL(amii_set_text_font, ( char *, int ));
 #endif
 
 #if defined(WIN32) || defined(MSDOS)
@@ -91,13 +102,17 @@
 #define DeleteFile unlink
 #endif
 
+#ifdef USER_SOUNDS
+extern char *sounddir;
+#endif
+
 extern int n_dgns;		/* from dungeon.c */
 
 STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*));
 STATIC_DCL char *NDECL(set_bonestemp_name);
 #ifdef COMPRESS
-STATIC_DCL void FDECL(redirect, (char *,char *,FILE *,BOOLEAN_P));
-STATIC_DCL void FDECL(docompress_file, (char *,BOOLEAN_P));
+STATIC_DCL void FDECL(redirect, (const char *,const char *,FILE *,BOOLEAN_P));
+STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P));
 #endif
 STATIC_DCL char *FDECL(make_lockname, (const char *,char *));
 STATIC_DCL FILE *FDECL(fopen_config_file, (const char *));
@@ -668,7 +650,7 @@
 
 STATIC_OVL void
 redirect(filename, mode, stream, uncomp)
-char *filename, *mode;
+const char *filename, *mode;
 FILE *stream;
 boolean uncomp;
 {
@@ -688,7 +670,7 @@
  */
 STATIC_OVL void
 docompress_file(filename, uncomp)
-char *filename;
+const char *filename;
 boolean uncomp;
 {
 	char cfn[80];
@@ -699,6 +681,9 @@
 # endif
 	int i = 0;
 	int f;
+# ifdef TTY_GRAPHICS
+	boolean istty = !strncmpi(windowprocs.name, "tty", 3);
+# endif
 
 	Strcpy(cfn, filename);
 # ifdef COMPRESS_EXTENSION
@@ -738,7 +723,24 @@
 	args[++i] = (char *)0;
 
 	f = fork();
+# ifdef TTY_GRAPHICS
+	/* If we don't do this and we are right after a y/n question *and*
+	 * there is an error message from the compression, the 'y' or 'n' can
+	 * end up being displayed after the error message.
+	 */
+	if (istty)
+	    mark_synch();
+# endif
 	if (f == 0) {	/* child */
+# ifdef TTY_GRAPHICS
+		/* any error messages from the compression must come out after
+		 * the first line, because the more() to let the user read
+		 * them will have to clear the first line.  This should be
+		 * invisible if there are no error messages.
+		 */
+		if (istty)
+		    raw_print("");
+# endif
 		/* run compressor without privileges, in case other programs
 		 * have surprises along the line of gzip once taking filenames
 		 * in GZIP.
@@ -789,6 +791,21 @@
 		/* no message needed for compress case; life will go on */
 		(void) unlink(cfn);
 	    }
+#ifdef TTY_GRAPHICS
+	    /* Give them a chance to read any error messages from the
+	     * compression--these would go to stdout or stderr and would get
+	     * overwritten only in tty mode.  It's still ugly, since the
+	     * messages are being written on top of the screen, but at least
+	     * the user can read them.
+	     */
+	    if (istty)
+	    {
+		clear_nhwindow(WIN_MESSAGE);
+		more();
+		/* No way to know if this is feasible */
+		/* doredraw(); */
+	    }
+#endif
 	}
 }
 #endif	/* COMPRESS */
@@ -1288,18 +1305,18 @@
 #else /*NOCWD_ASSUMPTIONS*/
 # ifdef MICRO
 	} else if (match_varname(buf, "HACKDIR", 4)) {
-		(void) strncpy(hackdir, bufp, PATHLEN);
+		(void) strncpy(hackdir, bufp, PATHLEN-1);
 #  ifdef MFLOPPY
 	} else if (match_varname(buf, "RAMDISK", 3)) {
 				/* The following ifdef is NOT in the wrong
 				 * place.  For now, we accept and silently
 				 * ignore RAMDISK */
 #   ifndef AMIGA
-		(void) strncpy(tmp_ramdisk, bufp, PATHLEN);
+		(void) strncpy(tmp_ramdisk, bufp, PATHLEN-1);
 #   endif
 #  endif
 	} else if (match_varname(buf, "LEVELS", 4)) {
-		(void) strncpy(tmp_levels, bufp, PATHLEN);
+		(void) strncpy(tmp_levels, bufp, PATHLEN-1);
 
 	} else if (match_varname(buf, "SAVE", 4)) {
 #  ifdef MFLOPPY
@@ -1319,7 +1336,7 @@
 		    saveprompt = flags.asksavedisk;
 # endif
 
-		(void) strncpy(SAVEP, bufp, PATHLEN);
+		(void) strncpy(SAVEP, bufp, SAVESIZE-1);
 		append_slash(SAVEP);
 # endif /* MICRO */
 #endif /*NOCWD_ASSUMPTIONS*/
@@ -1336,6 +1353,8 @@
 	} else if (match_varname(buf, "CATNAME", 3)) {
 	    (void) strncpy(catname, bufp, PL_PSIZ-1);
 
+	} else if (match_varname(buf, "BOULDER", 3)) {
+	    (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, 1, "BOULDER");
 	} else if (match_varname(buf, "GRAPHICS", 4)) {
 	    len = get_uchars(fp, buf, bufp, translate, MAXPCHARS, "GRAPHICS");
 	    assign_graphics(translate, len, MAXPCHARS, 0);
@@ -1361,10 +1380,13 @@
 	    (void) get_uchars(fp, buf, bufp, translate,
 					WARNCOUNT, "WARNINGS");
 	    assign_warnings(translate);
+#ifdef WIZARD
+	} else if (match_varname(buf, "WIZKIT", 6)) {
+	    (void) strncpy(wizkit, bufp, WIZKIT_MAX-1);
+#endif
 #ifdef AMIGA
 	} else if (match_varname(buf, "FONT", 4)) {
 		char *t;
-		extern void amii_set_text_font( char *, int );
 
 		if( t = strchr( buf+5, ':' ) )
 		{
@@ -1373,9 +1395,7 @@
 		    *t = ':';
 		}
 	} else if (match_varname(buf, "PATH", 4)) {
-		(void) strncpy(PATH, bufp, PATHLEN);
-#endif
-#ifdef AMIGA
+		(void) strncpy(PATH, bufp, PATHLEN-1);
 	} else if (match_varname(buf, "DEPTH", 5)) {
 		extern int amii_numcolors;
 		int val = atoi( bufp );
@@ -1390,8 +1410,10 @@
 		}
 	} else if (match_varname(buf, "SCREENMODE", 10 )) {
 		extern long amii_scrnmode;
-		if( sscanf(bufp, "%x", &amii_scrnmode) != 1 )
-			amii_scrnmode = 0;
+		if (!stricmp(bufp,"req"))
+		    amii_scrnmode = 0xffffffff; /* Requester */
+		else if( sscanf(bufp, "%x", &amii_scrnmode) != 1 )
+		    amii_scrnmode = 0;
 	} else if (match_varname(buf, "MSGPENS", 7)) {
 		extern int amii_msgAPen, amii_msgBPen;
 		char *t = strtok(bufp, ",/");
@@ -1449,6 +1471,34 @@
 			sscanf(t, "%hx", &amii_init_map[i]);
 		}
 		amii_setpens( amii_numcolors = i );
+	} else if (match_varname(buf, "FGPENS", 6)) {
+		extern int foreg[ AMII_MAXCOLORS ];
+		int i;
+		char *t;
+
+		for (i = 0, t = strtok(bufp, ",/");
+			i < AMII_MAXCOLORS && t != (char *)0;
+			t = strtok((char *)0, ",/"), ++i)
+		{
+			sscanf(t, "%d", &foreg[i]);
+		}
+	} else if (match_varname(buf, "BGPENS", 6)) {
+		extern int backg[ AMII_MAXCOLORS ];
+		int i;
+		char *t;
+
+		for (i = 0, t = strtok(bufp, ",/");
+			i < AMII_MAXCOLORS && t != (char *)0;
+			t = strtok((char *)0, ",/"), ++i)
+		{
+			sscanf(t, "%d", &backg[i]);
+		}
+#endif
+#ifdef USER_SOUNDS
+	} else if (match_varname(buf, "SOUNDDIR", 8)) {
+		sounddir = (char *)strdup(bufp);
+	} else if (match_varname(buf, "SOUND", 5)) {
+		add_sound_mapping(bufp);
 #endif
 #ifdef QT_GRAPHICS
 	} else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
@@ -1499,14 +1561,19 @@
 # endif
 	tmp_levels[0] = 0;
 #endif
+	/* begin detection of duplicate configfile options */
+	set_duplicate_opt_detection(1);
 
 	while (fgets(buf, 4*BUFSZ, fp)) {
 		if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) {
-			raw_printf("Bad option line:  \"%s\"", buf);
+			raw_printf("Bad option line:  \"%.50s\"", buf);
 			wait_synch();
 		}
 	}
 	(void) fclose(fp);
+	
+	/* turn off detection of duplicate configfile options */
+	set_duplicate_opt_detection(0);
 
 #if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
 	/* should be superseded by fqn_prefix[] */
@@ -1527,6 +1594,104 @@
 	return;
 }
 
+#ifdef WIZARD
+STATIC_OVL FILE *
+fopen_wizkit_file()
+{
+	FILE *fp;
+#if defined(VMS) || defined(UNIX)
+	char	tmp_wizkit[BUFSZ];
+#endif
+	char *envp;
+
+	envp = nh_getenv("WIZKIT");
+	if (envp && *envp) (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
+	if (!wizkit[0]) return (FILE *)0;
+
+#ifdef UNIX
+	if (access(wizkit, 4) == -1) {
+		/* 4 is R_OK on newer systems */
+		/* nasty sneaky attempt to read file through
+		 * NetHack's setuid permissions -- this is a
+		 * place a file name may be wholly under the player's
+		 * control
+		 */
+		raw_printf("Access to %s denied (%d).",
+				wizkit, errno);
+		wait_synch();
+		/* fall through to standard names */
+	} else
+#endif
+	if ((fp = fopenp(wizkit, "r")) != (FILE *)0) {
+	    return(fp);
+#if defined(UNIX) || defined(VMS)
+	} else {
+	    /* access() above probably caught most problems for UNIX */
+	    raw_printf("Couldn't open requested config file %s (%d).",
+				wizkit, errno);
+	    wait_synch();
+#endif
+	}
+
+#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
+	if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r"))
+								!= (FILE *)0)
+		return(fp);
+#else
+# ifdef VMS
+	envp = nh_getenv("HOME");
+	if (envp)
+		Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
+	else
+		Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
+	if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
+		return(fp);
+# else	/* should be only UNIX left */
+	envp = nh_getenv("HOME");
+	if (envp)
+		Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
+	else 	Strcpy(tmp_wizkit, wizkit);
+	if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
+		return(fp);
+	else if (errno != ENOENT) {
+		/* e.g., problems when setuid NetHack can't search home
+		 * directory restricted to user */
+		raw_printf("Couldn't open default wizkit file %s (%d).",
+					tmp_wizkit, errno);
+		wait_synch();
+	}
+# endif
+#endif
+	return (FILE *)0;
+}
+
+void
+read_wizkit()
+{
+	FILE *fp;
+	char *ep, buf[BUFSZ];
+	struct obj *otmp;
+	if (!wizard || !(fp = fopen_wizkit_file())) return;
+
+	while (fgets(buf, 4*BUFSZ, fp)) {
+		if ((ep = index(buf, '\n'))) *ep = '\0';
+		if (buf[0]) {
+			otmp = readobjnam(buf, (struct obj *)0, FALSE);
+			if (otmp) {
+			    if (otmp != &zeroobj)
+				otmp = addinv(otmp);
+			} else {
+			    raw_printf("Bad wizkit item: \"%.50s\"", buf);
+			    wait_synch();
+			}
+		}
+	}
+	(void) fclose(fp);
+	return;
+}
+
+#endif /*WIZARD*/
+
 /* ----------  END CONFIG FILE HANDLING ----------- */
 
 /* ----------  BEGIN SCOREBOARD CREATION ----------- */
diff -Naurd ../nethack-3.3.1/src/fountain.c ./src/fountain.c
--- ../nethack-3.3.1/src/fountain.c Sat Aug 5 00:37:04 2000
+++ ./src/fountain.c Fri Mar 22 14:41:02 2002
@@ -55,7 +55,7 @@
 	/* Give those on low levels a (slightly) better chance of survival */
 	    if (rnd(100) > (80 + level_difficulty())) {
 		pline("Grateful for %s release, %s grants you a wish!",
-		      his[pronoun_gender(mtmp)], he[pronoun_gender(mtmp)]);
+		      mhis(mtmp), mhe(mtmp));
 		makewish();
 		mongone(mtmp);
 	    } else if (t_at(mtmp->mx, mtmp->my))
@@ -126,7 +126,7 @@
 	water_damage(level.objects[x][y], FALSE, TRUE);
 
 	if ((mtmp = m_at(x, y)) != 0)
-		(void) minwater(mtmp);
+		(void) minliquid(mtmp);
 	else
 		newsym(x,y);
 }
@@ -135,8 +135,9 @@
 dofindgem() /* Find a gem in the sparkling waters. */
 {
 	if (!Blind) You("spot a gem in the sparkling waters!");
+	else You_feel("a gem here!");
 	(void) mksobj_at(rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE-1),
-						u.ux, u.uy, FALSE);
+			 u.ux, u.uy, FALSE, FALSE);
 	levl[u.ux][u.uy].looted |= F_LOOTED;
 	newsym(u.ux, u.uy);
 	exercise(A_WIS, TRUE);			/* a discovery! */
@@ -325,7 +326,7 @@
 			pline("This water gives you bad breath!");
 			for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
 			    if(!DEADMONSTER(mtmp))
-				mtmp->mflee = 1;
+				monflee(mtmp, 0, FALSE, FALSE);
 			}
 			break;
 
@@ -380,6 +381,7 @@
 			obj->oerodeproof = TRUE;
 			exercise(A_WIS, TRUE);
 		}
+		update_inventory();
 		levl[u.ux][u.uy].typ = ROOM;
 		levl[u.ux][u.uy].looted = 0;
 		if(Invisible) newsym(u.ux, u.uy);
@@ -461,6 +463,7 @@
 		    newsym(u.ux,u.uy);
 		    break;
 	}
+	update_inventory();
 	dryup(u.ux, u.uy, TRUE);
 }
 
@@ -552,7 +555,7 @@
 		case 10: pline("This water contains toxic wastes!");
 			if (!Unchanging) {
 				You("undergo a freakish metamorphosis!");
-				polyself();
+				polyself(FALSE);
 			}
 			break;
 		/* more odd messages --JJB */
diff -Naurd ../nethack-3.3.1/src/hack.c ./src/hack.c
--- ../nethack-3.3.1/src/hack.c Sat Jul 22 01:59:06 2000
+++ ./src/hack.c Fri Mar 22 14:41:02 2002
@@ -1,17 +1,18 @@
-/*	SCCS Id: @(#)hack.c	3.3	2000/04/22	*/
+/*	SCCS Id: @(#)hack.c	3.4	2002/03/09	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 
 #ifdef OVL1
-static void NDECL(maybe_wail);
+STATIC_DCL void NDECL(maybe_wail);
 #endif /*OVL1*/
 STATIC_DCL int NDECL(moverock);
 STATIC_DCL int FDECL(still_chewing,(XCHAR_P,XCHAR_P));
 #ifdef SINKS
 STATIC_DCL void NDECL(dosinkfall);
 #endif
+STATIC_DCL void NDECL(findtravelpath);
 STATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int));
 
 STATIC_DCL void FDECL(move_update, (BOOLEAN_P));
@@ -132,16 +133,14 @@
 		switch(ttmp->ttyp) {
 		case LANDMINE:
 		    if (rn2(10)) {
-			pline("KAABLAMM!!!  %s triggers %s land mine.",
-				The(xname(otmp)),
+			pline("KAABLAMM!!!  %s %s land mine.",
+				Tobjnam(otmp, "trigger"),
 				ttmp->madeby_u ? "your" : "a");
 			obj_extract_self(otmp);
 			place_object(otmp, rx, ry);
-			deltrap(ttmp);
-			del_engr_at(rx,ry);
-			scatter(rx,ry, 4,
-				MAY_DESTROY|MAY_HIT|MAY_FRACTURE|VIS_EFFECTS,
-				(struct obj *)0);
+			blow_up_landmine(ttmp);
+			/* if the boulder remains, it should fill the pit */
+			fill_pit(u.ux, u.uy);
 			if (cansee(rx,ry)) newsym(rx,ry);
 			continue;
 		    }
@@ -160,9 +159,15 @@
 		    continue;
 		case HOLE:
 		case TRAPDOOR:
-		    pline("%s %s and plugs a %s in the %s!",
-			  The(xname(otmp)),
-			  (ttmp->ttyp == TRAPDOOR) ? "triggers" : "falls into",
+		    if (Blind)
+			pline("Kerplunk!  You no longer feel %s.",
+				the(xname(otmp)));
+		    else
+			pline("%s%s and %s a %s in the %s!",
+			  Tobjnam(otmp,
+			   (ttmp->ttyp == TRAPDOOR) ? "trigger" : "fall"),
+			  (ttmp->ttyp == TRAPDOOR) ? nul : " into",
+			  otense(otmp, "plug"),
 			  (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole",
 			  surface(rx, ry));
 		    deltrap(ttmp);
@@ -313,28 +318,36 @@
 	(void) memset((genericptr_t)&digging, 0, sizeof digging);
 
     if (!boulder && IS_ROCK(lev->typ) && !may_dig(x,y)) {
-	You("hurt your teeth on the hard stone.");
+	You("hurt your teeth on the %s.",
+	    IS_TREE(lev->typ) ? "tree" : "hard stone");
 	nomul(0);
 	return 1;
     } else if (digging.pos.x != x || digging.pos.y != y ||
 		!on_level(&digging.level, &u.uz)) {
 	digging.down = FALSE;
 	digging.chew = TRUE;
+	digging.warned = FALSE;
 	digging.pos.x = x;
 	digging.pos.y = y;
 	assign_level(&digging.level, &u.uz);
 	/* solid rock takes more work & time to dig through */
-	digging.effort = (IS_ROCK(lev->typ) ? 30 : 60) + u.udaminc;
+	digging.effort =
+	    (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc;
 	You("start chewing %s %s.",
-	    boulder ? "on a" : "a hole in the",
-	    boulder ? "boulder" : IS_ROCK(lev->typ) ? "rock" : "door");
+	    (boulder || IS_TREE(lev->typ)) ? "on a" : "a hole in the",
+	    boulder ? "boulder" :
+	    IS_TREE(lev->typ) ? "tree" : IS_ROCK(lev->typ) ? "rock" : "door");
+	watch_dig((struct monst *)0, x, y, FALSE);
 	return 1;
     } else if ((digging.effort += (30 + u.udaminc)) <= 100)  {
 	if (flags.verbose)
 	    You("%s chewing on the %s.",
 		digging.chew ? "continue" : "begin",
-		boulder ? "boulder" : IS_ROCK(lev->typ) ? "rock" : "door");
+		boulder ? "boulder" :
+		IS_TREE(lev->typ) ? "tree" :
+		IS_ROCK(lev->typ) ? "rock" : "door");
 	digging.chew = TRUE;
+	watch_dig((struct monst *)0, x, y, FALSE);
 	return 1;
     }
 
@@ -434,18 +447,19 @@
 	register struct obj *obj;
 
 	if (is_floater(youmonst.data) || (HLevitation & FROMOUTSIDE)) {
-		You("wobble unsteadily for a moment.");
+	    You("wobble unsteadily for a moment.");
 	} else {
-		You("crash to the floor!");
-		losehp((rn1(10, 20 - (int)ACURR(A_CON))),
-			fell_on_sink, NO_KILLER_PREFIX);
-		exercise(A_DEX, FALSE);
-		for(obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
-		    if(obj->oclass == WEAPON_CLASS) {
-			You("fell on %s.",doname(obj));
-			losehp(rnd(3), fell_on_sink, NO_KILLER_PREFIX);
-			exercise(A_CON, FALSE);
-		    }
+	    You("crash to the floor!");
+	    losehp(rn1(8, 25 - (int)ACURR(A_CON)),
+		   fell_on_sink, NO_KILLER_PREFIX);
+	    exercise(A_DEX, FALSE);
+	    selftouch("Falling, you");
+	    for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
+		if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) {
+		    You("fell on %s.", doname(obj));
+		    losehp(rnd(3), fell_on_sink, NO_KILLER_PREFIX);
+		    exercise(A_CON, FALSE);
+		}
 	}
 
 	ELevitation &= ~W_ARTI;
@@ -511,11 +525,205 @@
 #endif /* OVL1 */
 #ifdef OVL3
 
+/* return TRUE if (dx,dy) is an OK place to move */
+boolean 
+test_move(ux, uy, dx, dy, test_only)
+int ux, uy, dx, dy;
+boolean test_only;
+{
+    int x = ux+dx;
+    int y = uy+dy;
+    register struct rm *tmpr = &levl[x][y];
+    register struct rm *ust;
+
+    /*
+     *  Check for physical obstacles.  First, the place we are going.
+     */
+    if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
+	if (Blind && !test_only) feel_location(x,y);
+	if (Passes_walls && may_passwall(x,y)) {
+	    ;	/* do nothing */
+	} else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
+	    /* Eat the rock. */
+	    if (!test_only && still_chewing(x,y)) return FALSE;
+	} else if (flags.autodig && !flags.run && !flags.nopick &&
+		   uwep && is_pick(uwep)) {
+	/* MRKR: Automatic digging when wielding the appropriate tool */
+	    if (!test_only)
+		(void) use_pick_axe2(uwep);
+	    return FALSE;
+	} else {
+	    if ( !test_only ) {
+		if (Is_stronghold(&u.uz) && is_db_wall(x,y))
+		    pline_The("drawbridge is up!");
+		if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz))
+	    		pline_The("Sokoban walls resist your ability.");
+	    }
+	    return FALSE;
+	}
+    } else if (IS_DOOR(tmpr->typ)) {
+	if (closed_door(x,y)) {
+	    if (Blind && !test_only) feel_location(x,y);
+	    if (Passes_walls)
+		;	/* do nothing */
+	    else if (can_ooze(&youmonst)) {
+		if ( !test_only ) You("ooze under the door.");
+	    } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
+		/* Eat the door. */
+		if (!test_only && still_chewing(x,y)) return FALSE;
+	    } else {
+		if ( !test_only ) {
+		    if (amorphous(youmonst.data))
+			You("try to ooze under the door, but can't squeeze your possessions through.");
+		    else if (x == ux || y == uy) {
+			if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) {
+#ifdef STEED
+			    if (u.usteed)
+				You_cant("lead %s through that closed door.",
+				      x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE));
+		 	    else {
+#else
+			        pline("Ouch!  You bump into a door.");
+			        exercise(A_DEX, FALSE);
+#endif
+#ifdef STEED
+			    }
+#endif
+			} else pline("That door is closed.");
+		    }
+		}
+		return FALSE;
+	    }
+	} else if (dx && dy && !Passes_walls
+		    && ((tmpr->doormask & ~D_BROKEN)
+#ifdef REINCARNATION
+				    || Is_rogue_level(&u.uz)
+#endif
+				    || block_door(x,y))) {
+	    /* Diagonal moves into a door are not allowed. */
+	    if ( Blind && !test_only )
+		feel_location(x,y);
+	    return FALSE;
+	}
+    }
+    if (dx && dy
+	    && bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) {
+	/* Move at a diagonal. */
+	if (In_sokoban(&u.uz)) {
+	    if ( !test_only )
+		You("cannot pass that way.");
+	    return FALSE;
+	}
+	if (bigmonst(youmonst.data)) {
+	    if ( !test_only )
+		Your("body is too large to fit through.");
+	    return FALSE;
+	}
+	if (invent && (inv_weight() + weight_cap() > 600)) {
+	    if ( !test_only )
+		You("are carrying too much to get through.");
+	    return FALSE;
+	}
+    }
+
+    ust = &levl[ux][uy];
+
+    /* Now see if other things block our way . . */
+    if (dx && dy && !Passes_walls
+		     && (IS_DOOR(ust->typ) && ((ust->doormask & ~D_BROKEN)
+#ifdef REINCARNATION
+			     || Is_rogue_level(&u.uz)
+#endif
+			     || block_entry(x, y))
+			 )) {
+	/* Can't move at a diagonal out of a doorway with door. */
+	return FALSE;
+    }
+
+    if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) {
+	if (!(Blind || Hallucination) && (flags.run >= 2))
+	    return FALSE;
+	if (!test_only) {
+	    /* tunneling monsters will chew before pushing */
+	    if (tunnels(youmonst.data) && !needspick(youmonst.data) &&
+		!In_sokoban(&u.uz)) {
+		if (still_chewing(x,y)) return FALSE;
+	    } else
+		if (moverock() < 0) return FALSE;
+	}
+	/* test_only will assume you'll be able to push it when you get there... */
+    }
+
+    /* OK, it is a legal place to move. */
+    return TRUE;
+}
+
+static void findtravelpath()
+{
+    if ( u.tx != u.ux || u.ty != u.uy ) {
+	xchar travel[COLNO][ROWNO];
+	xchar travelstepx[2][COLNO*ROWNO];
+	xchar travelstepy[2][COLNO*ROWNO];
+	int n=1;
+	int set=0;
+	int dia=1;
+
+	(void) memset((genericptr_t)travel,0,sizeof(travel));
+
+	travelstepx[0][0] = u.tx;
+	travelstepy[0][0] = u.ty;
+	while ( n ) {
+	    int i;
+	    int nn=0;
+	    for (i=0; i<n; i++) {
+		int dir;
+		int x = travelstepx[set][i];
+		int y = travelstepy[set][i];
+		static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 };
+		for (dir=0; dir<8; dir++) {
+		    int nx = x+xdir[ordered[dir]];
+		    int ny = y+ydir[ordered[dir]];
+    /*printf("try %d,%d\n",nx,ny);*/
+		    if ( isok(nx,ny) && test_move( x, y, nx-x, ny-y, 1 ) ) {
+			if ( nx == u.ux && ny == u.uy ) {
+			    u.dx = x-u.ux;
+			    u.dy = y-u.uy;
+	/*printf("found\n");*/
+	/*printf("findtravelpath %d,%d -> %d,%d by %d,%d\n",u.ux,u.uy,u.tx,u.ty,u.dx,u.dy);
+	*/
+			    return;
+			} else {
+	/*printf("%d %d %d",isok(nx,ny), !travel[nx][ny], ACCESSIBLE(levl[nx][ny].typ));*/
+			    if ( !travel[nx][ny] ) {
+				travelstepx[1-set][nn]=nx;
+				travelstepy[1-set][nn]=ny;
+				travel[nx][ny]=dia;
+				nn++;
+			    }
+			}
+		    }
+		}
+	    }
+	    n = nn;
+	    set = 1-set;
+	    dia++;
+	}
+
+	/* give up */
+    }
+
+    u.dx = 0;
+    u.dy = 0;
+    nomul(0);
+}
+
 void
 domove()
 {
 	register struct monst *mtmp;
-	register struct rm *tmpr,*ust;
+	register struct rm *tmpr;
 	register xchar x,y;
 	struct trap *trap;
 	int wtcap;
@@ -526,6 +734,9 @@
 
 	u_wipe_engr(rnd(5));
 
+	if ( flags.travel )
+	    findtravelpath();
+
 	if(((wtcap = near_capacity()) >= OVERLOADED
 	    || (wtcap > SLT_ENCUMBER &&
 		(Upolyd ? (u.mh < 5 && u.mh != u.mhmax)
@@ -636,9 +847,12 @@
 			 * otherwise:
 			 *	 7.5% chance of getting away.
 			 * [strength ought to be a factor]
+			 * If holder is tame and there is no conflict,
+			 * guaranteed escape.
 			 */
 			switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) {
 			case 0: case 1: case 2:
+			pull_free:
 			    You("pull free from %s.", mon_nam(u.ustuck));
 			    u.ustuck = 0;
 			    break;
@@ -650,6 +864,9 @@
 			    }
 			    /*FALLTHRU*/
 			default:
+			    if (u.ustuck->mtame &&
+				!Conflict && !u.ustuck->mconf)
+				goto pull_free;
 			    You("cannot escape from %s!", mon_nam(u.ustuck));
 			    nomul(0);
 			    return;
@@ -702,7 +919,7 @@
 		if(mtmp->m_ap_type && !Protection_from_shape_changers
 						    && !sensemon(mtmp))
 		    stumble_onto_mimic(mtmp);
-		else if (mtmp->mpeaceful)
+		else if (mtmp->mpeaceful && !Hallucination)
 		    pline("Pardon me, %s.", m_monnam(mtmp));
 		else
 		    You("move right into %s.", mon_nam(mtmp));
@@ -735,7 +952,10 @@
 	if (flags.forcefight ||
 	    /* remembered an 'I' && didn't use a move command */
 	    (glyph_is_invisible(levl[x][y].glyph) && !flags.nopick)) {
-		You("attack %s.", Underwater ? "empty water" : "thin air");
+	    	char buf[BUFSZ];
+		Sprintf(buf,"a vacant spot on the %s", surface(x,y));
+		You("attack %s.",
+		    !Underwater ? "thin air" : is_pool(x,y) ? "empty water" : buf);
 		unmap_object(x, y); /* known empty -- remove 'I' if present */
 		newsym(x, y);
 		nomul(0);
@@ -748,7 +968,10 @@
 	/* not attacking an animal, so we try to move */
 #ifdef STEED
 	if (u.usteed && !u.usteed->mcanmove && (u.dx || u.dy)) {
-		pline("%s won't move!", Monnam(u.usteed));
+		pline("%s won't move!",
+			upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)));
 		nomul(0);
 		return;
 	} else
@@ -770,20 +993,52 @@
 		    } else if (!(--u.utrap)) {
 			You("%s to the edge of the pit.",
 				(In_sokoban(&u.uz) && Levitation) ?
-				"struggle against the air currents and float" : "crawl");
+				"struggle against the air currents and float" :
+#ifdef STEED
+				u.usteed ? "ride" :
+#endif
+				"crawl");
 			fill_pit(u.ux, u.uy);
 			vision_full_recalc = 1;	/* vision limits change */
-		    } else if (flags.verbose)
+		    } else if (flags.verbose) {
+#ifdef STEED
+			if (u.usteed)
+			    	Norep("%s is still in a pit.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)));
+			else
+#endif
 			Norep( (Hallucination && !rn2(5)) ?
 				"You've fallen, and you can't get up." :
 				"You are still in a pit." );
+		    }
 		} else if (u.utraptype == TT_LAVA) {
-		    if(flags.verbose)
-			Norep("You are stuck in the lava.");
+		    if(flags.verbose) {
+			char *predicament = "stuck in the lava";
+#ifdef STEED
+			if (u.usteed)
+			    	Norep("%s is %s.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)),
+					 predicament);
+			else
+#endif
+			Norep("You are %s.", predicament);
+		    }
 		    if(!is_lava(x,y)) {
 			u.utrap--;
 			if((u.utrap & 0xff) == 0) {
-			    You("pull yourself to the edge of the lava.");
+#ifdef STEED
+			    if (u.usteed)
+				You("lead %s to the edge of the lava.",
+				      x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE));
+			    else
+#endif
+			     You("pull yourself to the edge of the lava.");
 			    u.utrap = 0;
 			}
 		    }
@@ -795,128 +1050,81 @@
 			return;
 		    }
 		    if(--u.utrap) {
-			if(flags.verbose)
-			    Norep("You are stuck to the web.");
-		    } else You("disentangle yourself.");
+			if(flags.verbose) {
+			    char *predicament = "stuck to the web";
+#ifdef STEED
+			    if (u.usteed)
+			    	Norep("%s is %s.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)),
+					 predicament);
+			    else
+#endif
+			    Norep("You are %s.", predicament);
+			}
+		    } else {
+#ifdef STEED
+			if (u.usteed)
+				pline("%s breaks out of the web.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)));
+			else
+#endif
+			You("disentangle yourself.");
+		    }
 		} else if (u.utraptype == TT_INFLOOR) {
 		    if(--u.utrap) {
-			if(flags.verbose)
-			    Norep("You are stuck in the %s.",
-					surface(u.ux, u.uy));
-		    } else You("finally wiggle free.");
-		} else {
-		    if(flags.verbose)
-			Norep("You are caught in a bear trap.");
-		    if((u.dx && u.dy) || !rn2(5)) u.utrap--;
-		}
-		return;
-	}
-
-
-	/*
-	 *  Check for physical obstacles.  First, the place we are going.
-	 */
-	if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
-	    if (Blind) feel_location(x,y);
-	    if (Passes_walls && may_passwall(x,y)) {
-		;	/* do nothing */
-	    } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
-		/* Eat the rock. */
-		if (still_chewing(x,y)) return;
-	    } else {
-		if (Is_stronghold(&u.uz) && is_db_wall(x,y))
-		    pline_The("drawbridge is up!");
-		flags.move = 0;
-		nomul(0);
-		return;
-	    }
-	} else if (IS_DOOR(tmpr->typ)) {
-	    if (closed_door(x,y)) {
-		if (Blind) feel_location(x,y);
-		if (Passes_walls)
-		    ;	/* do nothing */
-		else if (can_ooze(&youmonst))
-		    You("ooze under the door.");
-		else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
-		    /* Eat the door. */
-		    if (still_chewing(x,y)) return;
+			if(flags.verbose) {
+			    char *predicament = "stuck in the";
+#ifdef STEED
+			    if (u.usteed)
+			    	Norep("%s is %s %s.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)),
+					 predicament, surface(u.ux, u.uy));
+			    else
+#endif
+			    Norep("You are %s %s.", predicament, surface(u.ux, u.uy));
+			}
+		    } else {
+#ifdef STEED
+			if (u.usteed)
+				pline("%s finally wiggles free.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)));
+			else
+#endif
+			You("finally wiggle free.");
+		    }
 		} else {
-		    flags.move = 0;
-		    if (amorphous(youmonst.data))
-			You("try to ooze under the door, but can't squeeze your possessions through.");
-		    else if (x == u.ux || y == u.uy) {
-			if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) {
-			    pline("Ouch!  You bump into a door.");
-			    exercise(A_DEX, FALSE);
-			} else pline("That door is closed.");
+		    if(flags.verbose) {
+			char *predicament = "caught in a bear trap";
+#ifdef STEED
+			if (u.usteed)
+			    	Norep("%s is %s.",
+				      upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE)),
+					 predicament);
+			else
+#endif
+			Norep("You are %s.", predicament);
 		    }
-		    nomul(0);
-		    return;
+		    if((u.dx && u.dy) || !rn2(5)) u.utrap--;
 		}
-	    } else if (u.dx && u.dy && !Passes_walls
-			&& ((tmpr->doormask & ~D_BROKEN)
-#ifdef REINCARNATION
-					|| Is_rogue_level(&u.uz)
-#endif
-					|| block_door(x,y))) {
-		/* Diagonal moves into a door are not allowed. */
-		if (Blind) feel_location(x,y);	/* ?? */
-		flags.move = 0;
-		nomul(0);
-		return;
-	    }
-	}
-	if (u.dx && u.dy
-		&& bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) {
-	    /* Move at a diagonal. */
-	    if (In_sokoban(&u.uz)) {
-	    	You("cannot pass that way.");
-	    	nomul(0);
-	    	return;
-	    }
-	    if (bigmonst(youmonst.data)) {
-		Your("body is too large to fit through.");
-		nomul(0);
-		return;
-	    }
-	    if (invent && (inv_weight() + weight_cap() > 600)) {
-		You("are carrying too much to get through.");
-		nomul(0);
 		return;
-	    }
 	}
 
-	ust = &levl[u.ux][u.uy];
-
-	/* Now see if other things block our way . . */
-	if (u.dx && u.dy && !Passes_walls
-			 && (IS_DOOR(ust->typ) && ((ust->doormask & ~D_BROKEN)
-#ifdef REINCARNATION
-				 || Is_rogue_level(&u.uz)
-#endif
-				 || block_entry(x, y))
-			     )) {
-	    /* Can't move at a diagonal out of a doorway with door. */
+	if ( !test_move( u.ux, u.uy, x-u.ux, y-u.uy, 0 ) ) {
 	    flags.move = 0;
 	    nomul(0);
 	    return;
 	}
 
-	if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) {
-	    if (!(Blind || Hallucination) && (flags.run >= 2)) {
-		nomul(0);
-		flags.move = 0;
-		return;
-	    }
-	    /* tunneling monsters will chew before pushing */
-	    if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
-		if (still_chewing(x,y)) return;
-	    } else
-		if (moverock() < 0) return;
-	}
-
-	/* OK, it is a legal place to move. */
-
 	/* Move ball and chain.  */
 	if (Punished)
 	    if (!drag_ball(x,y, &bc_control, &ballx, &bally, &chainx, &chainy,
@@ -958,6 +1166,7 @@
 	    if (mtmp->mtrapped) {
 		if (!rn2(mtmp->mtame)) {
 		    mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0;
+		    if (mtmp->mleashed) m_unleash(mtmp, TRUE);
 		    growl(mtmp);
 		} else {
 		    yelp(mtmp);
@@ -974,15 +1183,19 @@
 		/* can't swap places with pet pinned in a pit by a boulder */
 		u.ux = u.ux0,  u.uy = u.uy0;	/* didn't move after all */
 	    } else {
+		char pnambuf[BUFSZ];
+
+		/* save its current description in case of polymorph */
+		Strcpy(pnambuf, y_monnam(mtmp));
 		mtmp->mtrapped = 0;
 		remove_monster(x, y);
 		place_monster(mtmp, u.ux0, u.uy0);
 
 		/* check for displacing it into pools and traps */
-		switch (minwater(mtmp) ? 2 : mintrap(mtmp)) {
+		switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) {
 		case 0:
 		    You("%s %s.", mtmp->mtame ? "displaced" : "frightened",
-			y_monnam(mtmp));
+			pnambuf);
 		    break;
 		case 1:		/* trapped */
 		case 3:		/* changed levels */
@@ -999,6 +1212,11 @@
 			u.ugangr++;
 			adjalign(-15);
 		    }
+
+		    /* you killed your pet by direct action.
+		     * minliquid and mintrap don't know to do this
+		     */
+		    u.uconduct.killer++;
 		    break;
 		default:
 		    pline("that's strange, unknown mintrap result!");
@@ -1009,6 +1227,7 @@
 
 	reset_occupations();
 	if (flags.run) {
+	    if ( flags.run < 8 )
 		if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) ||
 			IS_FURNITURE(tmpr->typ))
 		    nomul(0);
@@ -1111,10 +1330,23 @@
 stillinwater:;
 	if (!Levitation && !u.ustuck && !Flying) {
 	    /* limit recursive calls through teleds() */
-	    if(is_lava(u.ux,u.uy) && lava_effects())
-		    return;
-	    if(is_pool(u.ux,u.uy) && !Wwalking && drown())
+	    if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) {
+#ifdef STEED
+		if (u.usteed && !is_flyer(u.usteed->data) &&
+			!is_floater(u.usteed->data) &&
+			!is_clinger(u.usteed->data)) {
+		    dismount_steed(Underwater ?
+			    DISMOUNT_FELL : DISMOUNT_GENERIC);
+		    /* dismount_steed() -> float_down() -> pickup() */
+		    if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz))
+			pick = FALSE;
+		} else
+#endif
+		if (is_lava(u.ux, u.uy)) {
+		    if (lava_effects()) return;
+		} else if (!Wwalking && drown())
 		    return;
+	    }
 	}
 	check_special_room(FALSE);
 #ifdef SINKS
@@ -1123,8 +1355,9 @@
 #endif
 	if (pick && !in_steed_dismounting)
 		(void) pickup(1);
-	if ((trap = t_at(u.ux,u.uy)) != 0)
-		dotrap(trap);	/* fall into pit, arrow trap, etc. */
+	/* if dismounting, we'll check again later */
+	if ((trap = t_at(u.ux,u.uy)) != 0 && !in_steed_dismounting)
+		dotrap(trap, 0);	/* fall into pit, arrow trap, etc. */
 	if((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) {
 		mtmp->mundetected = mtmp->msleeping = 0;
 		switch(mtmp->data->mlet) {
@@ -1416,6 +1649,7 @@
 	multi = 0;	/* always reset */
 	/* uswallow case added by GAN 01/29/87 */
 	if(u.uswallow) {
+	    if (!u.ustuck->minvent) {
 		if (is_animal(u.ustuck->data)) {
 		    You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck)));
 		    pline("But it's kind of slimy, so you drop it.");
@@ -1423,6 +1657,10 @@
 		    You("don't %s anything in here to pick up.",
 			  Blind ? "feel" : "see");
 		return(1);
+	    } else {
+	    	int tmpcount = -count;
+		return loot_mon(u.ustuck, &tmpcount, (boolean *)0);
+	    }
 	}
 	if(is_pool(u.ux, u.uy)) {
 	    if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data)
@@ -1476,16 +1714,16 @@
     register struct monst *mtmp;
     register struct trap *trap;
 
-	/* Grid bugs stop if trying to move diagonal, even if blind.  Maybe */
-	/* they polymorphed while in the middle of a long move. */
-	if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) {
-		nomul(0);
-		return;
-	}
+    /* Grid bugs stop if trying to move diagonal, even if blind.  Maybe */
+    /* they polymorphed while in the middle of a long move. */
+    if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) {
+	nomul(0);
+	return;
+    }
 
-	if(Blind || flags.run == 0) return;
-	for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++) {
-		if(!isok(x,y)) continue;
+    if(Blind || flags.run == 0) return;
+    for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++) {
+	if(!isok(x,y)) continue;
 
 	if(u.umonnum == PM_GRID_BUG && x != u.ux && y != u.uy) continue;
 
@@ -1513,7 +1751,7 @@
 	} else if (levl[x][y].typ == CORR) {
 bcorr:
 	    if(levl[u.ux][u.uy].typ != ROOM) {
-		if(flags.run == 1 || flags.run == 3) {
+		if(flags.run == 1 || flags.run == 3 || flags.run == 8) {
 		    i = dist2(x,y,u.ux+u.dx,u.uy+u.dy);
 		    if(i > 2) continue;
 		    if(corrct == 1 && dist2(x,y,x0,y0) != 1)
@@ -1546,6 +1784,7 @@
 	    continue;
 	} else {		/* e.g. objects or trap or stairs */
 	    if(flags.run == 1) goto bcorr;
+	    if(flags.run == 8) continue;
 	    if(mtmp) continue;		/* d */
 	    if(((x == u.ux - u.dx) && (y != u.uy + u.dy)) ||
 	       ((y == u.uy - u.dy) && (x != u.ux + u.dx)))
@@ -1557,8 +1796,9 @@
     } /* end for loops */
 
     if(corrct > 1 && flags.run == 2) goto stop;
-    if((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
-				(corrct == 1 || (corrct == 2 && i0 == 1))) {
+    if((flags.run == 1 || flags.run == 3 || flags.run == 8) &&
+	!noturn && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1)))
+    {
 	/* make sure that we do not turn too far */
 	if(i0 == 2) {
 	    if(u.dx == y0-u.uy && u.dy == u.ux-x0)
@@ -1621,7 +1861,7 @@
 	u.uinvulnerable = FALSE;	/* Kludge to avoid ctrl-C bug -dlc */
 	u.usleep = 0;
 	multi = nval;
-	flags.mv = flags.run = 0;
+	flags.travel = flags.mv = flags.run = 0;
 }
 
 /* called when a non-movement, multi-turn action has completed */
@@ -1641,7 +1881,7 @@
 #endif /* OVL2 */
 #ifdef OVL1
 
-static void
+STATIC_OVL void
 maybe_wail()
 {
     static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES,
@@ -1745,7 +1985,7 @@
 inv_weight()
 {
 	register struct obj *otmp = invent;
-	register int wt;
+	register int wt = 0;
 
 	/* when putting stuff into containers, gold is inserted at the head
 	   of invent for easier manipulation by askchain & co, but it's also
@@ -1753,7 +1993,6 @@
 	   mustn't add its weight in twice under that circumstance */
 	wt = (otmp && otmp->oclass == GOLD_CLASS) ? 0 :
 		(int)((u.ugold + 50L) / 100L);
-
 	while (otmp) {
 		if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data))
 			wt += otmp->owt;
diff -Naurd ../nethack-3.3.1/src/hacklib.c ./src/hacklib.c
--- ../nethack-3.3.1/src/hacklib.c Tue Jul 18 02:49:58 2000
+++ ./src/hacklib.c Fri Mar 22 14:40:55 2002
@@ -15,6 +15,7 @@
 	char		highc		(char)
 	char		lowc		(char)
 	char *		lcase		(char *)
+	char *		upstart		(char *)
 	char *		mungspaces	(char *)
 	char *		eos		(char *)
 	char *		s_suffix	(const char *)
@@ -92,6 +93,14 @@
     return s;
 }
 
+char *
+upstart(s)		/* convert first character of a string to uppercase */
+    char *s;
+{
+    if (s) *s = highc(*s);
+    return s;
+}
+
 /* remove excess whitespace from a string buffer (in place) */
 char *
 mungspaces(bp)
diff -Naurd ../nethack-3.3.1/src/invent.c ./src/invent.c
--- ../nethack-3.3.1/src/invent.c Sat Aug 5 00:37:26 2000
+++ ./src/invent.c Fri Mar 22 14:41:02 2002
@@ -16,13 +16,17 @@
 STATIC_DCL boolean FDECL(only_here, (struct obj *));
 #endif /* OVL1 */
 STATIC_DCL void FDECL(compactify,(char *));
+STATIC_DCL boolean FDECL(taking_off, (const char *));
+STATIC_DCL boolean FDECL(putting_on, (const char *));
 STATIC_PTR int FDECL(ckunpaid,(struct obj *));
+STATIC_PTR int FDECL(ckvalidcat,(struct obj *));
+static char FDECL(display_pickinv, (const char *,BOOLEAN_P, long *));
 #ifdef OVLB
 STATIC_DCL boolean FDECL(this_type_only, (struct obj *));
 STATIC_DCL void NDECL(dounpaid);
 STATIC_DCL struct obj *FDECL(find_unpaid,(struct obj *,struct obj **));
 STATIC_DCL void FDECL(menu_identify, (int));
-static boolean FDECL(tool_in_use, (struct obj *));
+STATIC_DCL boolean FDECL(tool_in_use, (struct obj *));
 #endif /* OVLB */
 STATIC_DCL char FDECL(obj_to_let,(struct obj *));
 
@@ -49,6 +53,7 @@
 	register int i;
 	register struct obj *obj;
 
+
 	for(i = 0; i < 52; i++) inuse[i] = FALSE;
 	for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) {
 		i = obj->invlet;
@@ -164,7 +169,8 @@
 			    / (otmp->quan + obj->quan);
 
 		otmp->quan += obj->quan;
-		otmp->owt += obj->owt;
+		if (otmp->oclass == GOLD_CLASS) otmp->owt = weight(otmp);
+		else otmp->owt += obj->owt;
 		if(!otmp->onamelth && obj->onamelth)
 			otmp = *potmp = oname(otmp, ONAME(obj));
 		obj_extract_self(obj);
@@ -174,21 +180,40 @@
 		if (obj->timed) obj_stop_timers(obj);	/* follows lights */
 
 		/* fixup for `#adjust' merging wielded darts, daggers, &c */
-		if (obj->owornmask) {
-			otmp->owornmask |= obj->owornmask;
-			/* (it isn't necessary to "unwear" `obj' first) */
-			if (carried(otmp))
-			    setworn(otmp, otmp->owornmask);
+		if (obj->owornmask && carried(otmp)) {
+		    long wmask = otmp->owornmask | obj->owornmask;
+
+		    /* Both the items might be worn in competing slots;
+		       merger preference (regardless of which is which):
+			 primary weapon + alternate weapon -> primary weapon;
+			 primary weapon + quiver -> primary weapon;
+			 alternate weapon + quiver -> alternate weapon.
+		       (Prior to 3.3.0, it was not possible for the two
+		       stacks to be worn in different slots and `obj'
+		       didn't need to be unworn when merging.) */
+		    if (wmask & W_WEP) wmask = W_WEP;
+		    else if (wmask & W_SWAPWEP) wmask = W_SWAPWEP;
+		    else if (wmask & W_QUIVER) wmask = W_QUIVER;
+		    else {
+			impossible("merging strangely worn items (%lx)", wmask);
+			wmask = otmp->owornmask;
+		    }
+		    if ((otmp->owornmask & ~wmask) != 0L) setnotworn(otmp);
+		    setworn(otmp, wmask);
+		    setnotworn(obj);
+		}
 #if 0
-			/* (this should never be necessary, since items
-			    already in a monster's inventory don't ever get
-			    merged into other objects [only vice versa]) */
-			else if (mcarried(otmp)) {
-			    if (obj == MON_WEP(otmp->ocarry))
-				MON_WEP(otmp->ocarry) = otmp;
-			}
-#endif
+		/* (this should not be necessary, since items
+		    already in a monster's inventory don't ever get
+		    merged into other objects [only vice versa]) */
+		else if (obj->owornmask && mcarried(otmp)) {
+		    if (obj == MON_WEP(otmp->ocarry)) {
+			MON_WEP(otmp->ocarry) = otmp;
+			otmp->owornmask = W_WEP;
+		    }
 		}
+#endif /*0*/
+
 		obfree(obj,otmp);	/* free(obj), bill->otmp */
 		return(1);
 	}
@@ -212,7 +237,6 @@
 {
 	if (obj->oclass == GOLD_CLASS) {
 		u.ugold += obj->quan;
-		flags.botl = 1;
 	} else if (obj->otyp == AMULET_OF_YENDOR) {
 		if (u.uhave.amulet) impossible("already have amulet?");
 		u.uhave.amulet = 1;
@@ -269,6 +293,7 @@
 
 	if (obj->where != OBJ_FREE)
 	    panic("addinv: obj not free");
+	obj->no_charge = 0;	/* not meaningful for invent */
 
 	addinv_core1(obj);
 	/* if handed gold, we're done */
@@ -360,10 +385,7 @@
 			if (drop_fmt) pline(drop_fmt, drop_arg);
 			/* undo any merge which took place */
 			if (obj->quan > oquan) {
-			    struct obj *otmp = splitobj(obj, oquan);
-			    /* might have merged with weapon */
-			    if (obj->owornmask)
-				setworn(otmp, obj->owornmask);
+			    obj = splitobj(obj, oquan);
 			}
 			dropx(obj);
 		} else {
@@ -537,6 +559,14 @@
 	return((struct obj *) 0);
 }
 
+const char *
+currency(amount)
+long amount;
+{
+	if (amount == 1L) return "zorkmid";
+	else return "zorkmids";
+}
+
 boolean
 have_lizard()
 {
@@ -593,7 +623,6 @@
 
 #endif /* OVL2 */
 #ifdef OVLB
-
 /* Make a gold object from the hero's gold. */
 struct obj *
 mkgoldobj(q)
@@ -608,7 +637,6 @@
 	flags.botl = 1;
 	return(otmp);
 }
-
 #endif /* OVLB */
 #ifdef OVL1
 
@@ -642,11 +670,31 @@
 	}
 }
 
+/* match the prompt for either 'T' or 'R' command */
+STATIC_OVL boolean
+taking_off(action)
+const char *action;
+{
+    return !strcmp(action, "take off") || !strcmp(action, "remove");
+}
+
+/* match the prompt for either 'W' or 'P' command */
+STATIC_OVL boolean
+putting_on(action)
+const char *action;
+{
+    return !strcmp(action, "wear") || !strcmp(action, "put on");
+}
+
 /*
  * getobj returns:
  *	struct obj *xxx:	object to do something with.
  *	(struct obj *) 0	error return: no object.
  *	&zeroobj		explicitly no object (as in w-).
+#ifdef GOLDOBJ
+!!!! test if gold can be used in unusual ways (eaten etc.)
+!!!! may be able to remove "usegold"
+#endif
  */
 struct obj *
 getobj(let,word)
@@ -659,10 +707,8 @@
 	register int foo = 0;
 	register char *bp = buf;
 	xchar allowcnt = 0;	/* 0, 1 or 2 */
-	boolean allowgold = FALSE, usegold = FALSE;
-		/* Two possibilities: they can't use gold because it's illegal,
-		 * or they can't use gold because they don't have any.
-		 */
+	boolean allowgold = FALSE;	/* can't use gold because they don't have any */
+	boolean usegold = FALSE;	/* can't use gold because its illegal */
 	boolean allowall = FALSE;
 	boolean allownone = FALSE;
 	xchar foox = 0;
@@ -695,20 +741,22 @@
 
 	ilet = 'a';
 	for (otmp = invent; otmp; otmp = otmp->nobj) {
-	    if (!flags.invlet_constant) otmp->invlet = ilet;	/* reassign() */
-	    if (!*let || index(let, otmp->oclass)) {
+	    if (!flags.invlet_constant)
+		otmp->invlet = ilet;	/* reassign() */
+	    if (!*let || index(let, otmp->oclass)
+		) {
 		register int otyp = otmp->otyp;
 		bp[foo++] = otmp->invlet;
 
 		/* ugly check: remove inappropriate things */
-		if((!strcmp(word, "take off") &&
+		if ((taking_off(word) &&
 		    (!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))
 		     || (otmp==uarm && uarmc)
 #ifdef TOURIST
 		     || (otmp==uarmu && (uarm || uarmc))
 #endif
 		    ))
-		|| (!strcmp(word, "wear") &&
+		|| (putting_on(word) &&
 		     (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
 							/* already worn */
 		|| (!strcmp(word, "wield") &&
@@ -723,7 +771,7 @@
 		/* Second ugly check; unlike the first it won't trigger an
 		 * "else" in "you don't have anything else to ___".
 		 */
-		else if ((!strcmp(word, "wear") &&
+		else if ((putting_on(word) &&
 		    ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING) ||
 		    (otmp->oclass == TOOL_CLASS &&
 		     otyp != BLINDFOLD && otyp != TOWEL && otyp != LENSES)))
@@ -739,9 +787,10 @@
 		|| (!strcmp(word, "tin") &&
 		    (otyp != CORPSE || !tinnable(otmp)))
 		|| (!strcmp(word, "rub") &&
-		    (otmp->oclass == TOOL_CLASS &&
-		     otyp != OIL_LAMP && otyp != MAGIC_LAMP &&
-		     otyp != BRASS_LANTERN))
+		    ((otmp->oclass == TOOL_CLASS &&
+		      otyp != OIL_LAMP && otyp != MAGIC_LAMP &&
+		      otyp != BRASS_LANTERN) ||
+		     (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
 		|| ((!strcmp(word, "use or apply") ||
 			!strcmp(word, "untrap with")) &&
 		     /* Picks, axes, pole-weapons, bullwhips */
@@ -751,7 +800,8 @@
 		     /* only applicable potion is oil, and it will only
 			be offered as a choice when already discovered */
 		     (otyp != POT_OIL || !otmp->dknown ||
-		      !objects[POT_OIL].oc_name_known))))
+		      !objects[POT_OIL].oc_name_known))
+		|| (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
 		|| (!strcmp(word, "invoke") &&
 		    (!otmp->oartifact && !objects[otyp].oc_unique &&
 		     (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) &&
@@ -767,7 +817,7 @@
 		    )
 			foo--;
 		/* ugly check for unworn armor that can't be worn */
-		else if (!strcmp(word, "wear") && *let == ARMOR_CLASS &&
+		else if (putting_on(word) && *let == ARMOR_CLASS &&
 			 !canwearobj(otmp, &dummymask, FALSE)) {
 			foo--;
 			allowall = TRUE;
@@ -836,13 +886,13 @@
 			return(allownone ? &zeroobj : (struct obj *) 0);
 		}
 		if(ilet == def_oc_syms[GOLD_CLASS]) {
-			if(!usegold){
-				You("cannot %s gold.", word);
-				return(struct obj *)0;
+			if (!usegold) {
+			    You("cannot %s gold.", word);
+			    return(struct obj *)0;
 			} else if (!allowgold) {
 				You("are not carrying any gold.");
 				return(struct obj *)0;
-			}
+			} 
 			if(cnt == 0 && prezero) return((struct obj *)0);
 			/* Historic note: early Nethack had a bug which was
 			 * first reported for Larn, where trying to drop 2^32-n
@@ -859,24 +909,20 @@
 				cnt = u.ugold;
 			return(mkgoldobj(cnt));
 		}
-		if(allowcnt == 2 && !strcmp(word,"throw")) {
-			/* permit counts for throwing gold, but don't accept
-			 * counts for other things since the throw code will
-			 * split off a single item anyway */
-			allowcnt = 1;
-			if(cnt == 0 && prezero) return((struct obj *)0);
-			if(cnt > 1) {
-			    You("can only throw one item at a time.");
-			    continue;
-			}
-		}
 		if(ilet == '?' || ilet == '*') {
 		    char *allowed_choices = (ilet == '?') ? lets : (char *)0;
+		    long ctmp = 0;
 
 		    if (ilet == '?' && !*lets && *altlets)
 			allowed_choices = altlets;
-		    ilet = display_inventory(allowed_choices, TRUE);
+		    ilet = display_pickinv(allowed_choices, TRUE,
+					   allowcnt ? &ctmp : (long *)0);
 		    if(!ilet) continue;
+		    if (allowcnt && ctmp >= 0) {
+			cnt = ctmp;
+			if (!cnt) prezero = TRUE;
+			allowcnt = 2;
+		    }
 		    if(ilet == '\033') {
 			if(flags.verbose)
 			    pline(Never_mind);
@@ -884,6 +930,17 @@
 		    }
 		    /* they typed a letter (not a space) at the prompt */
 		}
+		if(allowcnt == 2 && !strcmp(word,"throw")) {
+		    /* permit counts for throwing gold, but don't accept
+		     * counts for other things since the throw code will
+		     * split off a single item anyway */
+			allowcnt = 1;
+		    if(cnt == 0 && prezero) return((struct obj *)0);
+		    if(cnt > 1) {
+			You("can only throw one item at a time.");
+			continue;
+		    }
+		}
 #ifdef REDO
 		savech(ilet);
 #endif
@@ -905,25 +962,23 @@
 		}
 		break;
 	}
-	if(!allowall && let && !index(let,otmp->oclass)) {
+	if(!allowall && let && !index(let,otmp->oclass)
+	   ) {
 		pline(silly_thing_to, word);
 		return((struct obj *)0);
 	}
 	if(allowcnt == 2) {	/* cnt given */
-		if(cnt == 0) return (struct obj *)0;
-		if(cnt != otmp->quan) {
-			register struct obj *obj = splitobj(otmp, cnt);
+	    if(cnt == 0) return (struct obj *)0;
+	    if(cnt != otmp->quan) {
+		otmp = splitobj(otmp, cnt);
 		/* Very ugly kludge necessary to prevent someone from trying
 		 * to drop one of several loadstones and having the loadstone
 		 * now be separate.
 		 */
-			if (!strcmp(word, "drop") &&
-			    obj->otyp == LOADSTONE && obj->cursed)
-				otmp->corpsenm = obj->invlet;
-			if(otmp == uwep) setuwep(obj);
-			else if (otmp == uquiver) setuqwep(obj);
-			if (otmp == uswapwep) setuswapwep(obj);
-		}
+		if (!strcmp(word, "drop") &&
+		    otmp->otyp == LOADSTONE && otmp->cursed)
+		    otmp->corpsenm = otmp->invlet;
+	    }
 	}
 	return(otmp);
 }
@@ -932,6 +987,14 @@
 #ifdef OVLB
 
 STATIC_PTR int
+ckvalidcat(otmp)
+register struct obj *otmp;
+{
+	/* use allow_category() from pickup.c */
+	return((int)allow_category(otmp));
+}
+
+STATIC_PTR int
 ckunpaid(otmp)
 register struct obj *otmp;
 {
@@ -966,10 +1029,11 @@
 /* Takeoff (A). Return the number of times fn was called successfully */
 /* If combo is TRUE, we just use this to get a category list */
 int
-ggetobj(word, fn, mx, combo)
+ggetobj(word, fn, mx, combo, resultflags)
 const char *word;
 int FDECL((*fn),(OBJ_P)), mx;
 boolean combo;		/* combination menu flag */
+unsigned *resultflags;
 {
 	int FDECL((*ckfn),(OBJ_P)) = (int FDECL((*),(OBJ_P))) 0;
 	boolean FDECL((*filter),(OBJ_P)) = (boolean FDECL((*),(OBJ_P))) 0;
@@ -978,14 +1042,15 @@
 	char sym, *ip, olets[MAXOCLASSES+5], ilets[MAXOCLASSES+5];
 	char buf[BUFSZ], qbuf[QBUFSZ];
 
+	if (resultflags) *resultflags = 0;
 	allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0;
 	takeoff = ident = allflag = m_seen = FALSE;
 	if(!invent && !allowgold){
 		You("have nothing to %s.", word);
 		return(0);
 	}
-	if (combo) add_valid_menu_class(0);	/* reset */
-	if (!strcmp(word, "take off")) {
+	add_valid_menu_class(0);	/* reset */
+	if (taking_off(word)) {
 	    takeoff = TRUE;
 	    filter = is_worn;
 	} else if (!strcmp(word, "identify")) {
@@ -994,7 +1059,9 @@
 	}
 
 	iletct = collect_obj_classes(ilets, invent,
-				     FALSE, (allowgold != 0), filter);
+				     	FALSE,
+					(allowgold != 0),
+					filter);
 	unpaid = count_unpaid(invent);
 
 	if (ident && !iletct) {
@@ -1002,6 +1069,10 @@
 	} else if (!takeoff && (unpaid || invent)) {
 	    ilets[iletct++] = ' ';
 	    if (unpaid) ilets[iletct++] = 'u';
+	    if (count_buc(invent, BUC_BLESSED))  ilets[iletct++] = 'B';
+	    if (count_buc(invent, BUC_UNCURSED)) ilets[iletct++] = 'U';
+	    if (count_buc(invent, BUC_CURSED))   ilets[iletct++] = 'C';
+	    if (count_buc(invent, BUC_UNKNOWN))  ilets[iletct++] = 'X';
 	    if (invent) ilets[iletct++] = 'a';
 	} else if (takeoff && invent) {
 	    ilets[iletct++] = ' ';
@@ -1060,9 +1131,21 @@
 		allflag = TRUE;
 	    } else if (sym == 'A') {
 		/* same as the default */ ;
-	    } else if (sym == 'u' || sym == 'U') {
+	    } else if (sym == 'u') {
 		add_valid_menu_class('u');
 		ckfn = ckunpaid;
+	    } else if (sym == 'B') {
+	    	add_valid_menu_class('B');
+	    	ckfn = ckvalidcat;
+	    } else if (sym == 'U') {
+	    	add_valid_menu_class('U');
+	    	ckfn = ckvalidcat;
+	    } else if (sym == 'C') {
+	    	add_valid_menu_class('C');
+		ckfn = ckvalidcat;
+	    } else if (sym == 'X') {
+	    	add_valid_menu_class('X');
+		ckfn = ckvalidcat;
 	    } else if (sym == 'm') {
 		m_seen = TRUE;
 	    } else if (oc_of_sym == MAXOCLASSES) {
@@ -1082,8 +1165,18 @@
 	    return 0;
 	else if (allowgold == 2 && !oletct)
 	    return 1;	/* you dropped gold (or at least tried to) */
-	else
-	    return askchain(&invent, olets, allflag, fn, ckfn, mx, word);
+	else {
+	    int cnt = askchain(&invent, olets, allflag, fn, ckfn, mx, word); 
+	    /*
+	     * askchain() has already finished the job in this case
+	     * so set a special flag to convey that back to the caller
+	     * so that it won't continue processing.
+	     * Fix for bug C331-1 reported by Irina Rempt-Drijfhout. 
+	     */
+	    if (combo && allflag && resultflags)
+		*resultflags |= ALL_FINISHED; 
+	    return cnt;
+	}
 }
 
 /*
@@ -1106,7 +1199,7 @@
 	boolean takeoff, nodot, ident, ininv;
 	char qbuf[QBUFSZ];
 
-	takeoff = !strcmp(word, "take off");
+	takeoff = taking_off(word);
 	ident = !strcmp(word, "identify");
 	nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") ||
 		 ident || takeoff);
@@ -1128,7 +1221,7 @@
 		if (ckfn && !(*ckfn)(otmp)) continue;
 		if (!allflag) {
 			Strcpy(qbuf, !ininv ? doname(otmp) :
-				xprname(otmp, (char *)0, ilet, !nodot, 0L));
+				xprname(otmp, (char *)0, ilet, !nodot, 0L, 0L));
 			Strcat(qbuf, "?");
 			sym = (takeoff || ident || otmp->quan < 2L) ?
 				nyaq(qbuf) : nyNaq(qbuf);
@@ -1148,13 +1241,7 @@
 			sym = 'y';
 			if (yn_number < otmp->quan && !welded(otmp) &&
 			    (!otmp->cursed || otmp->otyp != LOADSTONE)) {
-			    struct obj *otmpx = splitobj(otmp, yn_number);
-			    if (!otmpx || otmpx->nobj != otmp2)
-				impossible("bad object split in askchain");
-			    /* assume other worn items aren't mergable */
-			    if (otmp == uwep) setuwep(otmpx);
-				if (otmp == uquiver) setuqwep(otmpx);
-				if (otmp == uswapwep) setuswapwep(otmpx);
+			    otmp = splitobj(otmp, yn_number);
 			}
 		    }
 		}
@@ -1272,7 +1359,7 @@
 	n = 0;
 	if (flags.menu_style == MENU_TRADITIONAL)
 	    do {
-		n = ggetobj("identify", identify, id_limit, FALSE);
+		n = ggetobj("identify", identify, id_limit, FALSE, (unsigned *)0);
 		if (n < 0) break; /* quit or no eligible items */
 	    } while ((id_limit -= n) > 0);
 	if (n == 0 || n < -1)
@@ -1307,32 +1394,37 @@
 register struct obj *obj;
 long quan;
 {
-	long savequan = obj->quan;
-	if (quan) obj->quan = quan;
 	if (!prefix) prefix = "";
 	pline("%s%s%s",
 	      prefix, *prefix ? " " : "",
-	      xprname(obj, (char *)0, obj_to_let(obj), TRUE, 0L));
-	if (quan) obj->quan = savequan;
+	      xprname(obj, (char *)0, obj_to_let(obj), TRUE, 0L, quan));
 }
 
 #endif /* OVL2 */
 #ifdef OVL1
 
 char *
-xprname(obj, txt, let, dot, cost)
+xprname(obj, txt, let, dot, cost, quan)
 struct obj *obj;
 const char *txt;	/* text to print instead of obj */
 char let;		/* inventory letter */
 boolean dot;		/* append period; (dot && cost => Iu) */
 long cost;		/* cost (for inventory of unpaid or expended items) */
+long quan;		/* if non-0, print this quantity, not obj->quan */
 {
 #ifdef LINT	/* handle static char li[BUFSZ]; */
-	char li[BUFSZ];
+    char li[BUFSZ];
 #else
-	static char li[BUFSZ];
+    static char li[BUFSZ];
 #endif
-	boolean use_invlet = flags.invlet_constant && let != CONTAINED_SYM;
+    boolean use_invlet = flags.invlet_constant && let != CONTAINED_SYM;
+    long savequan = 0;
+
+    if (quan && obj) {
+	savequan = obj->quan;
+	obj->quan = quan;
+    }
+
     /*
      * If let is:
      *	*  Then obj == null and we are printing a total amount.
@@ -1340,10 +1432,10 @@
      */
     if (cost != 0 || let == '*') {
 	/* if dot is true, we're doing Iu, otherwise Ix */
-	Sprintf(li, "%c - %-45s %6ld zorkmid%s",
+	Sprintf(li, "%c - %-45s %6ld %s",
 		(dot && use_invlet ? obj->invlet : let),
-		(txt ? txt : doname(obj)), cost, plur(cost));
-    } else if (obj->oclass == GOLD_CLASS) {
+		(txt ? txt : doname(obj)), cost, currency(cost));
+    } else if (obj && obj->oclass == GOLD_CLASS) {
 	Sprintf(li, "%ld gold piece%s%s", obj->quan, plur(obj->quan),
 		(dot ? "." : ""));
     } else {
@@ -1352,6 +1444,8 @@
 		(use_invlet ? obj->invlet : let),
 		(txt ? txt : doname(obj)), (dot ? "." : ""));
     }
+    if (savequan) obj->quan = savequan;
+
     return li;
 }
 
@@ -1400,16 +1494,15 @@
 }
 
 /*
- * If lets == NULL or "", list all objects in the inventory.  Otherwise,
- * list all objects with object classes that match the order in lets.
- *
- * Returns the letter identifier of a selected item, or 0 if nothing
- * was selected.
+ * Internal function used by display_inventory and getobj that can display
+ * inventory and return a count as well as a letter. If out_cnt is not null,
+ * any count returned from the menu selection is placed here.
  */
-char
-display_inventory(lets, want_reply)
+static char
+display_pickinv(lets, want_reply, out_cnt)
 register const char *lets;
 boolean want_reply;
+long* out_cnt;
 {
 	struct obj *otmp;
 	char ilet, ret;
@@ -1456,8 +1549,8 @@
 	    for (otmp = invent; otmp; otmp = otmp->nobj) {
 		if (otmp->invlet == lets[0]) {
 		    ret = message_menu(lets[0],
-				  want_reply ? PICK_ONE : PICK_NONE,
-				  xprname(otmp, (char *)0, lets[0], TRUE, 0L));
+			  want_reply ? PICK_ONE : PICK_NONE,
+			  xprname(otmp, (char *)0, lets[0], TRUE, 0L, 0L));
 		    break;
 		}
 	    }
@@ -1499,6 +1592,7 @@
 	n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected);
 	if (n > 0) {
 	    ret = selected[0].item.a_char;
+	    if (out_cnt) *out_cnt = selected[0].count;
 	    free((genericptr_t)selected);
 	} else
 	    ret = !n ? '\0' : '\033';	/* cancelled */
@@ -1507,6 +1601,21 @@
 }
 
 /*
+ * If lets == NULL or "", list all objects in the inventory.  Otherwise,
+ * list all objects with object classes that match the order in lets.
+ *
+ * Returns the letter identifier of a selected item, or 0 if nothing
+ * was selected.
+ */
+char
+display_inventory(lets, want_reply)
+register const char *lets;
+boolean want_reply;
+{
+	return display_pickinv(lets, want_reply, (long *)0);
+}
+
+/*
  * Returns the number of unpaid items within the given list.  This includes
  * contained objects.
  */
@@ -1525,6 +1634,45 @@
     return count;
 }
 
+/*
+ * Returns the number of items with b/u/c/unknown within the given list.  
+ * This does NOT include contained objects.
+ */
+int
+count_buc(list, type)
+    struct obj *list;
+    int type;
+{
+    int count = 0;
+
+    while (list) {
+	switch(type) {
+	    case BUC_BLESSED:
+		if (list->oclass != GOLD_CLASS && list->bknown && list->blessed)
+		    count++;
+		break;
+	    case BUC_CURSED:
+		if (list->oclass != GOLD_CLASS && list->bknown && list->cursed)
+		    count++;
+		break;
+	    case BUC_UNCURSED:
+		if (list->oclass != GOLD_CLASS &&
+			list->bknown && !list->blessed && !list->cursed)
+		    count++;
+		break;
+	    case BUC_UNKNOWN:
+		if (list->oclass != GOLD_CLASS && !list->bknown)
+		    count++;
+		break;
+	    default:
+		impossible("need count of curse status %d?", type);
+		return 0;
+	}
+	list = list->nobj;
+    }
+    return count;
+}
+
 STATIC_OVL void
 dounpaid()
 {
@@ -1548,7 +1696,7 @@
 
 	pline("%s", xprname(otmp, distant_name(otmp, doname),
 			    marker ? otmp->invlet : CONTAINED_SYM,
-			    TRUE, unpaid_cost(otmp)));
+			    TRUE, unpaid_cost(otmp), 0L));
 	return;
     }
 
@@ -1573,7 +1721,7 @@
 		    save_unpaid = otmp->unpaid;
 		    otmp->unpaid = 0;
 		    putstr(win, 0, xprname(otmp, distant_name(otmp, doname),
-					   ilet, TRUE, cost));
+					   ilet, TRUE, cost, 0L));
 		    otmp->unpaid = save_unpaid;
 		    num_so_far++;
 		}
@@ -1599,7 +1747,7 @@
 		    marker->unpaid = 0;    /* suppress "(unpaid)" suffix */
 		    putstr(win, 0,
 			   xprname(marker, distant_name(marker, doname),
-				   CONTAINED_SYM, TRUE, cost));
+				   CONTAINED_SYM, TRUE, cost, 0L));
 		    marker->unpaid = save_unpaid;
 		}
 	    }
@@ -1607,7 +1755,7 @@
     }
 
     putstr(win, 0, "");
-    putstr(win, 0, xprname((struct obj *)0, "Total:", '*', FALSE, totcost));
+    putstr(win, 0, xprname((struct obj *)0, "Total:", '*', FALSE, totcost, 0L));
     display_nhwindow(win, FALSE);
     destroy_nhwindow(win);
 }
@@ -1657,7 +1805,8 @@
 	    /* collect a list of classes of objects carried, for use as a prompt */
 	    types[0] = 0;
 	    class_count = collect_obj_classes(types, invent,
-					      FALSE, (u.ugold != 0),
+					      FALSE,
+					      (u.ugold != 0),
 					      (boolean FDECL((*),(OBJ_P))) 0);
 	    if (unpaid_count) {
 		Strcat(types, "u");
@@ -1810,9 +1959,26 @@
 	winid tmpwin;
 	boolean skip_objects = (obj_cnt >= 5);
 
-	if (u.uswallow) {
+	if (u.uswallow && u.ustuck) {
+	    struct monst *mtmp = u.ustuck;
+	    Sprintf(fbuf, "Contents of %s %s",
+		s_suffix(mon_nam(mtmp)), mbodypart(mtmp, STOMACH));
+	    /* Skip "Contents of " by using fbuf index 12 */
+	    You("%s to %s what is lying in %s.",
+		Blind ? "try" : "look around", verb, &fbuf[12]);
+	    otmp = mtmp->minvent;
+	    if (otmp) {
+		for ( ; otmp; otmp = otmp->nobj) {
+			/* If swallower is an animal, it should have become stone but... */
+			if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE);
+		}
+		if (Blind) Strcpy(fbuf, "You feel");
+		Strcat(fbuf,":");
+	    	(void) display_minventory(mtmp, MINV_ALL, fbuf);
+	    } else {
 		You("%s no objects here.", verb);
-		return(!!Blind);
+	    }
+	    return(!!Blind);
 	}
 	if (!skip_objects && (trap = t_at(u.ux,u.uy)) && trap->tseen)
 		There("is %s here.",
@@ -1941,7 +2107,8 @@
 #endif
 	    obj->greased != otmp->greased ||
 	    obj->oeroded != otmp->oeroded ||
-	    obj->oeroded2 != otmp->oeroded2)
+	    obj->oeroded2 != otmp->oeroded2 ||
+	    obj->bypass != otmp->bypass)
 	    return(FALSE);
 
 	if ((obj->oclass==WEAPON_CLASS || obj->oclass==ARMOR_CLASS) &&
@@ -1960,7 +2127,7 @@
 	/* hatching eggs don't merge; ditto for revivable corpses */
 	if ((obj->otyp == EGG && (obj->timed || otmp->timed)) ||
 	    (obj->otyp == CORPSE && otmp->corpsenm >= LOW_PM &&
-		mons[otmp->corpsenm].mlet == S_TROLL))
+		is_reviver(&mons[otmp->corpsenm])))
 	    return FALSE;
 
 	/* allow candle merging only if their ages are close */
@@ -2078,7 +2245,7 @@
 	return 0;
 }
 
-static boolean
+STATIC_OVL boolean
 tool_in_use(obj)
 struct obj *obj;
 {
@@ -2139,7 +2306,7 @@
 	/* burn_floor_paper() keeps an object pointer that it tries to
 	 * useupf() multiple times, so obj must survive if plural */
 	if (obj->quan > numused)
-		otmp = splitobj(obj, obj->quan - numused);
+		otmp = splitobj(obj, numused);
 	else
 		otmp = obj;
 	if(costly_spot(otmp->ox, otmp->oy)) {
@@ -2364,11 +2531,13 @@
  *	MINV_ALL	- display all inventory
  */
 struct obj *
-display_minventory(mon, dflags)
+display_minventory(mon, dflags, title)
 register struct monst *mon;
 int dflags;
+char *title;
 {
-	struct obj *ret, m_gold;
+	struct obj *ret;
+	struct obj m_gold;
 	char tmp[QBUFSZ];
 	int n;
 	menu_item *selected = 0;
@@ -2401,7 +2570,7 @@
 		    panic("display_minventory: static object freed.");
 	    }
 
-	    n = query_objlist(tmp, mon->minvent, INVORDER_SORT, &selected,
+	    n = query_objlist(title ? title : tmp, mon->minvent, INVORDER_SORT, &selected,
 			(dflags & MINV_NOLET) ? PICK_NONE : PICK_ONE,
 			do_all ? allow_all : worn_wield_only);
 
@@ -2409,7 +2578,7 @@
 
 	    set_uasmon();
 	} else {
-	    invdisp_nothing(tmp, "(none)");
+	    invdisp_nothing(title ? title : tmp, "(none)");
 	    n = 0;
 	}
 
diff -Naurd ../nethack-3.3.1/src/light.c ./src/light.c
--- ../nethack-3.3.1/src/light.c Wed Feb 16 21:50:39 2000
+++ ./src/light.c Fri Mar 22 14:40:55 2002
@@ -40,15 +40,6 @@
 
 #ifdef OVL3
 
-typedef struct ls_t {
-    struct ls_t *next;
-    xchar x, y;		/* source's position */
-    short range;	/* source's current range */
-    short flags;
-    short type;		/* type of light source */
-    genericptr_t id;	/* source's identifier */
-} light_source;
-
 /* flags */
 #define LSF_SHOW	0x1		/* display the light source */
 #define LSF_NEEDS_FIXUP	0x2		/* need oid fixup */
@@ -462,6 +453,12 @@
 	if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) {
 	    obj = (struct obj *) ls->id;
 	    if (obj_is_burning(obj)) {
+		/* The only way to snuff Sunsword is to unwield it.  Darkness
+		 * scrolls won't affect it.  (If we got here because it was
+		 * dropped or thrown inside a monster, this won't matter anyway
+		 * because it will go out when dropped.)
+		 */
+		if (artifact_light(obj)) continue;
 		end_burn(obj, obj->otyp != MAGIC_LAMP);
 		/*
 		 * The current ls element has just been removed (and
@@ -494,7 +491,8 @@
 		|| obj->otyp == CANDELABRUM_OF_INVOCATION
 		|| obj->otyp == TALLOW_CANDLE
 		|| obj->otyp == WAX_CANDLE
-		|| obj->otyp == POT_OIL));
+		|| obj->otyp == POT_OIL
+		|| artifact_light(obj)));
 }
 
 /* copy the light source(s) attachted to src, and attach it/them to dest */
diff -Naurd ../nethack-3.3.1/src/lock.c ./src/lock.c
--- ../nethack-3.3.1/src/lock.c Sat Apr 22 03:31:53 2000
+++ ./src/lock.c Fri Mar 22 14:40:55 2002
@@ -103,7 +103,7 @@
 	    return((xlock.usedtime = 0));
 	}
 
-	if(rn2(100) > xlock.chance) return(1);		/* still busy */
+	if(rn2(100) >= xlock.chance) return(1);		/* still busy */
 
 	You("succeed in %s.", lock_action());
 	if (xlock.door) {
@@ -160,7 +160,7 @@
 	} else			/* blunt */
 	    wake_nearby();	/* due to hammering on the container */
 
-	if(rn2(100) > xlock.chance) return(1);		/* still busy */
+	if(rn2(100) >= xlock.chance) return(1);		/* still busy */
 
 	You("succeed in forcing the lock.");
 	xlock.box->olocked = 0;
@@ -201,7 +201,7 @@
 	    if (costly)
 		loss += stolen_value(xlock.box, u.ux, u.uy,
 					     (boolean)shkp->mpeaceful, TRUE);
-	    if(loss) You("owe %ld zorkmids for objects destroyed.", loss);
+	    if(loss) You("owe %ld %s for objects destroyed.", loss, currency(loss));
 	    delobj(xlock.box);
 	}
 	exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
@@ -476,7 +476,7 @@
 		else
 		    You("start bashing it with your %s.", xname(uwep));
 		xlock.box = otmp;
-		xlock.chance = objects[otmp->otyp].oc_wldam * 2;
+		xlock.chance = objects[uwep->otyp].oc_wldam * 2;
 		xlock.picktyp = picktyp;
 		xlock.usedtime = 0;
 		break;
@@ -877,8 +877,9 @@
 	long save_Blinded;
 
 	if (otmp->oclass == POTION_CLASS) {
-		You("%s a flask shatter!", Blind ? "hear" : "see");
-		potionbreathe(otmp);
+		You("%s a %s shatter!", Blind ? "hear" : "see", bottlename());
+		if (!breathless(youmonst.data) || haseyes(youmonst.data))
+			potionbreathe(otmp);
 		return;
 	}
 	/* We have functions for distant and singular names, but not one */
diff -Naurd ../nethack-3.3.1/src/mail.c ./src/mail.c
--- ../nethack-3.3.1/src/mail.c Wed Aug 9 19:02:04 2000
+++ ./src/mail.c Fri Mar 22 14:40:55 2002
@@ -446,7 +446,8 @@
     "Only Amiga makes it possible.",
     "CATS have all the answers.",
 #endif
-    "Report bugs to <devteam@nethack.org>."
+    "Report bugs to <devteam@nethack.org>.",
+    "Invitation: Visit the NetHack web site at http://www.nethack.org!"
     };
 
     if (Blind) {
diff -Naurd ../nethack-3.3.1/src/makemon.c ./src/makemon.c
--- ../nethack-3.3.1/src/makemon.c Sun Jul 16 02:45:21 2000
+++ ./src/makemon.c Fri Mar 22 14:41:02 2002
@@ -12,6 +12,13 @@
 
 STATIC_VAR NEARDATA struct monst zeromonst;
 
+/* this assumes that a human quest leader or nemesis is an archetype
+   of the corresponding role; that isn't so for some roles (tourist
+   for instance) but is for the priests and monks we use it for... */
+#define quest_mon_represents_role(mptr,role_pm) \
+		(mptr->mlet == S_HUMAN && Role_if(role_pm) && \
+		  (mptr->msound == MS_LEADER || mptr->msound == MS_NEMESIS))
+
 #ifdef OVL0
 STATIC_DCL boolean FDECL(uncommon, (int));
 STATIC_DCL int FDECL(align_shift, (struct permonst *));
@@ -125,6 +132,7 @@
 		if (enexto(&mm, mm.x, mm.y, mtmp->data)) {
 		    mon = makemon(mtmp->data, mm.x, mm.y, NO_MM_FLAGS);
 		    mon->mpeaceful = FALSE;
+		    mon->mavenge = 0;
 		    set_malign(mon);
 		    /* Undo the second peace_minded() check in makemon(); if the
 		     * monster turned out to be peaceful the first time we
@@ -238,7 +246,8 @@
 			    (void)mongets(mtmp, PICK_AXE);
 			if (!rn2(50)) (void)mongets(mtmp, CRYSTAL_BALL);
 		    }
-		} else if (ptr->msound == MS_PRIEST) {
+		} else if (ptr->msound == MS_PRIEST ||
+			quest_mon_represents_role(ptr,PM_PRIEST)) {
 		    otmp = mksobj(MACE, FALSE, FALSE);
 		    if(otmp) {
 			otmp->spe = rnd(3);
@@ -466,6 +475,7 @@
 #endif /* OVL2 */
 #ifdef OVL1
 
+
 STATIC_OVL void
 m_initinv(mtmp)
 register struct	monst	*mtmp;
@@ -526,7 +536,7 @@
 		    if (mac < 10 && rn2(3))
 			mac += 1 + mongets(mtmp, LEATHER_GLOVES);
 		    else if (mac < 10 && rn2(2))
-			mac += 1 + mongets(mtmp, ELVEN_CLOAK);
+			mac += 1 + mongets(mtmp, LEATHER_CLOAK);
 
 		    if(ptr != &mons[PM_GUARD] &&
 			ptr != &mons[PM_WATCHMAN] &&
@@ -547,10 +557,16 @@
 		    case 2: (void) mongets(mtmp, POT_HEALING);
 		    case 3: (void) mongets(mtmp, WAN_STRIKING);
 		    }
-		} else if (ptr->msound == MS_PRIEST) {
-		    (void) mongets(mtmp, ROBE);
+		} else if (ptr->msound == MS_PRIEST ||
+			quest_mon_represents_role(ptr,PM_PRIEST)) {
+		    (void) mongets(mtmp, rn2(7) ? ROBE :
+					     rn2(3) ? CLOAK_OF_PROTECTION :
+						 CLOAK_OF_MAGIC_RESISTANCE);
 		    (void) mongets(mtmp, SMALL_SHIELD);
 		    mtmp->mgold = (long)rn1(10,20);
+		} else if (quest_mon_represents_role(ptr,PM_MONK)) {
+		    (void) mongets(mtmp, rn2(11) ? ROBE :
+					     CLOAK_OF_MAGIC_RESISTANCE);
 		}
 		break;
 	    case S_NYMPH:
@@ -658,31 +674,42 @@
 	 * polymorphed away from their original forms, the clone doesn't have
 	 * room for the extra information.  we also don't want two shopkeepers
 	 * around for the same shop.
-	 * similarly, clones of named monsters don't have room for the name,
-	 * so we just make the clone unnamed instead of bothering to create
-	 * a clone with room and copying over the name from the right place
-	 * (which changes if the original was a shopkeeper or guard).
 	 */
 	if (mon->isshk) m2->isshk = FALSE;
 	if (mon->isgd) m2->isgd = FALSE;
 	if (mon->ispriest) m2->ispriest = FALSE;
 	m2->mxlth = 0;
-	m2->mnamelth = 0;
 	place_monster(m2, m2->mx, m2->my);
 	if (emits_light(m2->data))
 	    new_light_source(m2->mx, m2->my, emits_light(m2->data),
 			     LS_MONSTER, (genericptr_t)m2);
+	if (m2->mnamelth) {
+	    m2->mnamelth = 0; /* or it won't get allocated */
+	    m2 = christen_monst(m2, NAME(mon));
+	}
 	newsym(m2->mx,m2->my);	/* display the new monster */
 	if (mon->mtame) {
 	    struct monst *m3;
 
-	    /* because m2 is a copy of mon it is tame but not init'ed.
-	     * however, tamedog will not re-tame a tame dog, so m2
-	     * must be made non-tame to get initialized properly.
-	     */
-	    m2->mtame = 0;
-	    if ((m3 = tamedog(m2, (struct obj *)0)) != 0)
+	    if (mon->isminion) {
+		m3 = newmonst(sizeof(struct epri) + mon->mnamelth);
+		*m3 = *m2;
+		m3->mxlth = sizeof(struct epri);
+		if (m2->mnamelth) Strcpy(NAME(m3), NAME(m2));
+		*(EPRI(m3)) = *(EPRI(mon));
+		replmon(m2, m3);
 		m2 = m3;
+	    } else {
+		/* because m2 is a copy of mon it is tame but not init'ed.
+		 * however, tamedog will not re-tame a tame dog, so m2
+		 * must be made non-tame to get initialized properly.
+		 */
+		m2->mtame = 0;
+		if ((m3 = tamedog(m2, (struct obj *)0)) != 0) {
+		    m2 = m3;
+		    *(EDOG(m2)) = *(EDOG(mon));
+		}
+	    }
 	}
 	return m2;
 }
@@ -705,6 +732,7 @@
 	boolean anymon = (!ptr);
 	boolean byyou = (x == u.ux && y == u.uy);
 	boolean allow_minvent = ((mmflags & NO_MINVENT) == 0);
+	boolean countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0);
 	uchar lim;
 
 	/* if caller wants random location, do it here */
@@ -772,11 +800,11 @@
 	 * the caller manually decrement mvitals if the monster is created
 	 * under circumstances where one would not logically expect the
 	 * creation to reduce the supply of wild monsters.  Monster cloning
-	 * might be one such case, but we go against logic there in order to
+ 	 * might be one such case, but we go against logic there in order to
 	 * reduce the possibility of abuse.
 	 */
-	if (mvitals[mndx].born < 255) mvitals[mndx].born++;
-	lim = (mndx == PM_NAZGUL ? 9 : mndx == PM_ERINYS ? 3 : MAXMONNO);
+	if (mvitals[mndx].born < 255 && countbirth) mvitals[mndx].born++;
+	lim = mbirth_limit(mndx);
 	if ((int) mvitals[mndx].born >= lim && !(mons[mndx].geno & G_NOGEN) &&
 		!(mvitals[mndx].mvflags & G_EXTINCT)) {
 #ifdef DEBUG
@@ -798,6 +826,8 @@
 	mtmp->m_id = flags.ident++;
 	if (!mtmp->m_id) mtmp->m_id = flags.ident++;	/* ident overflowed */
 	set_mon_data(mtmp, ptr, 0);
+	if (mtmp->data->msound == MS_LEADER)
+	    quest_status.leader_m_id = mtmp->m_id;
 	mtmp->mxlth = xlth;
 	mtmp->mnum = mndx;
 
@@ -832,6 +862,8 @@
 
 	if (In_sokoban(&u.uz) && !mindless(ptr))  /* know about traps here */
 	    mtmp->mtrapseen = (1L << (PIT - 1)) | (1L << (HOLE - 1));
+	if (ptr->msound == MS_LEADER)		/* leader knows about portal */
+	    mtmp->mtrapseen |= (1 << (MAGIC_PORTAL-1));
 
 	place_monster(mtmp, x, y);
 	mtmp->mcansee = mtmp->mcanmove = TRUE;
@@ -877,7 +909,7 @@
 			break;
 		case S_BAT:
 			if (Inhell && is_bat(ptr))
-			    mon_adjust_speed(mtmp, 2);
+			    mon_adjust_speed(mtmp, 2, (struct obj *)0);
 			break;
 	}
 	if ((ct = emits_light(mtmp->data)) > 0)
@@ -893,7 +925,7 @@
 			mtmp->cham = CHAM_ORDINARY;
 		else {
 			mtmp->cham = mcham;
-			(void) newcham(mtmp, rndmonst());
+			(void) newcham(mtmp, rndmonst(), FALSE);
 		}
 	} else if (mndx == PM_WIZARD_OF_YENDOR) {
 		mtmp->iswiz = TRUE;
@@ -931,6 +963,7 @@
 	}
 	if(is_dprince(ptr) && ptr->msound == MS_BRIBE) {
 	    mtmp->mpeaceful = mtmp->minvis = mtmp->perminvis = 1;
+	    mtmp->mavenge = 0;
 	    if (uwep && uwep->oartifact == ART_EXCALIBUR)
 		mtmp->mpeaceful = mtmp->mtame = FALSE;
 	}
@@ -981,6 +1014,13 @@
 	return(mtmp);
 }
 
+int
+mbirth_limit(mndx)
+int mndx;
+{
+	return (mndx == PM_NAZGUL ? 9 : mndx == PM_ERINYS ? 3 : MAXMONNO); 
+}
+
 /* used for wand/scroll/spell of create monster */
 /* returns TRUE iff you know monsters have been created */
 boolean
@@ -1326,7 +1366,7 @@
 	    if (mvitals[newtype].mvflags & G_GENOD) {	/* allow G_EXTINCT */
 		if (sensemon(mtmp))
 		    pline("As %s grows up into %s, %s %s!", mon_nam(mtmp),
-			an(ptr->mname), he[pronoun_gender(mtmp)],
+			an(ptr->mname), mhe(mtmp),
 			nonliving(ptr) ? "expires" : "dies");
 		set_mon_data(mtmp, ptr, -1);	/* keep mvitals[] accurate */
 		mondied(mtmp);
diff -Naurd ../nethack-3.3.1/src/mapglyph.c ./src/mapglyph.c
--- ../nethack-3.3.1/src/mapglyph.c Thu Jan 1 01:00:00 1970
+++ ./src/mapglyph.c Fri Mar 22 14:40:55 2002
@@ -0,0 +1,232 @@
+/*	SCCS Id: @(#)mapglyph.c	3.4	2000/08/18	*/
+/* Copyright (c) David Cohrs, 1991				  */
+/* NetHack may be freely redistributed.  See license for details. */
+
+#include "hack.h"
+#if defined(TTY_GRAPHICS)
+#include "wintty.h"	/* for prototype of has_color() only */
+#endif
+#include "color.h"
+
+int explcolors[] = {
+	CLR_BLACK,	/* dark    */
+	CLR_GREEN,	/* noxious */
+	CLR_BROWN,	/* muddy   */
+	CLR_BLUE,	/* wet     */
+	CLR_MAGENTA,	/* magical */
+	CLR_ORANGE,	/* fiery   */
+	CLR_WHITE,	/* frosty  */
+};
+
+#if !defined(TTY_GRAPHICS)
+#define has_color(n)  TRUE
+#endif
+
+#ifdef TEXTCOLOR
+#define zap_color(n)  color = iflags.use_color ? zapcolors[n] : NO_COLOR
+#define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR
+#define obj_color(n)  color = iflags.use_color ? objects[n].oc_color : NO_COLOR
+#define mon_color(n)  color = iflags.use_color ? mons[n].mcolor : NO_COLOR
+#define invis_color(n) color = NO_COLOR
+#define pet_color(n)  color = iflags.use_color ? mons[n].mcolor : NO_COLOR
+#define warn_color(n) color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
+#define explode_color(n) color = iflags.use_color ? explcolors[n] : NO_COLOR
+# if defined(REINCARNATION) && defined(ASCIIGRAPH)
+#  define ROGUE_COLOR
+# endif
+
+#else	/* no text color */
+
+#define zap_color(n)
+#define cmap_color(n)
+#define obj_color(n)
+#define mon_color(n)
+#define invis_color(n)
+#define pet_color(c)
+#define warn_color(n)
+#define explode_color(n)
+#endif
+
+#ifdef ROGUE_COLOR
+# if defined(USE_TILES) && defined(MSDOS)
+#define HAS_ROGUE_IBM_GRAPHICS (iflags.IBMgraphics && !iflags.grmode && \
+	Is_rogue_level(&u.uz))
+# else
+#define HAS_ROGUE_IBM_GRAPHICS (iflags.IBMgraphics && Is_rogue_level(&u.uz))
+# endif
+#endif
+
+/*ARGSUSED*/
+void
+mapglyph(glyph, ochar, ocolor, ospecial, x, y)
+int glyph, *ocolor, x, y;
+int *ochar;
+unsigned *ospecial;
+{
+	register int offset;
+	int color = NO_COLOR;
+	uchar ch;
+	unsigned special = 0;
+
+    /*
+     *  Map the glyph back to a character and color.
+     *
+     *  Warning:  For speed, this makes an assumption on the order of
+     *		  offsets.  The order is set in display.h.
+     */
+    if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) {	/* a warning flash */
+    	ch = warnsyms[offset];
+# ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS)
+	    color = NO_COLOR;
+	else
+# endif
+	    warn_color(offset);
+    } else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {	/* swallow */
+	/* see swallow_to_glyph() in display.c */
+	ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color)
+	    color = NO_COLOR;
+	else
+#endif
+	    mon_color(offset >> 3);
+    } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {	/* zap beam */
+	/* see zapdir_to_glyph() in display.c */
+	ch = showsyms[S_vbeam + (offset & 0x3)];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color)
+	    color = NO_COLOR;
+	else
+#endif
+	    zap_color((offset >> 2));
+    } else if ((offset = (glyph - GLYPH_EXPLODE_OFF)) >= 0) {	/* explosion */
+	ch = showsyms[(offset % MAXEXPCHARS) + S_explode1];
+	explode_color(offset / MAXEXPCHARS);
+    } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) {	/* cmap */
+	ch = showsyms[offset];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) {
+	    if (offset >= S_vwall && offset <= S_hcdoor)
+		color = CLR_BROWN;
+	    else if (offset >= S_arrow_trap && offset <= S_polymorph_trap)
+		color = CLR_MAGENTA;
+	    else if (offset == S_corr || offset == S_litcorr)
+		color = CLR_GRAY;
+	    else if (offset >= S_room && offset <= S_water)
+		color = CLR_GREEN;
+	    else
+		color = NO_COLOR;
+	} else
+#endif
+	    cmap_color(offset);
+    } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {	/* object */
+	if (offset == BOULDER && iflags.bouldersym) ch = iflags.bouldersym;
+	else ch = oc_syms[(int)objects[offset].oc_class];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) {
+	    switch(objects[offset].oc_class) {
+		case GOLD_CLASS: color = CLR_YELLOW; break;
+		case FOOD_CLASS: color = CLR_RED; break;
+		default: color = CLR_BRIGHT_BLUE; break;
+	    }
+	} else
+#endif
+	    obj_color(offset);
+    } else if ((offset = (glyph - GLYPH_RIDDEN_OFF)) >= 0) {	/* mon ridden */
+	ch = monsyms[(int)mons[offset].mlet];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS)
+	    /* This currently implies that the hero is here -- monsters */
+	    /* don't ride (yet...).  Should we set it to yellow like in */
+	    /* the monster case below?  There is no equivalent in rogue. */
+	    color = NO_COLOR;	/* no need to check iflags.use_color */
+	else
+#endif
+	    mon_color(offset);
+	    special |= MG_RIDDEN;
+    } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {	/* a corpse */
+	ch = oc_syms[(int)objects[CORPSE].oc_class];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color)
+	    color = CLR_RED;
+	else
+#endif
+	    mon_color(offset);
+	    special |= MG_CORPSE;
+    } else if ((offset = (glyph - GLYPH_DETECT_OFF)) >= 0) {	/* mon detect */
+	ch = monsyms[(int)mons[offset].mlet];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS)
+	    color = NO_COLOR;	/* no need to check iflags.use_color */
+	else
+#endif
+	    mon_color(offset);
+	/* Disabled for now; anyone want to get reverse video to work? */
+	/* is_reverse = TRUE; */
+	    special |= MG_DETECT;
+    } else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) {	/* invisible */
+	ch = DEF_INVISIBLE;
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS)
+	    color = NO_COLOR;	/* no need to check iflags.use_color */
+	else
+#endif
+	    invis_color(offset);
+	    special |= MG_INVIS;
+    } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {	/* a pet */
+	ch = monsyms[(int)mons[offset].mlet];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS)
+	    color = NO_COLOR;	/* no need to check iflags.use_color */
+	else
+#endif
+	    pet_color(offset);
+	    special |= MG_PET;
+    } else {							/* a monster */
+	ch = monsyms[(int)mons[glyph].mlet];
+#ifdef ROGUE_COLOR
+	if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) {
+	    if (x == u.ux && y == u.uy)
+		/* actually player should be yellow-on-gray if in a corridor */
+		color = CLR_YELLOW;
+	    else
+		color = NO_COLOR;
+	} else
+#endif
+	    mon_color(glyph);
+    }
+
+#ifdef TEXTCOLOR
+    /* Turn off color if no color defined, or rogue level w/o PC graphics. */
+# ifdef REINCARNATION
+#  ifdef ASCIIGRAPH
+    if (!has_color(color) || (Is_rogue_level(&u.uz) && !HAS_ROGUE_IBM_GRAPHICS))
+#  else
+    if (!has_color(color) || Is_rogue_level(&u.uz))
+#  endif
+# else
+    if (!has_color(color))
+# endif
+	color = NO_COLOR;
+#endif
+    if (ochar)
+	*ochar = (int)ch;
+    else
+    	impossible("glyphmap(): Invalid output character buffer.");
+
+    if (ospecial)
+	*ospecial = special;
+    else
+    	impossible("glyphmap(): Invalid special feature return buffer.");
+
+#ifdef TEXTCOLOR
+    if (ocolor)
+	*ocolor = color;
+    else
+    	impossible("glyphmap(): Invalid color  buffer.");
+#endif
+    return;
+}
+
+/*mapglyph.c*/
diff -Naurd ../nethack-3.3.1/src/mcastu.c ./src/mcastu.c
--- ../nethack-3.3.1/src/mcastu.c Mon Jan 17 14:51:51 2000
+++ ./src/mcastu.c Fri Mar 22 14:40:55 2002
@@ -1,10 +1,42 @@
-/*	SCCS Id: @(#)mcastu.c	3.3	97/11/02	*/
+/*	SCCS Id: @(#)mcastu.c	3.4	2002/02/07	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 
-STATIC_DCL void FDECL(cursetxt,(struct monst *));
+/* monster mage spells */
+#define MGC_PSI_BOLT	 0
+#define MGC_CURE_SELF	 1
+#define MGC_HASTE_SELF	 2
+#define MGC_STUN_YOU	 3
+#define MGC_DISAPPEAR	 4
+#define MGC_WEAKEN_YOU	 5
+#define MGC_DESTRY_ARMR	 6
+#define MGC_CURSE_ITEMS	 7
+#define MGC_AGGRAVATION	 8
+#define MGC_SUMMON_MONS	 9
+#define MGC_CLONE_WIZ	10
+#define MGC_DEATH_TOUCH	11
+
+/* monster cleric spells */
+#define CLC_OPEN_WOUNDS	 0
+#define CLC_CURE_SELF	 1
+#define CLC_CONFUSE_YOU	 2
+#define CLC_PARALYZE	 3
+#define CLC_BLIND_YOU	 4
+#define CLC_INSECTS	 5
+#define CLC_CURSE_ITEMS	 6
+#define CLC_LIGHTNING	 7
+#define CLC_FIRE_PILLAR	 8
+#define CLC_GEYSER	 9
+
+STATIC_DCL void FDECL(cursetxt,(struct monst *,BOOLEAN_P));
+STATIC_DCL int FDECL(choose_magic_spell, (int));
+STATIC_DCL int FDECL(choose_clerical_spell, (int));
+STATIC_DCL void FDECL(cast_wizard_spell,(struct monst *, int,int));
+STATIC_DCL void FDECL(cast_cleric_spell,(struct monst *, int,int));
+STATIC_DCL boolean FDECL(is_undirected_spell,(unsigned int,int));
+STATIC_DCL boolean FDECL(spell_would_be_useless,(struct monst *,unsigned int,int));
 
 #ifdef OVL0
 
@@ -13,13 +45,16 @@
 /* feedback when frustrated monster couldn't cast a spell */
 STATIC_OVL
 void
-cursetxt(mtmp)
+cursetxt(mtmp, undirected)
 struct monst *mtmp;
+boolean undirected;
 {
-	if (canseemon(mtmp)) {
+	if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
 	    const char *point_msg;  /* spellcasting monsters are impolite */
 
-	    if ((Invis && !perceives(mtmp->data) &&
+	    if (undirected)
+		point_msg = "all around, then curses";
+	    else if ((Invis && !perceives(mtmp->data) &&
 			(mtmp->mux != u.ux || mtmp->muy != u.uy)) ||
 		    (youmonst.m_ap_type == M_AP_OBJECT &&
 			youmonst.mappearance == STRANGE_OBJECT) ||
@@ -39,34 +74,198 @@
 #endif /* OVL0 */
 #ifdef OVLB
 
+/* convert a level based random selection into a specific mage spell;
+   inappropriate choices will be screened out by spell_would_be_useless() */
+STATIC_OVL int
+choose_magic_spell(spellval)
+int spellval;
+{
+    switch (spellval) {
+    case 22:
+    case 21:
+    case 20:
+	return MGC_DEATH_TOUCH;
+    case 19:
+    case 18:
+	return MGC_CLONE_WIZ;
+    case 17:
+    case 16:
+    case 15:
+	return MGC_SUMMON_MONS;	/* also aggravates */
+    case 14:
+    case 13:
+	return MGC_AGGRAVATION;
+    case 12:
+    case 11:
+    case 10:
+	return MGC_CURSE_ITEMS;
+    case 9:
+    case 8:
+	return MGC_DESTRY_ARMR;
+    case 7:
+    case 6:
+	return MGC_WEAKEN_YOU;
+    case 5:
+    case 4:
+	return MGC_DISAPPEAR;
+    case 3:
+	return MGC_STUN_YOU;
+    case 2:
+	return MGC_HASTE_SELF;
+    case 1:
+	return MGC_CURE_SELF;
+    case 0:
+    default:
+	return MGC_PSI_BOLT;
+    }
+}
+
+/* convert a level based random selection into a specific cleric spell */
+STATIC_OVL int
+choose_clerical_spell(spellnum)
+int spellnum;
+{
+    switch (spellnum) {
+    case 13:
+	return CLC_GEYSER;
+    case 12:
+	return CLC_FIRE_PILLAR;
+    case 11:
+	return CLC_LIGHTNING;
+    case 10:
+    case 9:
+	return CLC_CURSE_ITEMS;
+    case 8:
+	return CLC_INSECTS;
+    case 7:
+    case 6:
+	return CLC_BLIND_YOU;
+    case 5:
+    case 4:
+	return CLC_PARALYZE;
+    case 3:
+    case 2:
+	return CLC_CONFUSE_YOU;
+    case 1:
+	return CLC_CURE_SELF;
+    case 0:
+    default:
+	return CLC_OPEN_WOUNDS;
+    }
+}
+
+/* return values:
+ * 1: successful spell
+ * 0: unsuccessful spell
+ */
 int
-castmu(mtmp, mattk)	/* monster casts spell at you */
+castmu(mtmp, mattk, thinks_it_foundyou, foundyou)
 	register struct monst *mtmp;
 	register struct attack *mattk;
+	boolean thinks_it_foundyou;
+	boolean foundyou;
 {
 	int	dmg, ml = mtmp->m_lev;
+	int ret;
+	int spellnum = 0;
 
-	if(mtmp->mcan || mtmp->mspec_used || !ml) {  /* could not attack */
-	    cursetxt(mtmp);
+	/* Three cases:
+	 * -- monster is attacking you.  Search for a useful spell.
+	 * -- monster thinks it's attacking you.  Search for a useful spell,
+	 *    without checking for undirected.  If the spell found is directed,
+	 *    it fails with cursetxt() and loss of mspec_used.
+	 * -- monster isn't trying to attack.  Select a spell once.  Don't keep
+	 *    searching; if that spell is not useful (or if it's directed),
+	 *    return and do something else. 
+	 * Since most spells are directed, this means that a monster that isn't
+	 * attacking casts spells only a small portion of the time that an
+	 * attacking monster does.
+	 */
+	if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) {
+	    int cnt = 40;
+
+	    do {
+		spellnum = rn2(ml);
+		if (mattk->adtyp == AD_SPEL)
+		    spellnum = choose_magic_spell(spellnum);
+		else
+		    spellnum = choose_clerical_spell(spellnum);
+		/* not trying to attack?  don't allow directed spells */
+		if (!thinks_it_foundyou) {
+		    if (!is_undirected_spell(mattk->adtyp, spellnum) ||
+			spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) {
+			if (foundyou)
+			    impossible("spellcasting monster found you and doesn't know it?");
+			return 0;
+		    }
+		    break;
+		}
+	    } while(--cnt > 0 &&
+		    spell_would_be_useless(mtmp, mattk->adtyp, spellnum));
+	    if (cnt == 0) return 0;
+	}
+
+	/* monster unable to cast spells? */
+	if(mtmp->mcan || mtmp->mspec_used || !ml) {
+	    cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum));
 	    return(0);
-	} else {
-	    nomul(0);
-	    if(rn2(ml*10) < (mtmp->mconf ? 100 : 20)) {	/* fumbled attack */
-		if (canseemon(mtmp) && flags.soundok)
-		    pline_The("air crackles around %s.", mon_nam(mtmp));
-		return(0);
-	    }
 	}
+
+	if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) {
+	    mtmp->mspec_used = 10 - mtmp->m_lev;
+	    if (mtmp->mspec_used < 2) mtmp->mspec_used = 2;
+	}
+
+	/* monster can cast spells, but is casting a directed spell at the
+	   wrong place?  If so, give a message, and return.  Do this *after*
+	   penalizing mspec_used. */
+	if (!foundyou && thinks_it_foundyou &&
+		!is_undirected_spell(mattk->adtyp, spellnum)) {
+	    pline("%s casts a spell at %s!",
+		canseemon(mtmp) ? Monnam(mtmp) : "Something",
+		levl[mtmp->mux][mtmp->muy].typ == WATER
+		    ? "empty water" : "thin air");
+	    return(0);
+	}
+
+	nomul(0);
+	if(rn2(ml*10) < (mtmp->mconf ? 100 : 20)) {	/* fumbled attack */
+	    if (canseemon(mtmp) && flags.soundok)
+		pline_The("air crackles around %s.", mon_nam(mtmp));
+	    return(0);
+	}
+	if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) {
+	    pline("%s casts a spell%s!",
+		  canspotmon(mtmp) ? Monnam(mtmp) : "Something",
+		  is_undirected_spell(mattk->adtyp, spellnum) ? "" :
+		  (Invisible && !perceives(mtmp->data) && 
+		   (mtmp->mux != u.ux || mtmp->muy != u.uy)) ?
+		  " at a spot near you" :
+		  (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) ?
+		  " at your displaced image" :
+		  " at you");
+	}
+
 /*
  *	As these are spells, the damage is related to the level
  *	of the monster casting the spell.
  */
-	if (mattk->damd)
-		dmg = d((int)((ml/3) + mattk->damn), (int)mattk->damd);
-	else dmg = d((int)((ml/3) + 1), 6);
+	if (!foundyou) {
+	    dmg = 0;
+	    if (mattk->adtyp != AD_SPEL && mattk->adtyp != AD_CLRC) {
+		impossible(
+	      "%s casting non-hand-to-hand version of hand-to-hand spell %d?",
+			   Monnam(mtmp), mattk->adtyp);
+		return(0);
+	    }
+	} else if (mattk->damd)
+	    dmg = d((int)((ml/2) + mattk->damn), (int)mattk->damd);
+	else dmg = d((int)((ml/2) + 1), 6);
 	if (Half_spell_damage) dmg = (dmg+1) / 2;
 
-	switch(mattk->adtyp)   {
+	ret = 1;
+
+	switch (mattk->adtyp) {
 
 	    case AD_FIRE:
 		pline("You're enveloped in flames.");
@@ -93,229 +292,459 @@
 			dmg = 0;
 		} else dmg = d((int)mtmp->m_lev/2 + 1,6);
 		break;
-	    case AD_SPEL:	/* random spell */
+	    case AD_SPEL:	/* wizard spell */
+	    case AD_CLRC:       /* clerical spell */
+	    {
+		if (mattk->adtyp == AD_SPEL)
+		    cast_wizard_spell(mtmp, dmg, spellnum);
+		else
+		    cast_cleric_spell(mtmp, dmg, spellnum);
+		dmg = 0; /* done by the spell casting functions */
+		break;
+	    }
+	}
+	if(dmg) mdamageu(mtmp, dmg);
+	return(ret);
+}
 
-		mtmp->mspec_used = 10 - mtmp->m_lev;
-		if (mtmp->mspec_used < 2) mtmp->mspec_used = 2;
-		switch(rn2((int)mtmp->m_lev)) {
-		    case 22:
-		    case 21:
-		    case 20:
-			pline("Oh no, %s's using the touch of death!",
-			      humanoid(mtmp->data)
-				  ? (mtmp->female ? "she" : "he")
-				  : "it"
-			     );
-			if (nonliving(youmonst.data) || is_demon(youmonst.data))
-			    You("seem no deader than before.");
-			else if (!Antimagic && rn2(ml) > 12) {
+/* monster wizard and cleric spellcasting functions */
+/*
+   If dmg is zero, then the monster is not casting at you.
+   If the monster is intentionally not casting at you, we have previously
+   called spell_would_be_useless() and spellnum should always be a valid
+   undirected spell.
+   If you modify either of these, be sure to change is_undirected_spell()
+   and spell_would_be_useless().
+ */
+STATIC_OVL
+void
+cast_wizard_spell(mtmp, dmg, spellnum)
+struct monst *mtmp;
+int dmg;
+int spellnum;
+{
+    if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) {
+	impossible("cast directed wizard spell (%d) with dmg=0?", spellnum);
+	return;
+    }
 
-			    if(Hallucination)
-				You("have an out of body experience.");
-			    else  {
-				killer_format = KILLED_BY_AN;
-				killer = "touch of death";
-				done(DIED);
-			    }
-			} else {
-				if(Antimagic) shieldeff(u.ux, u.uy);
-				pline("Lucky for you, it didn't work!");
-			}
-			dmg = 0;
-			break;
-		    case 19:
-		    case 18:
-			if(mtmp->iswiz && flags.no_of_wizards == 1) {
-				pline("Double Trouble...");
-				clonewiz();
-				dmg = 0;
-				break;
-			} /* else fall into the next case */
-		    case 17:
-		    case 16:
-		    case 15:
-			if(mtmp->iswiz)
-			    verbalize("Destroy the thief, my pets!");
-			nasty(mtmp);	/* summon something nasty */
-			/* fall into the next case */
-		    case 14:		/* aggravate all monsters */
-		    case 13:
-			aggravate();
-			dmg = 0;
-			break;
-		    case 12:		/* curse random items */
-		    case 11:
-		    case 10:
-			rndcurse();
-			dmg = 0;
-			break;
-		    case 9:
-		    case 8:		/* destroy armor */
-			if (Antimagic) {
-				shieldeff(u.ux, u.uy);
-				pline("A field of force surrounds you!");
-			} else if(!destroy_arm(some_armor(&youmonst)))
-				Your("skin itches.");
-			dmg = 0;
-			break;
-		    case 7:
-		    case 6:		/* drain strength */
-			if(Antimagic) {
-			    shieldeff(u.ux, u.uy);
-			    You_feel("momentarily weakened.");
-			} else {
-			    You("suddenly feel weaker!");
-			    dmg = ml - 6;
-			    if(Half_spell_damage) dmg = (dmg+1) / 2;
-			    losestr(rnd(dmg));
-			    if(u.uhp < 1)
-				done_in_by(mtmp);
-			}
-			dmg = 0;
-			break;
-		    case 5:		/* make invisible if not */
-		    case 4:
-			if (!mtmp->minvis && !mtmp->invis_blkd) {
-			    if(canseemon(mtmp) && !See_invisible)
-				pline("%s suddenly disappears!", Monnam(mtmp));
-			    mon_set_minvis(mtmp);
-			    dmg = 0;
-			    break;
-			} /* else fall into the next case */
-		    case 3:		/* stun */
-			if (Antimagic || Free_action) {
-			    shieldeff(u.ux, u.uy);
-			    if(!Stunned)
-				You_feel("momentarily disoriented.");
-			    make_stunned(1L, FALSE);
-			} else {
-			    if (Stunned)
-				You("struggle to keep your balance.");
-			    else
-				You("reel...");
-			    dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4);
-			    if(Half_spell_damage) dmg = (dmg+1) / 2;
-			    make_stunned(HStun + dmg, FALSE);
-			}
-			dmg = 0;
-			break;
-		    case 2:		/* haste self */
-			mon_adjust_speed(mtmp, 1);
-			dmg = 0;
-			break;
-		    case 1:		/* cure self */
-			if(mtmp->mhp < mtmp->mhpmax) {
-			    if((mtmp->mhp += rnd(8)) > mtmp->mhpmax)
-				mtmp->mhp = mtmp->mhpmax;
-			    dmg = 0;
-			    break;
-			} /* else fall through to default case */
-		    default:		/* psi bolt */
-			if(Antimagic) {
-			    shieldeff(u.ux, u.uy);
-			    You("get a slight %sache.",body_part(HEAD));
-			    dmg = 1;
-			} else {
-			    if (dmg <= 10)
-				Your("brain is on fire!");
-			    else Your("%s suddenly aches!", body_part(HEAD));
-			}
-			break;
-		}
+    switch (spellnum) {
+    case MGC_DEATH_TOUCH:
+	pline("Oh no, %s's using the touch of death!", mhe(mtmp));
+	if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
+	    You("seem no deader than before.");
+	} else if (!Antimagic && rn2(mtmp->m_lev) > 12) {
+	    if (Hallucination) {
+		You("have an out of body experience.");
+	    } else {
+		killer_format = KILLED_BY_AN;
+		killer = "touch of death";
+		done(DIED);
+	    }
+	} else {
+	    if (Antimagic) shieldeff(u.ux, u.uy);
+	    pline("Lucky for you, it didn't work!");
+	}
+	dmg = 0;
+	break;
+    case MGC_CLONE_WIZ:
+	if (mtmp->iswiz && flags.no_of_wizards == 1) {
+	    pline("Double Trouble...");
+	    clonewiz();
+	    dmg = 0;
+	} else
+	    impossible("bad wizard cloning?");
+	break;
+    case MGC_SUMMON_MONS:		/* also aggravates */
+    {
+	int count;
+
+	count = nasty(mtmp);	/* summon something nasty */
+	if (mtmp->iswiz)
+	    verbalize("Destroy the thief, my pet%s!", plur(count));
+	else {
+	    const char *mappear =
+		(count == 1) ? "A monster appears" : "Monsters appear";
+
+	    /* messages not quite right if plural monsters created but
+	       only a single monster is seen */
+	    if (Invisible && !perceives(mtmp->data) &&
+				    (mtmp->mux != u.ux || mtmp->muy != u.uy))
+		pline("%s around a spot near you!", mappear);
+	    else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
+		pline("%s around your displaced image!", mappear);
+	    else
+		pline("%s from nowhere!", mappear);
+	}
+	dmg = 0;
+	break;
+    }
+    case MGC_AGGRAVATION:
+	You_feel("that monsters are aware of your presence.");
+	aggravate();
+	dmg = 0;
+	break;
+    case MGC_CURSE_ITEMS:
+	You_feel("as if you need some help.");
+	rndcurse();
+	dmg = 0;
+	break;
+    case MGC_DESTRY_ARMR:
+	if (Antimagic) {
+	    shieldeff(u.ux, u.uy);
+	    pline("A field of force surrounds you!");
+	} else if (!destroy_arm(some_armor(&youmonst))) {
+	    Your("skin itches.");
+	}
+	dmg = 0;
+	break;
+    case MGC_WEAKEN_YOU:		/* drain strength */
+	if (Antimagic) {
+	    shieldeff(u.ux, u.uy);
+	    You_feel("momentarily weakened.");
+	} else {
+	    You("suddenly feel weaker!");
+	    dmg = mtmp->m_lev - 6;
+	    if (Half_spell_damage) dmg = (dmg + 1) / 2;
+	    losestr(rnd(dmg));
+	    if (u.uhp < 1)
+		done_in_by(mtmp);
+	}
+	dmg = 0;
+	break;
+    case MGC_DISAPPEAR:		/* makes self invisible */
+	if (!mtmp->minvis && !mtmp->invis_blkd) {
+	    if (canseemon(mtmp))
+		pline("%s suddenly %s!", Monnam(mtmp),
+		      !See_invisible ? "disappears" : "becomes transparent");
+	    mon_set_minvis(mtmp);
+	    dmg = 0;
+	} else
+	    impossible("no reason for monster to cast disappear spell?");
+	break;
+    case MGC_STUN_YOU:
+	if (Antimagic || Free_action) {
+	    shieldeff(u.ux, u.uy);
+	    if (!Stunned)
+		You_feel("momentarily disoriented.");
+	    make_stunned(1L, FALSE);
+	} else {
+	    You(Stunned ? "struggle to keep your balance." : "reel...");
+	    dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4);
+	    if (Half_spell_damage) dmg = (dmg + 1) / 2;
+	    make_stunned(HStun + dmg, FALSE);
+	}
+	dmg = 0;
+	break;
+    case MGC_HASTE_SELF:
+	mon_adjust_speed(mtmp, 1, (struct obj *)0);
+	dmg = 0;
+	break;
+    case MGC_CURE_SELF:
+	if (mtmp->mhp < mtmp->mhpmax) {
+	    if (canseemon(mtmp))
+		pline("%s looks better.", Monnam(mtmp));
+	    /* note: player healing does 6d4; this used to do 1d8 */
+	    if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax)
+		mtmp->mhp = mtmp->mhpmax;
+	    dmg = 0;
+	}
+	break;
+    case MGC_PSI_BOLT:
+	/* prior to 3.4.0 Antimagic was setting the damage to 1--this
+	   made the spell virtually harmless to players with magic res. */
+	if (Antimagic) {
+	    shieldeff(u.ux, u.uy);
+	    dmg = (dmg + 1) / 2;
+	}
+	if (dmg <= 5)
+	    You("get a slight %sache.", body_part(HEAD));
+	else if (dmg <= 10)
+	    Your("brain is on fire!");
+	else if (dmg <= 20)
+	    Your("%s suddenly aches painfully!", body_part(HEAD));
+	else
+	    Your("%s suddenly aches very painfully!", body_part(HEAD));
+	break;
+    default:
+	impossible("mcastu: invalid magic spell (%d)", spellnum);
+	dmg = 0;
+	break;
+    }
+
+    if (dmg) mdamageu(mtmp, dmg);
+}
+
+STATIC_OVL
+void
+cast_cleric_spell(mtmp, dmg, spellnum)
+struct monst *mtmp;
+int dmg;
+int spellnum;
+{
+    if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) {
+	impossible("cast directed cleric spell (%d) with dmg=0?", spellnum);
+	return;
+    }
+
+    switch (spellnum) {
+    case CLC_GEYSER:
+	/* this is physical damage, not magical damage */
+	pline("A sudden geyser slams into you from nowhere!");
+	dmg = d(8, 6);
+	if (Half_physical_damage) dmg = (dmg + 1) / 2;
+	break;
+    case CLC_FIRE_PILLAR:
+	pline("A pillar of fire strikes all around you!");
+	if (Fire_resistance) {
+	    shieldeff(u.ux, u.uy);
+	    dmg = 0;
+	} else
+	    dmg = d(8, 6);
+	if (Half_spell_damage) dmg = (dmg + 1) / 2;
+	burn_away_slime();
+	(void) burnarmor(&youmonst);
+	destroy_item(SCROLL_CLASS, AD_FIRE);
+	destroy_item(POTION_CLASS, AD_FIRE);
+	destroy_item(SPBOOK_CLASS, AD_FIRE);
+	(void) burn_floor_paper(u.ux, u.uy, TRUE, FALSE);
+	break;
+    case CLC_LIGHTNING:
+    {
+	boolean reflects;
+
+	pline("A bolt of lightning strikes down at you from above!");
+	reflects = ureflects("It bounces off your %s%s.", "");
+	if (reflects || Shock_resistance) {
+	    shieldeff(u.ux, u.uy);
+	    dmg = 0;
+	    if (reflects)
 		break;
+	} else
+	    dmg = d(8, 6);
+	if (Half_spell_damage) dmg = (dmg + 1) / 2;
+	destroy_item(WAND_CLASS, AD_ELEC);
+	destroy_item(RING_CLASS, AD_ELEC);
+	break;
+    }
+    case CLC_CURSE_ITEMS:
+	You_feel("as if you need some help.");
+	rndcurse();
+	dmg = 0;
+	break;
+    case CLC_INSECTS:
+      {
+	/* Try for insects, and if there are none
+	   left, go for (sticks to) snakes.  -3. */
+	struct permonst *pm = mkclass(S_ANT,0);
+	struct monst *mtmp2 = (struct monst *)0;
+	char let = (pm ? S_ANT : S_SNAKE);
+	boolean success;
+	int i;
+	coord bypos;
 
-	    case AD_CLRC:	/* clerical spell */
-		mtmp->mspec_used = 10 - mtmp->m_lev;
-		if (mtmp->mspec_used < 2) mtmp->mspec_used = 2;
-		switch(rn2((int)mtmp->m_lev)) {
-		    /* Other ideas: lightning bolts, towers of flame,
-				    gush of water  -3. */
+	success = pm ? TRUE : FALSE;
+	for (i = 0; i <= (int) mtmp->m_lev; i++) {
+	    if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data))
+		break;
+	    if ((pm = mkclass(let,0)) != 0 &&
+		    (mtmp2 = makemon(pm, bypos.x, bypos.y, NO_MM_FLAGS)) != 0) {
+		success = TRUE;
+		mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0;
+		set_malign(mtmp2);
+	    }
+	}
+	/* Not quite right:
+         * -- message doesn't always make sense for unseen caster (particularly
+	 *    the first message)
+         * -- message assumes plural monsters summoned (non-plural should be
+         *    very rare, unlike in nasty())
+         * -- message assumes plural monsters seen
+         */
+	if (!success)
+	    pline("%s casts at a clump of sticks, but nothing happens.",
+		Monnam(mtmp));
+	else if (let == S_SNAKE)
+	    pline("%s transforms a clump of sticks into snakes!",
+		Monnam(mtmp));
+	else if (Invisible && !perceives(mtmp->data) &&
+				(mtmp->mux != u.ux || mtmp->muy != u.uy))
+	    pline("%s summons insects around a spot near you!",
+		Monnam(mtmp));
+	else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
+	    pline("%s summons insects around your displaced image!",
+		Monnam(mtmp));
+	else
+	    pline("%s summons insects!", Monnam(mtmp));
+	dmg = 0;
+	break;
+      }
+    case CLC_BLIND_YOU:
+	/* note: resists_blnd() doesn't apply here */
+	if (!Blinded) {
+	    int num_eyes = eyecount(youmonst.data);
+	    pline("Scales cover your %s!",
+		  (num_eyes == 1) ?
+		  body_part(EYE) : makeplural(body_part(EYE)));
+	    make_blinded(Half_spell_damage ? 100L : 200L, FALSE);
+	    if (!Blind) Your(vision_clears);
+	    dmg = 0;
+	} else
+	    impossible("no reason for monster to cast blindness spell?");
+	break;
+    case CLC_PARALYZE:
+	if (Antimagic || Free_action) {
+	    shieldeff(u.ux, u.uy);
+	    if (multi >= 0)
+		You("stiffen briefly.");
+	    nomul(-1);
+	} else {
+	    if (multi >= 0)
+		You("are frozen in place!");
+	    dmg = 4 + (int)mtmp->m_lev;
+	    if (Half_spell_damage) dmg = (dmg + 1) / 2;
+	    nomul(-dmg);
+	}
+	dmg = 0;
+	break;
+    case CLC_CONFUSE_YOU:
+	if (Antimagic) {
+	    shieldeff(u.ux, u.uy);
+	    You_feel("momentarily dizzy.");
+	} else {
+	    boolean oldprop = !!Confusion;
 
-		    default:		/* confuse */
-			if(Antimagic) {
-			    shieldeff(u.ux, u.uy);
-			    You_feel("momentarily dizzy.");
-			} else {
-			    dmg = (int)mtmp->m_lev;
-			    if(Half_spell_damage) dmg = (dmg+1) / 2;
-			    make_confused(HConfusion + dmg, TRUE);
-			}
-			dmg = 0;
-			break;
-		    case 12:		/* curse random items */
-		    case 11:
-		    case 10:
-			rndcurse();
-			dmg = 0;
-			break;
-		    case 9:
-		    case 8:		/* insects */
-			/* Try for insects, and if there are none
-			   left, go for (sticks to) snakes.  -3. */
-			{
-			int i;
-			struct permonst *pm = mkclass(S_ANT,0);
-			struct monst *mtmp2;
-			char let = (pm ? S_ANT : S_SNAKE);
+	    dmg = (int)mtmp->m_lev;
+	    if (Half_spell_damage) dmg = (dmg + 1) / 2;
+	    make_confused(HConfusion + dmg, TRUE);
+	    if (Hallucination)
+		You_feel("%s!", oldprop ? "trippier" : "trippy");
+	    else
+		You_feel("%sconfused!", oldprop ? "more " : "");
+	}
+	dmg = 0;
+	break;
+    case CLC_CURE_SELF:
+	if (mtmp->mhp < mtmp->mhpmax) {
+	    if (canseemon(mtmp))
+		pline("%s looks better.", Monnam(mtmp));
+	    /* note: player healing does 6d4; this used to do 1d8 */
+	    if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax)
+		mtmp->mhp = mtmp->mhpmax;
+	    dmg = 0;
+	}
+	break;
+    case CLC_OPEN_WOUNDS:
+	if (Antimagic) {
+	    shieldeff(u.ux, u.uy);
+	    dmg = (dmg + 1) / 2;
+	}
+	if (dmg <= 5)
+	    Your("skin itches badly for a moment.");
+	else if (dmg <= 10)
+	    pline("Wounds appear on your body!");
+	else if (dmg <= 20)
+	    pline("Severe wounds appear on your body!");
+	else
+	    Your("body is covered with painful wounds!");
+	break;
+    default:
+	impossible("mcastu: invalid clerical spell (%d)", spellnum);
+	dmg = 0;
+	break;
+    }
 
-			for (i = 0; i <= (int) mtmp->m_lev; i++)
-			   if ((pm = mkclass(let,0)) &&
-			(mtmp2 = makemon(pm, u.ux, u.uy, NO_MM_FLAGS))) {
-				mtmp2->msleeping = mtmp2->mpeaceful =
-					mtmp2->mtame = 0;
-				set_malign(mtmp2);
-			    }
-			}
-			dmg = 0;
-			break;
-		    case 6:
-		    case 7:		/* blindness */
-			/* note: resists_blnd() doesn't apply here */
-			if (!Blinded) {
-			    pline("Scales cover your %s!", makeplural(body_part(EYE)));
-			    make_blinded(Half_spell_damage ? 100L:200L, FALSE);
-			    dmg = 0;
-			    break;
-			}
-		    case 4:
-		    case 5:		/* wound */
-			if(Antimagic) {
-			    shieldeff(u.ux, u.uy);
-			    Your("skin itches badly for a moment.");
-			    dmg = 0;
-			} else {
-			    pline("Wounds appear on your body!");
-			    dmg = d(2,8) + 1;
-			    if (Half_spell_damage) dmg = (dmg+1) / 2;
-			}
-			break;
-		    case 3:		/* hold */
-			if (Antimagic || Free_action) {
-			    shieldeff(u.ux, u.uy);
-			    if(multi >= 0)
-				You("stiffen briefly.");
-			    nomul(-1);
-			} else {
-			    if (multi >= 0)
-				You("are frozen in place!");
-			    dmg = 4 + (int)mtmp->m_lev;
-			    if (Half_spell_damage) dmg = (dmg+1) / 2;
-			    nomul(-dmg);
-			}
-			dmg = 0;
-			break;
-		    case 2:
-		    case 1:		/* cure self */
-			if(mtmp->mhp < mtmp->mhpmax) {
-			    if((mtmp->mhp += rnd(8)) > mtmp->mhpmax)
-				mtmp->mhp = mtmp->mhpmax;
-			    dmg = 0;
-			    break;
-			} /* else fall through to default case */
-		}
+    if (dmg) mdamageu(mtmp, dmg);
+}
+
+STATIC_DCL
+boolean
+is_undirected_spell(adtyp, spellnum)
+unsigned int adtyp;
+int spellnum;
+{
+    if (adtyp == AD_SPEL) {
+	switch (spellnum) {
+	case MGC_CLONE_WIZ:
+	case MGC_SUMMON_MONS:
+	case MGC_AGGRAVATION:
+	case MGC_DISAPPEAR:
+	case MGC_HASTE_SELF:
+	case MGC_CURE_SELF:
+	    return TRUE;
+	default:
+	    break;
 	}
-	if(dmg) mdamageu(mtmp, dmg);
-	return(1);
+    } else if (adtyp == AD_CLRC) {
+	switch (spellnum) {
+	case CLC_INSECTS:
+	case CLC_CURE_SELF:
+	    return TRUE;
+	default:
+	    break;
+	}
+    }
+    return FALSE;
+}
+
+/* Some spells are useless under some circumstances. */
+STATIC_DCL
+boolean
+spell_would_be_useless(mtmp, adtyp, spellnum)
+struct monst *mtmp;
+unsigned int adtyp;
+int spellnum;
+{
+    /* Some spells don't require the player to really be there and can be cast
+     * by the monster when you're invisible, yet still shouldn't be cast when
+     * the monster doesn't even think you're there.
+     * This check isn't quite right because it always uses your real position.
+     * We really want something like "if the monster could see mux, muy".
+     */
+    boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my);
+
+    if (adtyp == AD_SPEL) {
+	/* aggravate monsters, etc. won't be cast by peaceful monsters */
+	if (mtmp->mpeaceful && (spellnum == MGC_AGGRAVATION ||
+		spellnum == MGC_SUMMON_MONS || spellnum == MGC_CLONE_WIZ))
+	    return TRUE;
+	/* haste self when already fast */
+	if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF)
+	    return TRUE;
+	/* invisibility when already invisible */
+	if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR)
+	    return TRUE;
+	/* peaceful monster won't cast invisibility if you can't see invisible,
+	   same as when monsters drink potions of invisibility.  This doesn't
+	   really make a lot of sense, but lets the player avoid hitting
+	   peaceful monsters by mistake */
+	if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR)
+	    return TRUE;
+	/* healing when already healed */
+	if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF)
+	    return TRUE;
+	/* don't summon monsters if it doesn't think you're around */
+	if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS ||
+		(!mtmp->iswiz && spellnum == MGC_CLONE_WIZ)))
+	    return TRUE;
+	if ((!mtmp->iswiz || flags.no_of_wizards > 1)
+						&& spellnum == MGC_CLONE_WIZ)
+	    return TRUE;
+    } else if (adtyp == AD_CLRC) {
+	/* summon insects/sticks to snakes won't be cast by peaceful monsters */
+	if (mtmp->mpeaceful && spellnum == CLC_INSECTS)
+	    return TRUE;
+	/* healing when already healed */
+	if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF)
+	    return TRUE;
+	/* don't summon insects if it doesn't think you're around */
+	if (!mcouldseeu && spellnum == CLC_INSECTS)
+	    return TRUE;
+	/* blindness spell on blinded player */
+	if (Blinded && spellnum == CLC_BLIND_YOU)
+	    return TRUE;
+    }
+    return FALSE;
 }
 
 #endif /* OVLB */
@@ -329,8 +758,13 @@
 	register struct monst *mtmp;
 	register struct attack  *mattk;
 {
-	if(mtmp->mcan || mattk->adtyp > AD_SPC2) {
-	    cursetxt(mtmp);
+	/* don't print constant stream of curse messages for 'normal'
+	   spellcasting monsters at range */
+	if (mattk->adtyp > AD_SPC2)
+	    return(0);
+
+	if (mtmp->mcan) {
+	    cursetxt(mtmp, FALSE);
 	    return(0);
 	}
 	if(lined_up(mtmp) && rn2(3)) {
diff -Naurd ../nethack-3.3.1/src/mhitm.c ./src/mhitm.c
--- ../nethack-3.3.1/src/mhitm.c Thu Aug 3 21:00:42 2000
+++ ./src/mhitm.c Fri Mar 22 14:41:03 2002
@@ -189,7 +189,7 @@
 		    attk,	/* attack attempted this time */
 		    struck = 0,	/* hit at least once */
 		    res[NATTK];	/* results of all attacks */
-    struct attack   *mattk;
+    struct attack   *mattk, alt_attk;
     struct permonst *pa, *pd;
 
     if (!magr || !mdef) return(MM_MISS);		/* mike@genat */
@@ -233,7 +233,7 @@
     /* Now perform all attacks for the monster. */
     for (i = 0; i < NATTK; i++) {
 	res[i] = MM_MISS;
-	mattk = &(pa->mattk[i]);
+	mattk = getmattk(pa, i, res, &alt_attk);
 	otmp = (struct obj *)0;
 	attk = 1;
 	switch (mattk->aatyp) {
@@ -274,9 +274,22 @@
 		/* KMH -- don't accumulate to-hit bonuses */
 		if (otmp)
 		    tmp -= hitval(otmp, mdef);
-		if (strike)
+		if (strike) {
 		    res[i] = hitmm(magr, mdef, mattk);
-		else
+		    if((mdef->data == &mons[PM_BLACK_PUDDING] || mdef->data == &mons[PM_BROWN_PUDDING])
+		       && otmp && objects[otmp->otyp].oc_material == IRON
+		       && mdef->mhp > 1 && !mdef->mcan)
+		    {
+			if (clone_mon(mdef)) {
+			    if (vis) {
+				char buf[BUFSZ];
+
+				Strcpy(buf, Monnam(mdef));
+				pline("%s divides as %s hits it!", buf, mon_nam(magr));
+			    }
+			}
+		    }
+		} else
 		    missmm(magr, mdef, mattk);
 		break;
 
@@ -393,8 +406,8 @@
 			default:
 				Sprintf(buf,"%s hits", magr_name);
 		    }
+		    pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr));
 		}
-		pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr));
 	} else  noises(magr, mattk);
 	return(mdamagem(magr, mdef, mattk));
 }
@@ -412,10 +425,38 @@
 		pline("%s %s...", buf, mon_nam(mdef));
 	}
 
-	if (!mdef->mcansee || mdef->msleeping) {
+	if (magr->mcan || !magr->mcansee ||
+	    (magr->minvis && !perceives(mdef->data)) ||
+	    !mdef->mcansee || mdef->msleeping) {
 	    if(vis) pline("but nothing happens.");
 	    return(MM_MISS);
 	}
+	/* call mon_reflects 2x, first test, then, if visible, print message */
+	if (magr->data == &mons[PM_MEDUSA] && mon_reflects(mdef, (char *)0)) {
+	    if (canseemon(mdef))
+		(void) mon_reflects(mdef,
+				    "The gaze is reflected away by %s %s.");
+	    if (mdef->mcansee) {
+		if (mon_reflects(magr, (char *)0)) {
+		    if (canseemon(magr))
+			(void) mon_reflects(magr,
+					"The gaze is reflected away by %s %s.");
+		    return (MM_MISS);
+		}
+		if (mdef->minvis && !perceives(magr->data)) {
+		    if (canseemon(magr)) {
+			pline("%s doesn't seem to notice that %s gaze was reflected.",
+			      Monnam(magr), mhis(magr));
+		    }
+		    return (MM_MISS);
+		}
+		if (canseemon(magr))
+		    pline("%s is turned to stone!", Monnam(magr));
+		monstone(magr);
+		if (magr->mhp > 0) return (MM_MISS);
+		return (MM_AGR_DIED);
+	    }
+	}
 
 	return(mdamagem(magr, mdef, mattk));
 }
@@ -526,11 +567,15 @@
 	int	tmp = d((int)mattk->damn,(int)mattk->damd);
 	struct obj *obj;
 	char buf[BUFSZ];
+	int protector =
+	    mattk->aatyp == AT_TENT ? 0 :
+	    mattk->aatyp == AT_KICK ? W_ARMF : W_ARMG;
+	int num;
 
 	if (touch_petrifies(pd) && !resists_ston(magr) &&
 	   (mattk->aatyp != AT_WEAP || !otmp) &&
 	   (mattk->aatyp != AT_GAZE && mattk->aatyp != AT_EXPL) &&
-	   !(magr->misc_worn_check & W_ARMG)) {
+	   !(magr->misc_worn_check & protector)) {
 		if (poly_when_stoned(pa)) {
 		    mon_to_stone(magr);
 		    return MM_HIT; /* no damage during the polymorph */
@@ -564,10 +609,37 @@
 		tmp = mdef->mhp;
 		/* Use up amulet of life saving */
 		if (!!(obj = mlifesaver(mdef))) m_useup(mdef, obj);
+
+		/* Is a corpse for nutrition possible?  It may kill magr */
+		if (!corpse_chance(mdef, magr, TRUE) || magr->mhp < 1)
+		    break;
+
+		/* Pets get nutrition from swallowing monster whole.
+		 * No nutrition from G_NOCORPSE monster, eg, undead.
+		 * DGST monsters don't die from undead corpses
+		 */
+		num = monsndx(mdef->data);
+		if (magr->mtame && !magr->isminion &&
+		    !(mvitals[num].mvflags & G_NOCORPSE)) {
+		    struct obj *virtualcorpse = mksobj(CORPSE, FALSE, FALSE);
+		    int nutrit;
+
+		    virtualcorpse->corpsenm = num;
+		    virtualcorpse->owt = weight(virtualcorpse);
+		    nutrit = dog_nutrition(magr, virtualcorpse);
+		    dealloc_obj(virtualcorpse);
+
+		    /* only 50% nutrition, 25% of normal eating time */
+		    if (magr->meating > 1) magr->meating = (magr->meating+3)/4;
+		    if (nutrit > 1) nutrit /= 2;
+		    EDOG(magr)->hungrytime += nutrit;
+		}
 		break;
 	    case AD_STUN:
 		if (magr->mcan) break;
-		if(vis) pline("%s staggers for a moment.", Monnam(mdef));
+		if (canseemon(mdef))
+		    pline("%s %s for a moment.", Monnam(mdef),
+			  makeplural(stagger(mdef->data, "stagger")));
 		mdef->mstun = 1;
 		/* fall through */
 	    case AD_WERE:
@@ -607,6 +679,7 @@
 		}
 		if (vis)
 		    pline("%s is %s!", Monnam(mdef),
+			  mdef->data == &mons[PM_WATER_ELEMENTAL] ? "boiling" :
 			  mattk->aatyp == AT_HUGS ?
 				"being roasted" : "on fire");
 		if (pd == &mons[PM_STRAW_GOLEM] ||
@@ -679,7 +752,7 @@
 		    pline("It burns %s!", mon_nam(mdef));
 		}
 		if (!rn2(30)) erode_armor(mdef, TRUE);
-		if (!rn2(6)) erode_weapon(MON_WEP(mdef), TRUE);
+		if (!rn2(6)) erode_obj(MON_WEP(mdef), TRUE, TRUE);
 		break;
 	    case AD_RUST:
 		if (!magr->mcan && pd == &mons[PM_IRON_GOLEM]) {
@@ -694,8 +767,8 @@
 		hurtmarmor(mdef, AD_RUST);
 		tmp = 0;
 		break;
-	    case AD_CORRODE:
-		hurtmarmor(mdef, AD_CORRODE);
+	    case AD_CORR:
+		hurtmarmor(mdef, AD_CORR);
 		tmp = 0;
 		break;
 	    case AD_DCAY:
@@ -771,7 +844,7 @@
 		if (!magr->mcan && vis && mdef->mspeed != MSLOW) {
 		    unsigned int oldspeed = mdef->mspeed;
 
-		    mon_adjust_speed(mdef, -1);
+		    mon_adjust_speed(mdef, -1, (struct obj *)0);
 		    if (mdef->mspeed != oldspeed && vis)
 			pline("%s slows down.", Monnam(mdef));
 		}
@@ -867,14 +940,20 @@
 #endif
 	    case AD_SITM:	/* for now these are the same */
 	    case AD_SEDU:
-		if (!magr->mcan && mdef->minvent) {
+		/* find an object to steal, non-cursed if magr is tame */
+		for (obj = mdef->minvent; obj; obj = obj->nobj) {
+		    if (!magr->mtame || !obj->cursed)
+			break;
+		}
+
+		if (!magr->mcan && obj) {
 			char onambuf[BUFSZ], mdefnambuf[BUFSZ];
 
 			/* make a special x_monnam() call that never omits
 			   the saddle, and save it for later messages */
 			Strcpy(mdefnambuf, x_monnam(mdef, ARTICLE_THE, (char *)0, 0, FALSE));
 
-			otmp = mdef->minvent;
+			otmp = obj;
 #ifdef STEED
 			if (u.usteed == mdef &&
 					otmp == which_armor(mdef, W_SADDLE))
@@ -884,6 +963,8 @@
 			obj_extract_self(otmp);
 			if (otmp->owornmask) {
 				mdef->misc_worn_check &= ~otmp->owornmask;
+				if (otmp->owornmask & W_WEP)
+				    setmnotwielded(mdef,otmp);
 				otmp->owornmask = 0L;
 				update_mon_intrinsics(mdef, otmp, FALSE);
 			}
@@ -933,6 +1014,7 @@
 	    case AD_DRIN:
 		if (notonhead || !has_head(pd)) {
 		    if (vis) pline("%s doesn't seem harmed.", Monnam(mdef));
+		    /* Not clear what to do for green slimes */
 		    tmp = 0;
 		    break;
 		}
@@ -941,7 +1023,7 @@
 			Strcpy(buf, s_suffix(Monnam(mdef)));
 			pline("%s helmet blocks %s attack to %s head.",
 				buf, s_suffix(mon_nam(magr)),
-				his[pronoun_gender(mdef)]);
+				mhis(mdef));
 		    }
 		    break;
 		}
@@ -962,9 +1044,10 @@
 	    case AD_SLIM:
 	    	if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
 	    			mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
+	    			mdef->data != &mons[PM_SALAMANDER] &&
 	    			mdef->data != &mons[PM_GREEN_SLIME]) {
 	    	    if (vis) pline("%s turns into slime.", Monnam(mdef));
-	    	    (void) newcham(mdef, &mons[PM_GREEN_SLIME]);
+	    	    (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE);
 	    	    tmp = 0;
 	    	}
 	    	break;
@@ -1054,7 +1137,7 @@
 
 	if (!magr || !mdef || !obj) return; /* just in case */
 
-	if (dmgtype(mdef->data, AD_CORRODE))
+	if (dmgtype(mdef->data, AD_CORR))
 	    is_acid = TRUE;
 	else if (dmgtype(mdef->data, AD_RUST))
 	    is_acid = FALSE;
@@ -1088,11 +1171,11 @@
 register struct obj *otemp;
 {
 	char buf[BUFSZ];
+	if (!flags.verbose || Blind || !mon_visible(magr)) return;
 	Strcpy(buf, mon_nam(mdef));
-	if (!flags.verbose || Blind) return;
 	pline("%s %s %s %s at %s.", Monnam(magr),
 	      (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings",
-	      his[pronoun_gender(magr)], xname(otemp), buf);
+	      mhis(magr), singular(otemp, xname), buf);
 }
 
 /*
@@ -1198,7 +1281,8 @@
 		if (!magr->mstun) {
 		    magr->mstun = 1;
 		    if (canseemon(magr))
-			pline("%s staggers...", Monnam(magr));
+			pline("%s %s...", Monnam(magr),
+			      makeplural(stagger(magr->data, "stagger")));
 		}
 		tmp = 0;
 		break;
diff -Naurd ../nethack-3.3.1/src/mhitu.c ./src/mhitu.c
--- ../nethack-3.3.1/src/mhitu.c Sat Aug 5 00:37:50 2000
+++ ./src/mhitu.c Fri Mar 22 14:41:03 2002
@@ -99,6 +99,7 @@
 	    else
 		pline("%s just misses!", Monnam(mtmp));
 	}
+	stop_occupation();
 }
 
 STATIC_OVL void
@@ -110,7 +111,7 @@
 		return;
 	pline("%s %s %s %s.", Monnam(mtmp),
 	      (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings",
-	      his[pronoun_gender(mtmp)], xname(otemp));
+	      mhis(mtmp), singular(otemp, xname));
 }
 
 /* return how a poison attack was delivered */
@@ -265,6 +266,30 @@
 #endif /* OVLB */
 #ifdef OVL0
 
+/* select a monster's next attack, possibly substituting for its usual one */
+struct attack *
+getmattk(mptr, indx, prev_result, alt_attk_buf)
+struct permonst *mptr;
+int indx, prev_result[];
+struct attack *alt_attk_buf;
+{
+    struct attack *attk = &mptr->mattk[indx];
+
+    /* prevent a monster with two consecutive disease or hunger attacks
+       from hitting with both of them on the same turn; if the first has
+       already hit, switch to a stun attack for the second */
+    if (indx > 0 && prev_result[indx - 1] > 0 &&
+	    (attk->adtyp == AD_DISE ||
+		attk->adtyp == AD_PEST ||
+		attk->adtyp == AD_FAMN) &&
+	    attk->adtyp == mptr->mattk[indx - 1].adtyp) {
+	*alt_attk_buf = *attk;
+	attk = alt_attk_buf;
+	attk->adtyp = AD_STUN;
+    }
+    return attk;
+}
+
 /*
  * mattacku: monster attacks you
  *	returns 1 if monster dies (e.g. "yellow light"), 0 otherwise
@@ -278,7 +303,7 @@
 mattacku(mtmp)
 	register struct monst *mtmp;
 {
-	struct	attack	*mattk;
+	struct	attack	*mattk, alt_attk;
 	int	i, j, tmp, sum[NATTK];
 	struct	permonst *mdat = mtmp->data;
 	boolean ranged = (distu(mtmp->mx, mtmp->my) > 3);
@@ -319,7 +344,7 @@
 			i = mattackm(mtmp, u.usteed);
 			if ((i & MM_AGR_DIED))
 				return (1);
-			if (i & MM_DEF_DIED || u.ux != u.ux0 || u.uy != u.uy0)
+			if (i & MM_DEF_DIED || u.umoved)
 				return (0);
 			/* Let your steed retaliate */
 			return (!!(mattackm(u.usteed, mtmp) & MM_DEF_DIED));
@@ -343,7 +368,7 @@
 			newsym(u.ux,u.uy);
 		    } else {
 			pline("%s is killed by a falling %s (you)!",
-						Monnam(mtmp), youmonst.data->mname);
+					Monnam(mtmp), youmonst.data->mname);
 			killed(mtmp);
 			newsym(u.ux,u.uy);
 			if (mtmp->mhp > 0) return 0;
@@ -399,7 +424,8 @@
 		}
 		return(0);
 	}
-	if (youmonst.data->mlet == S_MIMIC && youmonst.m_ap_type && !range2 && foundyou && !u.uswallow) {
+	if (youmonst.data->mlet == S_MIMIC && youmonst.m_ap_type &&
+		    !range2 && foundyou && !u.uswallow) {
 		if (!youseeit) pline("It gets stuck on you.");
 		else pline("Wait, %s!  That's a %s named %s!",
 			   m_monnam(mtmp), youmonst.data->mname, plname);
@@ -475,11 +501,11 @@
 	}
 
 	if(u.uinvulnerable) {
-	    /* monster's won't attack you */
+	    /* monsters won't attack you */
 	    if(mtmp == u.ustuck)
 		pline("%s loosens its grip slightly.", Monnam(mtmp));
 	    else if(!range2) {
-		if(youseeit)
+		if (youseeit || sensemon(mtmp))
 		    pline("%s starts to attack you, but pulls back.",
 			  Monnam(mtmp));
 		else
@@ -498,7 +524,7 @@
 	for(i = 0; i < NATTK; i++) {
 
 	    sum[i] = 0;
-	    mattk = &(mdat->mattk[i]);
+	    mattk = getmattk(mdat, i, sum, &alt_attk);
 	    if (u.uswallow && (mattk->aatyp != AT_ENGL))
 		continue;
 	    switch(mattk->aatyp) {
@@ -554,18 +580,17 @@
 				} else {
 				    missmu(mtmp, (tmp == j), mattk);
 				}
-			   } else if (is_animal(mtmp->data))
-					pline("%s gulps some air!", youseeit ?
-					      Monnam(mtmp) : "It");
-				  else
-					if (youseeit)
-					 pline("%s lunges forward and recoils!",
-					       Monnam(mtmp));
-					else
-						You_hear("a %s nearby.",
-						    is_whirly(mtmp->data)?
-						    "rushing noise" :
-						    "splat");
+			    } else if (is_animal(mtmp->data)) {
+				pline("%s gulps some air!", Monnam(mtmp));
+			    } else {
+				if (youseeit)
+				    pline("%s lunges forward and recoils!",
+					  Monnam(mtmp));
+				else
+				    You_hear("a %s nearby.",
+					     is_whirly(mtmp->data) ?
+						"rushing noise" : "splat");
+			   }
 			}
 			break;
 		case AT_BREA:
@@ -597,7 +622,6 @@
 				if (mon_wield_item(mtmp) != 0) break;
 			    }
 			    if (foundyou) {
-				possibly_unwield(mtmp);
 				otmp = MON_WEP(mtmp);
 				if(otmp) {
 				    hittmp = hitval(otmp, &youmonst);
@@ -618,20 +642,12 @@
 		case AT_MAGC:
 			if (range2)
 			    sum[i] = buzzmu(mtmp, mattk);
-			else
+			else {
 			    if (foundyou)
-				sum[i] = castmu(mtmp, mattk);
+				sum[i] = castmu(mtmp, mattk, TRUE, TRUE);
 			    else
-				pline("%s casts a spell at %s!",
-					youseeit ? Monnam(mtmp) : "It",
-					levl[mtmp->mux][mtmp->muy].typ == WATER
-					    ? "empty water" : "thin air");
-				/* FIXME: castmu includes spells that are not
-				 * cast at the player and thus should be
-				 * possible whether the monster knows your
-				 * location or not.
-				 * --KAA
-				 */
+				sum[i] = castmu(mtmp, mattk, TRUE, FALSE);
+			}
 			break;
 
 		default:		/* no attack */
@@ -668,7 +684,7 @@
 	switch(attk) {
 	    /* 0 is burning, which we should never be called with */
 	    case AD_RUST: hurt = 1; break;
-	    case AD_CORRODE: hurt = 3; break;
+	    case AD_CORR: hurt = 3; break;
 	    default: hurt = 2; break;
 	}
 
@@ -760,7 +776,7 @@
 		  /* avoid "slippery slippery cloak"
 		     for undiscovered oilskin cloak */
 		  (obj->greased || objects[obj->otyp].oc_name_known) ?
-			xname(obj) : "cloak");
+			xname(obj) : cloak_simple_name(obj));
 
 	    if (obj->greased && !rn2(2)) {
 		pline_The("grease wears off.");
@@ -784,7 +800,7 @@
 {
 	register struct permonst *mdat = mtmp->data;
 	register int uncancelled, ptmp;
-	int dmg, armpro;
+	int dmg, armpro, permdmg;
 	char	 buf[BUFSZ];
 	struct permonst *olduasmon = youmonst.data;
 	int res;
@@ -833,6 +849,7 @@
 		armpro = objects[uarmh->otyp].a_can;
 	uncancelled = !mtmp->mcan && ((rn2(3) >= armpro) || !rn2(50));
 
+	permdmg = 0;
 /*	Now, adjust damages via resistances or specific attacks */
 	switch(mattk->adtyp) {
 	    case AD_PHYS:
@@ -867,6 +884,7 @@
 			     hitmsg(mtmp, mattk);
 			if (!dmg) break;
 			if (u.mh > 1 && u.mh > ((u.uac>0) ? dmg : dmg+u.uac) &&
+				   objects[otmp->otyp].oc_material == IRON &&
 					(u.umonnum==PM_BLACK_PUDDING
 					|| u.umonnum==PM_BROWN_PUDDING)) {
 			    /* This redundancy necessary because you have to
@@ -895,6 +913,8 @@
 		hitmsg(mtmp, mattk);
 		if (uncancelled) {
 		    pline("You're %s!",
+			  youmonst.data == &mons[PM_WATER_ELEMENTAL] ?
+				"boiling" :
 			  mattk->aatyp == AT_HUGS ? "being roasted" :
 			  "on fire");
 		    if (youmonst.data == &mons[PM_STRAW_GOLEM] ||
@@ -955,6 +975,7 @@
 		if (can_blnd(mtmp, &youmonst, mattk->aatyp, (struct obj*)0)) {
 		    if (!Blind) pline("%s blinds you!", Monnam(mtmp));
 		    make_blinded(Blinded+(long)dmg,FALSE);
+		    if (!Blind) Your(vision_clears);
 		}
 		dmg = 0;
 		break;
@@ -981,6 +1002,7 @@
 		hitmsg(mtmp, mattk);
 		if (defends(AD_DRIN, uwep) || !has_head(youmonst.data)) {
 		    You("don't seem harmed.");
+		    /* Not clear what to do for green slimes */
 		    break;
 		}
 		if (u_slip_free(mtmp,mattk)) break;
@@ -1094,7 +1116,7 @@
 		}
 	    case AD_STON:	/* cockatrice */
 		hitmsg(mtmp, mattk);
-		if(!rn2(3) && !Stoned) {
+		if(!rn2(3)) {
 		    if (mtmp->mcan) {
 			if (flags.soundok)
 			    You_hear("a cough from %s!", mon_nam(mtmp));
@@ -1104,7 +1126,7 @@
 			if(!rn2(10) ||
 			    (flags.moonphase == NEW_MOON && !have_lizard())) {
 do_stone:
-			    if (!Stone_resistance
+			    if (!Stoned && !Stone_resistance
 				    && !(poly_when_stoned(youmonst.data) &&
 					polymon(PM_STONE_GOLEM))) {
 				Stoned = 5;
@@ -1203,7 +1225,8 @@
 		    }
 		    break;
 		}
-		switch (steal(mtmp)) {
+		buf[0] = '\0';
+		switch (steal(mtmp, buf)) {
 		  case -1:
 			return 2;
 		  case 0:
@@ -1211,7 +1234,19 @@
 		  default:
 			if (!is_animal(mtmp->data) && !tele_restrict(mtmp))
 			    rloc(mtmp);
-			mtmp->mflee = 1;
+			if (is_animal(mtmp->data) && *buf) {
+			    /* set mavenge bit for animals so knights won't
+			       suffer an alignment penalty during retaliation;
+			       note that only happens when the thief succeeds
+			       in getting something (*buf != 0) */
+			    mtmp->mavenge = 1;
+			    if (canseemon(mtmp))
+				pline("%s tries to %s away with %s.",
+				      Monnam(mtmp),
+				      locomotion(mtmp->data, "run"),
+				      buf);
+			}
+			monflee(mtmp, 0, FALSE, FALSE);
 			return 3;
 		}
 		break;
@@ -1251,10 +1286,10 @@
 		}
 		hurtarmor(AD_RUST);
 		break;
-	    case AD_CORRODE:
+	    case AD_CORR:
 		hitmsg(mtmp, mattk);
 		if (mtmp->mcan) break;
-		hurtarmor(AD_CORRODE);
+		hurtarmor(AD_CORR);
 		break;
 	    case AD_DCAY:
 		hitmsg(mtmp, mattk);
@@ -1303,10 +1338,7 @@
 			return 2;
 		    } else if (!rn2(33)) {
 			if (!tele_restrict(mtmp)) rloc(mtmp);
-			if (!mtmp->mflee) {
-			    mtmp->mflee = 1;
-			    mtmp->mfleetim = d(3,6);
-			}
+			monflee(mtmp, d(3, 6), TRUE, FALSE);
 			return 3;
 		    }
 		    dmg = 0;
@@ -1375,7 +1407,8 @@
 		    else You("are getting confused.");
 		    make_confused(HConfusion + dmg, FALSE);
 		}
-		/* fall through to next case */
+		dmg = 0;
+		break;
 	    case AD_DETH:
 		pline("%s reaches out with its deadly touch.", Monnam(mtmp));
 		if (is_undead(youmonst.data)) {
@@ -1383,16 +1416,24 @@
 		    pline("Was that the touch of death?");
 		    break;
 		}
-		if(!Antimagic && rn2(20) > 16)  {
-		    killer_format = KILLED_BY_AN;
-		    killer = "touch of death";
-		    done(DIED);
-		} else {
-		    if(!rn2(5)) {
-			if(Antimagic) shieldeff(u.ux, u.uy);
-			pline("Lucky for you, it didn't work!");
+		switch (rn2(20)) {
+		case 19: case 18: case 17:
+		    if (!Antimagic) {
+			killer_format = KILLED_BY_AN;
+			killer = "touch of death";
+			done(DIED);
 			dmg = 0;
-		    } else You_feel("your life force draining away...");
+			break;
+		    } /* else FALLTHRU */
+		default: /* case 16: ... case 5: */
+		    You_feel("your life force draining away...");
+		    permdmg = 1;	/* actual damage done below */
+		    break;
+		case 4: case 3: case 2: case 1: case 0:
+		    if (Antimagic) shieldeff(u.ux, u.uy);
+		    pline("Lucky for you, it didn't work!");
+		    dmg = 0;
+		    break;
 		}
 		break;
 	    case AD_PEST:
@@ -1408,36 +1449,38 @@
 		/* plus the normal damage */
 		break;
 	    case AD_SLIM:    
-	    	hitmsg(mtmp, mattk);
-	    	if (!uncancelled) break;
-	    	if (youmonst.data == &mons[PM_FIRE_VORTEX] ||
-	    			youmonst.data == &mons[PM_FIRE_ELEMENTAL]) {
-	    	    pline_The("slime burns away!");
-	    	    dmg = 0;
-	    	} else if (Unchanging ||
-	    			youmonst.data == &mons[PM_GREEN_SLIME]) {
-	    	    You("are unaffected.");
-	    	    dmg = 0;
-	    	} else if (!Slimed) {
-	    	    You("don't feel very well.");
-	    	    Slimed = 10L;
+		hitmsg(mtmp, mattk);
+		if (!uncancelled) break;
+		if (youmonst.data == &mons[PM_FIRE_VORTEX] ||
+				youmonst.data == &mons[PM_SALAMANDER] ||
+				youmonst.data == &mons[PM_FIRE_ELEMENTAL]) {
+		    pline_The("slime burns away!");
+		    dmg = 0;
+		} else if (Unchanging ||
+				youmonst.data == &mons[PM_GREEN_SLIME]) {
+		    You("are unaffected.");
+		    dmg = 0;
+		} else if (!Slimed) {
+		    You("don't feel very well.");
+		    Slimed = 10L;
+		    flags.botl = 1;
 		    killer_format = KILLED_BY_AN;
 		    delayed_killer = mtmp->data->mname;
-	    	} else
-	    	    pline("Yuck!");
-	    	break;
+		} else
+		    pline("Yuck!");
+		break;
 	    case AD_ENCH:	/* KMH -- remove enchantment (disenchanter) */
-	    	hitmsg(mtmp, mattk);
-	    	/* uncancelled is sufficient enough; please
-	    	   don't make this attack less frequent */
-	    	if (uncancelled) {
-	    		struct obj *obj = some_armor(&youmonst);
+		hitmsg(mtmp, mattk);
+		/* uncancelled is sufficient enough; please
+		   don't make this attack less frequent */
+		if (uncancelled) {
+		    struct obj *obj = some_armor(&youmonst);
 
-	    		if (drain_item(obj)) {
-	    			Your("%s less effective.", aobjnam(obj, "seem"));
-	    		}
-	    	}
-	    	break;
+		    if (drain_item(obj)) {
+			Your("%s less effective.", aobjnam(obj, "seem"));
+		    }
+		}
+		break;
 	    default:	dmg = 0;
 			break;
 	}
@@ -1457,15 +1500,49 @@
 		|| (Role_if(PM_PRIEST) && uarmh && is_quest_artifact(uarmh) &&
 		    (is_undead(mtmp->data) || is_demon(mtmp->data))))
 		dmg = (dmg+1) / 2;
+
+	    if (permdmg) {	/* Death's life force drain */
+		int lowerlimit, *hpmax_p;
+		/*
+		 * Apply some of the damage to permanent hit points:
+		 *	polymorphed	    100% against poly'd hpmax
+		 *	hpmax > 25*lvl	    100% against normal hpmax
+		 *	hpmax > 10*lvl	50..100%
+		 *	hpmax >  5*lvl	25..75%
+		 *	otherwise	 0..50%
+		 * Never reduces hpmax below 1 hit point per level.
+		 */
+		permdmg = rn2(dmg / 2 + 1);
+		if (Upolyd || u.uhpmax > 25 * u.ulevel) permdmg = dmg;
+		else if (u.uhpmax > 10 * u.ulevel) permdmg += dmg / 2;
+		else if (u.uhpmax > 5 * u.ulevel) permdmg += dmg / 4;
+
+		if (Upolyd) {
+		    hpmax_p = &u.mhmax;
+		    /* [can't use youmonst.m_lev] */
+		    lowerlimit = min((int)youmonst.data->mlevel, u.ulevel);
+		} else {
+		    hpmax_p = &u.uhpmax;
+		    lowerlimit = u.ulevel;
+		}
+		if (*hpmax_p - permdmg > lowerlimit)
+		    *hpmax_p -= permdmg;
+		else if (*hpmax_p > lowerlimit)
+		    *hpmax_p = lowerlimit;
+		else	/* unlikely... */
+		    ;	/* already at or below minimum threshold; do nothing */
+		flags.botl = 1;
+	    }
+
 	    mdamageu(mtmp, dmg);
 	}
 
-	if (dmg) {
+	if (dmg)
 	    res = passiveum(olduasmon, mtmp, mattk);
-	    stop_occupation();
-	    return res;
-	} else
-	    return 1;
+	else
+	    res = 1;
+	stop_occupation();
+	return res;
 }
 
 #endif /* OVL1 */
@@ -1519,10 +1596,9 @@
 
 		i = number_leashed();
 		if (i > 0) {
-			pline_The("leash%s snap%s loose.",
-					(i > 1) ? "es" : "",
-					(i > 1) ? "" : "s");
-			unleash_all();
+		    char *s = (i > 1) ? "leashes" : "leash";
+		    pline_The("%s %s loose.", s, vtense(s, "snap"));
+		    unleash_all();
 		}
 
 		if (touch_petrifies(youmonst.data) && !resists_ston(mtmp)) {
@@ -1585,6 +1661,7 @@
 			if(!Blind) {
 			    You_cant("see in here!");
 			    make_blinded((long)tmp,FALSE);
+			    if (!Blind) Your(vision_clears);
 			} else
 			    /* keep him blind until disgorged */
 			    make_blinded(Blinded+1,FALSE);
@@ -1700,6 +1777,7 @@
 		    if (mon_visible(mtmp) || (rnd(tmp /= 2) > u.ulevel)) {
 			You("are blinded by a blast of light!");
 			make_blinded((long)tmp, FALSE);
+			if (!Blind) Your(vision_clears);
 		    } else if (flags.verbose)
 			You("get the impression it was not terribly bright.");
 		}
@@ -1726,6 +1804,7 @@
 	}
     }
     mondead(mtmp);
+    wake_nearto(mtmp->mx, mtmp->my, 7*7);
     if (mtmp->mhp > 0) return(0);
     return(2);	/* it dies */
 }
@@ -1737,13 +1816,20 @@
 {
 	switch(mattk->adtyp) {
 	    case AD_STON:
-		if (mtmp->mcan) {
-		    if (mtmp->data == &mons[PM_MEDUSA] && canseemon(mtmp))
-			pline("%s doesn't look all that ugly.", Monnam(mtmp));
+		if (mtmp->mcan || !mtmp->mcansee) {
+		    if (mtmp->data == &mons[PM_MEDUSA] && canseemon(mtmp)) {
+			if (mtmp->mcan) {
+			    pline("%s doesn't look all that ugly.",
+				  Monnam(mtmp));
+			    break;
+			}
+		    }
+		    if (canseemon(mtmp))
+			pline("%s gazes ineffectually.", Monnam(mtmp));
 		    break;
 		}
-		if(Reflecting && mtmp->mcansee &&
-		   !mtmp->mcan && mtmp->data == &mons[PM_MEDUSA]) {
+		if (Reflecting && canspotmon(mtmp) &&
+		    mtmp->data == &mons[PM_MEDUSA]) {
 		    if(!Blind) {
 		    	(void) ureflects("%s gaze is reflected by your %s.",
 		    			s_suffix(Monnam(mtmp)));
@@ -1753,7 +1839,7 @@
 			if (!m_canseeu(mtmp)) { /* probably you're invisible */
 			    pline("%s doesn't seem to notice that %s gaze was reflected.",
 				Monnam(mtmp),
-				his[pronoun_gender(mtmp)]);
+				mhis(mtmp));
 			    break;
 			}
 			pline("%s is turned to stone!", Monnam(mtmp));
@@ -1764,19 +1850,22 @@
 		    if (mtmp->mhp > 0) break;
 		    return 2;
 		}
-		if (canseemon(mtmp) && !Stone_resistance) {
+		if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) &&
+		    !Stone_resistance) {
 		    You("meet %s gaze.", s_suffix(mon_nam(mtmp)));
+		    stop_occupation();
 		    if(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
 			break;
 		    You("turn to stone...");
 		    killer_format = KILLED_BY;
-		    killer = mons[PM_MEDUSA].mname;
+		    killer = mtmp->data->mname;
 		    done(STONING);
 		}
 		break;
 	    case AD_CONF:
-		if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
-					!mtmp->mspec_used && rn2(5)) {
+		if(!mtmp->mcan && canseemon(mtmp) &&
+		   couldsee(mtmp->mx, mtmp->my) &&
+		   mtmp->mcansee && !mtmp->mspec_used && rn2(5)) {
 		    int conf = d(3,4);
 
 		    mtmp->mspec_used = mtmp->mspec_used + (conf + rn2(6));
@@ -1786,52 +1875,65 @@
 		    else
 			You("are getting more and more confused.");
 		    make_confused(HConfusion + conf, FALSE);
+		    stop_occupation();
 		}
 		break;
 	    case AD_STUN:
-		if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
-					!mtmp->mspec_used && rn2(5)) {
+		if(!mtmp->mcan && canseemon(mtmp) &&
+		   couldsee(mtmp->mx, mtmp->my) &&
+		   mtmp->mcansee && !mtmp->mspec_used && rn2(5)) {
 		    int stun = d(2,6);
 
 		    mtmp->mspec_used = mtmp->mspec_used + (stun + rn2(6));
 		    make_stunned(HStun + stun, TRUE);
 		    pline("%s stares piercingly at you!", Monnam(mtmp));
+		    stop_occupation();
 		}
 		break;
 	    case AD_BLND:
 		if (!mtmp->mcan && canseemon(mtmp) && !resists_blnd(&youmonst)
 			&& distu(mtmp->mx,mtmp->my) <= BOLT_LIM*BOLT_LIM) {
 		    int blnd = d((int)mattk->damn, (int)mattk->damd);
-		    make_blinded((long)blnd,FALSE);
-		    make_stunned((long)d(1,3),TRUE);
+
 		    You("are blinded by %s radiance!",
 			              s_suffix(mon_nam(mtmp)));
+		    make_blinded((long)blnd,FALSE);
+		    stop_occupation();
+		    /* not blind at this point implies you're wearing
+		       the Eyes of the Overworld; make them block this
+		       particular stun attack too */
+		    if (!Blind) Your(vision_clears);
+		    else make_stunned((long)d(1,3),TRUE);
 		}
 		break;
 	    case AD_FIRE:
-	    	if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
-	    			!mtmp->mspec_used && rn2(5)) {
-	    	    int dmg = d(2,6);
-	    	    pline("%s attacks you with a fiery gaze!",
-	    	    		Monnam(mtmp));
-	    	    if (Fire_resistance) {
-	    	    	pline_The("fire doesn't feel hot!");
-	    	    	dmg = 0;
-	    	    }
-	    	    burn_away_slime();
-	    	    if((int) mtmp->m_lev > rn2(20))
-	    	    	destroy_item(SCROLL_CLASS, AD_FIRE);
-	    	    if((int) mtmp->m_lev > rn2(20))
-	    	    	destroy_item(POTION_CLASS, AD_FIRE);
-	    	    if((int) mtmp->m_lev > rn2(25))
-	    	    	destroy_item(SPBOOK_CLASS, AD_FIRE);
-	    	    if (dmg) mdamageu(mtmp, dmg);
-	    	}
+		if (!mtmp->mcan && canseemon(mtmp) &&
+			couldsee(mtmp->mx, mtmp->my) &&
+			mtmp->mcansee && !mtmp->mspec_used && rn2(5)) {
+		    int dmg = d(2,6);
+
+		    pline("%s attacks you with a fiery gaze!", Monnam(mtmp));
+		    stop_occupation();
+		    if (Fire_resistance) {
+			pline_The("fire doesn't feel hot!");
+			dmg = 0;
+		    }
+		    burn_away_slime();
+		    if ((int) mtmp->m_lev > rn2(20))
+			destroy_item(SCROLL_CLASS, AD_FIRE);
+		    if ((int) mtmp->m_lev > rn2(20))
+			destroy_item(POTION_CLASS, AD_FIRE);
+		    if ((int) mtmp->m_lev > rn2(25))
+			destroy_item(SPBOOK_CLASS, AD_FIRE);
+		    if (dmg) mdamageu(mtmp, dmg);
+		}
 		break;
 #ifdef PM_BEHOLDER /* work in progress */
 	    case AD_SLEE:
-		if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
+		if(!mtmp->mcan && canseemon(mtmp) &&
+		   couldsee(mtmp->mx, mtmp->my) && mtmp->mcansee &&
 		   multi >= 0 && !rn2(5) && !Sleep_resistance) {
+
 		    fall_asleep(-rnd(10), TRUE);
 		    pline("%s gaze makes you very sleepy...",
 			  s_suffix(Monnam(mtmp)));
@@ -1841,7 +1943,9 @@
 		if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
 		   (HFast & (INTRINSIC|TIMEOUT)) &&
 		   !defends(AD_SLOW, uwep) && !rn2(4))
+
 		    u_slow_down();
+		    stop_occupation();
 		break;
 #endif
 	    default: impossible("Gaze attack %d?", mattk->adtyp);
@@ -1880,7 +1984,7 @@
 	boolean is_acid;
 
 	if (!mon || !obj) return; /* just in case */
-	if (dmgtype(youmonst.data, AD_CORRODE))
+	if (dmgtype(youmonst.data, AD_CORR))
 	    is_acid = TRUE;
 	else if (dmgtype(youmonst.data, AD_RUST))
 	    is_acid = FALSE;
@@ -1978,7 +2082,7 @@
 
 	if (mon->mcan || mon->mspec_used) {
 		pline("%s acts as though %s has got a %sheadache.",
-		      Monnam(mon), he[pronoun_gender(mon)],
+		      Monnam(mon), mhe(mon),
 		      mon->mcan ? "severe " : "");
 		return 0;
 	}
@@ -2064,7 +2168,7 @@
 	else
 		pline("%s murmurs in your ear, while helping you undress.",
 			Blind ? (fem ? "She" : "He") : Monnam(mon));
-	mayberem(uarmc, "cloak");
+	mayberem(uarmc, cloak_simple_name(uarmc));
 	if(!uarmc)
 		mayberem(uarm, "suit");
 	mayberem(uarmf, "boots");
@@ -2088,11 +2192,11 @@
 
 	/* by this point you have discovered mon's identity, blind or not... */
 	pline("Time stands still while you and %s lie in each other's arms...",
-		mon_nam(mon));
+		noit_mon_nam(mon));
 	if (rn2(35) > ACURR(A_CHA) + ACURR(A_INT)) {
 		/* Don't bother with mspec_used here... it didn't get tired! */
 		pline("%s seems to have enjoyed it more than you...",
-			Monnam(mon));
+			noit_Monnam(mon));
 		switch (rn2(5)) {
 			case 0: You_feel("drained of energy.");
 				u.uen = 0;
@@ -2130,42 +2234,44 @@
 		}
 	} else {
 		mon->mspec_used = rnd(100); /* monster is worn out */
-		You("seem to have enjoyed it more than %s...", mon_nam(mon));
+		You("seem to have enjoyed it more than %s...",
+		    noit_mon_nam(mon));
 		switch (rn2(5)) {
-			case 0: You_feel("raised to your full potential.");
-				exercise(A_CON, TRUE);
-				u.uen = (u.uenmax += rnd(5));
-				break;
-			case 1: You_feel("good enough to do it again.");
-				(void) adjattrib(A_CON, 1, TRUE);
-				exercise(A_CON, TRUE);
-				flags.botl = 1;
-				break;
-			case 2: You("will always remember %s...", mon_nam(mon));
-				(void) adjattrib(A_WIS, 1, TRUE);
-				exercise(A_WIS, TRUE);
-				flags.botl = 1;
-				break;
-			case 3: pline("That was a very educational experience.");
-				pluslvl(FALSE);
-				exercise(A_WIS, TRUE);
-				break;
-			case 4: You_feel("restored to health!");
-				u.uhp = u.uhpmax;
-				if (Upolyd) u.mh = u.mhmax;
-				exercise(A_STR, TRUE);
-				flags.botl = 1;
-				break;
+		case 0: You_feel("raised to your full potential.");
+			exercise(A_CON, TRUE);
+			u.uen = (u.uenmax += rnd(5));
+			break;
+		case 1: You_feel("good enough to do it again.");
+			(void) adjattrib(A_CON, 1, TRUE);
+			exercise(A_CON, TRUE);
+			flags.botl = 1;
+			break;
+		case 2: You("will always remember %s...", noit_mon_nam(mon));
+			(void) adjattrib(A_WIS, 1, TRUE);
+			exercise(A_WIS, TRUE);
+			flags.botl = 1;
+			break;
+		case 3: pline("That was a very educational experience.");
+			pluslvl(FALSE);
+			exercise(A_WIS, TRUE);
+			break;
+		case 4: You_feel("restored to health!");
+			u.uhp = u.uhpmax;
+			if (Upolyd) u.mh = u.mhmax;
+			exercise(A_STR, TRUE);
+			flags.botl = 1;
+			break;
 		}
 	}
 
 	if (mon->mtame) /* don't charge */ ;
 	else if (rn2(20) < ACURR(A_CHA)) {
 		pline("%s demands that you pay %s, but you refuse...",
-			Monnam(mon), him[fem]);
+			noit_Monnam(mon),
+			Blind ? (fem ? "her" : "him") : mhim(mon));
 	} else if (u.umonnum == PM_LEPRECHAUN)
 		pline("%s tries to take your money, but fails...",
-				Monnam(mon));
+				noit_Monnam(mon));
 	else {
 		long cost;
 
@@ -2180,8 +2286,8 @@
 		if (cost > u.ugold) cost = u.ugold;
 		if (!cost) verbalize("It's on the house!");
 		else {
-		    pline("%s takes %ld zorkmid%s for services rendered!",
-			    Monnam(mon), cost, plur(cost));
+		    pline("%s takes %ld %s for services rendered!",
+			    noit_Monnam(mon), cost, currency(cost));
 		    u.ugold -= cost;
 		    mon->mgold += cost;
 		    flags.botl = 1;
@@ -2262,14 +2368,18 @@
 		    }
 		} else tmp = 0;
 		if (!rn2(30)) erode_armor(mtmp, TRUE);
-		if (!rn2(6)) erode_weapon(MON_WEP(mtmp), TRUE);
+		if (!rn2(6)) erode_obj(MON_WEP(mtmp), TRUE, TRUE);
 		goto assess_dmg;
 	    case AD_STON: /* cockatrice */
+	    {
+		int protector =
+		    mattk->aatyp == AT_TENT ? 0 :
+		    mattk->aatyp == AT_KICK ? W_ARMF : W_ARMG;
 		if (!resists_ston(mtmp) &&
 		    (mattk->aatyp != AT_WEAP || !MON_WEP(mtmp)) &&
 		    mattk->aatyp != AT_GAZE && mattk->aatyp != AT_EXPL &&
 		    mattk->aatyp != AT_MAGC &&
-		    !(mtmp->misc_worn_check & W_ARMG)) {
+		    !(mtmp->misc_worn_check & protector)) {
 		    if(poly_when_stoned(mtmp->data)) {
 			mon_to_stone(mtmp);
 			return (1);
@@ -2281,6 +2391,7 @@
 		    return 2;
 		}
 		return 1;
+	    }
 	    case AD_ENCH:	/* KMH -- remove enchantment (disenchanter) */
 	    	if (otmp) {
 	    	    (void) drain_item(otmp);
@@ -2345,7 +2456,8 @@
 	    case AD_STUN: /* Yellow mold */
 		if (!mtmp->mstun) {
 		    mtmp->mstun = 1;
-		    pline("%s staggers.", Monnam(mtmp));
+		    pline("%s %s.", Monnam(mtmp),
+			  makeplural(stagger(mtmp->data, "stagger")));
 		}
 		tmp = 0;
 		break;
diff -Naurd ../nethack-3.3.1/src/minion.c ./src/minion.c
--- ../nethack-3.3.1/src/minion.c Mon Jul 3 21:01:00 2000
+++ ./src/minion.c Fri Mar 22 14:41:03 2002
@@ -11,7 +11,7 @@
 register struct permonst *ptr;
 {
 	register int dtype = NON_PM, cnt = 0;
-	aligntyp atyp = sgn(ptr->maligntyp);
+	aligntyp atyp = (ptr->maligntyp==A_NONE) ? A_NONE : sgn(ptr->maligntyp);
 
 	if (is_dprince(ptr) || (ptr == &mons[PM_WIZARD_OF_YENDOR])) {
 	    dtype = (!rn2(20)) ? dprince(atyp) :
@@ -132,12 +132,12 @@
 	    return(1);
 	}
 	demand = (u.ugold * (rnd(80) + 20 * Athome)) /
-		 100 * (1 + (sgn(u.ualign.type) == sgn(mtmp->data->maligntyp)));
+	    (100 * (1 + (sgn(u.ualign.type) == sgn(mtmp->data->maligntyp))));
 	if (!demand)		/* you have no gold */
 	    return mtmp->mpeaceful = 0;
 	else {
-	    pline("%s demands %ld zorkmid%s for safe passage.",
-		  Amonnam(mtmp), demand, plur(demand));
+	    pline("%s demands %ld %s for safe passage.",
+		  Amonnam(mtmp), demand, currency(demand));
 
 	    if ((offer = bribe(mtmp)) >= demand) {
 		pline("%s vanishes, laughing about cowardly mortals.",
@@ -164,22 +164,23 @@
 	long offer;
 
 	getlin("How much will you offer?", buf);
-	(void) sscanf(buf, "%ld", &offer);
+	if (sscanf(buf, "%ld", &offer) != 1) offer = 0L;
 
 	/*Michael Paddon -- fix for negative offer to monster*/
 	/*JAR880815 - */
 	if (offer < 0L) {
 		You("try to shortchange %s, but fumble.",
 			mon_nam(mtmp));
-		offer = 0L;
+		return 0L;
 	} else if (offer == 0L) {
 		You("refuse.");
+		return 0L;
 	} else if (offer >= u.ugold) {
 		You("give %s all your gold.", mon_nam(mtmp));
 		offer = u.ugold;
-	} else You("give %s %ld zorkmid%s.", mon_nam(mtmp), offer,
-		   plur(offer));
-
+	} else {
+		You("give %s %ld %s.", mon_nam(mtmp), offer, currency(offer));
+	}
 	u.ugold -= offer;
 	mtmp->mgold += offer;
 	flags.botl = 1;
diff -Naurd ../nethack-3.3.1/src/mklev.c ./src/mklev.c
--- ../nethack-3.3.1/src/mklev.c Wed Aug 9 16:46:19 2000
+++ ./src/mklev.c Fri Mar 22 14:40:55 2002
@@ -34,7 +34,9 @@
 STATIC_DCL boolean FDECL(place_niche,(struct mkroom *,int*,int*,int*));
 STATIC_DCL void FDECL(makeniche,(int));
 STATIC_DCL void NDECL(make_niches);
+
 STATIC_PTR int FDECL(do_comp,(const genericptr,const genericptr));
+
 STATIC_DCL void FDECL(dosdoor,(XCHAR_P,XCHAR_P,struct mkroom *,int));
 STATIC_DCL void FDECL(join,(int,int,BOOLEAN_P));
 STATIC_DCL void FDECL(do_room_or_subroom, (struct mkroom *,int,int,int,int,
@@ -498,8 +500,9 @@
 		    dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR);
 		else {
 		    if (!level.flags.noteleport)
-			(void) mksobj_at(SCR_TELEPORTATION, xx, yy+dy, TRUE);
-		    if(!rn2(3)) (void) mkobj_at(0, xx, yy+dy, TRUE);
+			(void) mksobj_at(SCR_TELEPORTATION,
+					 xx, yy+dy, TRUE, FALSE);
+		    if (!rn2(3)) (void) mkobj_at(0, xx, yy+dy, TRUE);
 		}
 	    }
 	    return;
@@ -761,8 +764,8 @@
 		    x = somex(croom); y = somey(croom);
 		    tmonst = makemon((struct permonst *) 0, x,y,NO_MM_FLAGS);
 		    if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER] &&
-			!is_pool(x,y))
-			(void) maketrap (x,y,WEB);
+			    !occupied(x, y))
+			(void) maketrap(x, y, WEB);
 		}
 		/* put traps and mimics inside */
 		goldseen = FALSE;
@@ -796,7 +799,7 @@
 		 */
 		if(!rn2(nroom * 5 / 2))
 		    (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST,
-				     somex(croom), somey(croom), TRUE);
+				     somex(croom), somey(croom), TRUE, FALSE);
 
 		/* maybe make some graffiti */
 		if(!rn2(27 + 3 * abs(depth(&u.uz)))) {
@@ -848,7 +851,7 @@
 	    for (y = 1; y < (ROWNO - 1); y++)
 		if ((levl[x][y].typ == POOL && !rn2(10)) ||
 			(levl[x][y].typ == MOAT && !rn2(30)))
-	    	    (void)mksobj_at(KELP_FROND, x, y, TRUE);
+		    (void) mksobj_at(KELP_FROND, x, y, TRUE, FALSE);
 
 	/* determine if it is even allowed;
 	   almost all special levels are excluded */
@@ -1403,7 +1406,7 @@
 	}
 
 	/* Leave a bell, in case we accidentally buried someone alive */
-	if (dobell) (void) mksobj_at(BELL, m.x, m.y, TRUE);
+	if (dobell) (void) mksobj_at(BELL, m.x, m.y, TRUE, FALSE);
 	return;
 }
 
diff -Naurd ../nethack-3.3.1/src/mkmap.c ./src/mkmap.c
--- ../nethack-3.3.1/src/mkmap.c Sat Aug 5 00:38:39 2000
+++ ./src/mkmap.c Fri Mar 22 14:40:55 2002
@@ -348,6 +348,7 @@
 		for(j=0; j<ROWNO; j++)
 		    if((!IS_ROCK(fg_typ) && levl[i][j].typ == fg_typ) ||
 		       (!IS_ROCK(bg_typ) && levl[i][j].typ == bg_typ) ||
+		       (bg_typ == TREE && levl[i][j].typ == bg_typ) ||
 			(walled && IS_WALL(levl[i][j].typ)))
 			levl[i][j].lit = TRUE;
 	    for(i = 0; i < nroom; i++)
diff -Naurd ../nethack-3.3.1/src/mkmaze.c ./src/mkmaze.c
--- ../nethack-3.3.1/src/mkmaze.c Tue Jan 4 22:48:26 2000
+++ ./src/mkmaze.c Fri Mar 22 14:40:55 2002
@@ -29,9 +29,12 @@
 iswall(x,y)
 int x,y;
 {
-	if (!isok(x,y)) return FALSE;
-	return (IS_WALL(levl[x][y].typ) || IS_DOOR(levl[x][y].typ)
-		|| levl[x][y].typ == SDOOR);
+    register int type;
+
+    if (!isok(x,y)) return FALSE;
+    type = levl[x][y].typ;
+    return (IS_WALL(type) || IS_DOOR(type) ||
+	    type == SDOOR || type == IRONBARS);
 }
 
 STATIC_OVL boolean
@@ -44,7 +47,8 @@
     if (!isok(x,y)) return TRUE;
 
     type = levl[x][y].typ;
-    return (type == STONE || IS_WALL(type) || IS_DOOR(type) || type == SDOOR);
+    return (type == STONE || IS_WALL(type) || IS_DOOR(type) ||
+	    type == SDOOR || type == IRONBARS);
 }
 
 /* return TRUE if out of bounds, wall or rock */
@@ -258,11 +262,9 @@
     /* first a probabilistic approach */
 
     oneshot = (lx == hx && ly == hy);
-    for(trycnt = 0; trycnt < 100; trycnt ++) {
-
+    for (trycnt = 0; trycnt < 200; trycnt++) {
 	x = rn1((hx - lx) + 1, lx);
 	y = rn1((hy - ly) + 1, ly);
-
 	if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev))
 	    return;
     }
@@ -286,14 +288,14 @@
 boolean oneshot;
 d_level *lev;
 {
-    if(oneshot) {
+    if (bad_location(x, y, nlx, nly, nhx, nhy)) return FALSE;
+    if (oneshot) {
 	/* must make due with the only location possible */
 	/* avoid failure due to a misplaced trap */
 	/* it might still fail if there's a dungeon feature here */
 	struct trap *t = t_at(x,y);
 	if (t) deltrap(t);
     }
-    if(bad_location(x, y, nlx, nly, nhx, nhy)) return(FALSE);
     switch (rtype) {
     case LR_TELE:
     case LR_UPTELE:
@@ -513,12 +515,36 @@
 
 	} else Strcpy(protofile, "");
 
+#ifdef WIZARD
+	/* SPLEVTYPE format is "level-choice,level-choice"... */
+	if (wizard && *protofile && sp && sp->rndlevs) {
+	    char *ep = getenv("SPLEVTYPE");	/* not nh_getenv */
+	    if (ep) {
+		/* rindex always succeeds due to code in prior block */
+		int len = (rindex(protofile, '-') - protofile) + 1;
+
+		while (ep && *ep) {
+		    if (!strncmp(ep, protofile, len)) {
+			int pick = atoi(ep + len);
+			/* use choice only if valid */
+			if (pick > 0 && pick <= (int) sp->rndlevs)
+			    Sprintf(protofile + len, "%d", pick);
+			break;
+		    } else {
+			ep = index(ep, ',');
+			if (ep) ++ep;
+		    }
+		}
+	    }
+	}
+#endif
+
 	if(*protofile) {
 	    Strcat(protofile, LEV_EXT);
 	    if(load_special(protofile)) {
 		fixup_special();
 		/* some levels can end up with monsters
-                   on dead mon list, including light source monsters */
+		   on dead mon list, including light source monsters */
 		dmonsfree();
 		return;	/* no mazification right now */
 	    }
@@ -540,7 +566,7 @@
 	maze0xy(&mm);
 	walkfrom((int) mm.x, (int) mm.y);
 	/* put a boulder at the maze center */
-	(void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE);
+	(void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE, FALSE);
 
 #ifdef WALLIFIED_MAZE
 	wallification(2, 2, x_maze_max, y_maze_max);
@@ -603,7 +629,7 @@
 	}
 	for(x = rn1(10,2); x; x--) {
 		mazexy(&mm);
-		(void) mksobj_at(BOULDER, mm.x, mm.y, TRUE);
+		(void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
 	}
 	for (x = rn2(3); x; x--) {
 		mazexy(&mm);
@@ -880,24 +906,12 @@
 /* to ease the work of debuggers at this stage */
 #define register
 
-struct container {
-	struct container *next;
-	xchar x, y;
-	short what;
-	genericptr_t list;
-};
 #define CONS_OBJ   0
 #define CONS_MON   1
 #define CONS_HERO  2
 #define CONS_TRAP  3
 
-static struct bubble {
-	xchar x, y;	/* coordinates of the upper left corner */
-	schar dx, dy;	/* the general direction of the bubble's movement */
-	uchar *bm;	/* pointer to the bubble bit mask */
-	struct bubble *prev, *next; /* need to traverse the list up and down */
-	struct container *cons;
-} *bbubbles, *ebubbles;
+static struct bubble *bbubbles, *ebubbles;
 
 static struct trap *wportal;
 static int xmin, ymin, xmax, ymax;	/* level boundaries */
diff -Naurd ../nethack-3.3.1/src/mkobj.c ./src/mkobj.c
--- ../nethack-3.3.1/src/mkobj.c Sat Aug 5 00:39:04 2000
+++ ./src/mkobj.c Fri Mar 22 14:40:55 2002
@@ -78,26 +78,26 @@
 };
 
 struct obj *
-mkobj_at(let,x,y, artif)
+mkobj_at(let, x, y, artif)
 char let;
-int x,y;
+int x, y;
 boolean artif;
 {
-	register struct obj *otmp;
+	struct obj *otmp;
 
-	otmp = mkobj(let,artif);
+	otmp = mkobj(let, artif);
 	place_object(otmp, x, y);
 	return(otmp);
 }
 
 struct obj *
-mksobj_at(otyp,x,y,init)
-int otyp,x,y;
-boolean init;
+mksobj_at(otyp, x, y, init, artif)
+int otyp, x, y;
+boolean init, artif;
 {
-	register struct obj *otmp;
+	struct obj *otmp;
 
-	otmp = mksobj(otyp,init,TRUE);
+	otmp = mksobj(otyp, init, artif);
 	place_object(otmp, x, y);
 	return(otmp);
 }
@@ -107,7 +107,7 @@
 char oclass;
 boolean artif;
 {
-	register int tprob, i, prob = rnd(1000);
+	int tprob, i, prob = rnd(1000);
 
 	if(oclass == RANDOM_CLASS) {
 		const struct icp *iprobs =
@@ -138,21 +138,21 @@
 struct obj *box;
 {
 	register int n;
-	register struct obj *otmp, *gold = 0;
+	register struct obj *otmp;
 
 	box->cobj = (struct obj *) 0;
 
-	switch(box->otyp) {
-		case ICE_BOX:		n = 20; break;
-		case CHEST:		n = 5; break;
-		case LARGE_BOX:		n = 3; break;
-		case SACK:
-		case OILSKIN_SACK:
+	switch (box->otyp) {
+	case ICE_BOX:		n = 20; break;
+	case CHEST:		n = 5; break;
+	case LARGE_BOX:		n = 3; break;
+	case SACK:
+	case OILSKIN_SACK:
 				/* initial inventory: sack starts out empty */
 				if (moves <= 1 && !in_mklev) { n = 0; break; }
 				/*else FALLTHRU*/
-		case BAG_OF_HOLDING:	n = 1; break;
-		default:		n = 0; break;
+	case BAG_OF_HOLDING:	n = 1; break;
+	default:		n = 0; break;
 	}
 
 	for (n = rn2(n+1); n > 0; n--) {
@@ -178,13 +178,7 @@
 		if (otmp->oclass == GOLD_CLASS) {
 		    /* 2.5 x level's usual amount; weight adjusted below */
 		    otmp->quan = (long)(rnd(level_difficulty()+2) * rnd(75));
-		    if (gold) {			/* gold already in this box */
-			gold->quan += otmp->quan;	/* merge */
-			dealloc_obj(otmp);	/* note: not yet in any chain */
-			continue;
-		    } else {
-			gold = otmp;		/* remember this object */
-		    }
+		    otmp->owt = weight(otmp);
 		} else while (otmp->otyp == ROCK) {
 		    otmp->otyp = rnd_class(DILITHIUM_CRYSTAL, LOADSTONE);
 		    if (otmp->quan > 2L) otmp->quan = 1L;
@@ -199,10 +193,8 @@
 			    otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING);
 		}
 	    }
-	    add_to_container(box, otmp);
+	    (void) add_to_container(box, otmp);
 	}
-	if (gold) gold->owt = weight(gold);	/* quantity was diddled */
-	return;
 }
 
 int
@@ -225,10 +217,10 @@
 }
 
 /*
- * Split obj so that it gets size num. The remainder is put in the object
- * structure delivered by this call.  The object is positioned just
- * following the original in the nobj chain (and nexthere chain when on
- * the floor).
+ * Split obj so that it gets size gets reduced by num. The quantity num is
+ * put in the object structure delivered by this call.  The returned object
+ * has its wornmask cleared and is positioned just following the original
+ * in the nobj chain (and nexthere chain when on the floor).
  */
 struct obj *
 splitobj(obj, num)
@@ -237,7 +229,7 @@
 {
 	struct obj *otmp;
 
-	if (obj->cobj || num <= 0L || obj->quan < num)
+	if (obj->cobj || num <= 0L || obj->quan <= num)
 	    panic("splitobj");	/* can't split containers */
 	otmp = newobj(obj->oxlth + obj->onamelth);
 	*otmp = *obj;		/* copies whole structure */
@@ -245,9 +237,10 @@
 	if (!otmp->o_id) otmp->o_id = flags.ident++;	/* ident overflowed */
 	otmp->timed = 0;	/* not timed, yet */
 	otmp->lamplit = 0;	/* ditto */
-	obj->quan = num;
+	otmp->owornmask = 0L;	/* new object isn't worn */
+	obj->quan -= num;
 	obj->owt = weight(obj);
-	otmp->quan -= num;
+	otmp->quan = num;
 	otmp->owt = weight(otmp);	/* -= obj->owt ? */
 	obj->nobj = otmp;
 	/* Only set nexthere when on the floor, nexthere is also used */
@@ -452,14 +445,17 @@
 	    case SLIME_MOLD:
 		otmp->spe = current_fruit;
 		break;
+	    case KELP_FROND:
+		otmp->quan = (long) rnd(2);
+		break;
 	    }
-	    if (otmp->otyp == CORPSE || otmp->otyp == MEAT_RING) break;
+	    if (otmp->otyp == CORPSE || otmp->otyp == MEAT_RING ||
+		otmp->otyp == KELP_FROND) break;
 	    /* fall into next case */
 
 	case GEM_CLASS:
 		if (otmp->otyp == LOADSTONE) curse(otmp);
 		else if (otmp->otyp == ROCK) otmp->quan = (long) rn1(6,6);
-		else if (otmp->otyp == KELP_FROND) otmp->quan = (long) rnd(2);
 		else if (otmp->otyp != LUCKSTONE && !rn2(6)) otmp->quan = 2L;
 		else otmp->quan = 1L;
 		break;
@@ -609,7 +605,8 @@
 			otmp->corpsenm = rndmonnum();
 			if (!verysmall(&mons[otmp->corpsenm]) &&
 				rn2(level_difficulty()/2 + 10) > 10)
-			    add_to_container(otmp, mkobj(SPBOOK_CLASS,FALSE));
+			    (void) add_to_container(otmp,
+						    mkobj(SPBOOK_CLASS,FALSE));
 		}
 		break;
 	case GOLD_CLASS:
@@ -627,27 +624,32 @@
 }
 
 /*
- * Start a corpse decay or revive timer.  This assumes that the corpse
- * was just dropped and its age is 0.
+ * Start a corpse decay or revive timer.
+ * This takes the age of the corpse into consideration as of 3.4.0.
  */
 void
 start_corpse_timeout(body)
 	struct obj *body;
 {
-	long when;
+	long when; 		/* rot away when this old */
+	long corpse_age;	/* age of corpse          */
 	int rot_adjust;
 	short action;
 
 #define TAINT_AGE (50L)		/* age when corpses go bad */
 #define TROLL_REVIVE_CHANCE 37	/* 1/37 chance for 50 turns ~ 75% chance */
-#define ROT_AGE (250L)	/* age when corpses rot away */
+#define ROT_AGE (250L)		/* age when corpses rot away */
 
 	/* lizards and lichen don't rot or revive */
 	if (body->corpsenm == PM_LIZARD || body->corpsenm == PM_LICHEN) return;
 
 	action = ROT_CORPSE;		/* default action: rot away */
-	when = ROT_AGE;			/* rot away when this old */
 	rot_adjust = in_mklev ? 25 : 10;	/* give some variation */
+	corpse_age = monstermoves - body->age;
+	if (corpse_age > ROT_AGE)
+		when = rot_adjust;
+	else
+		when = ROT_AGE - corpse_age;
 	when += (long)(rnz(rot_adjust) - rot_adjust);
 
 	if (is_rider(&mons[body->corpsenm])) {
@@ -659,7 +661,7 @@
 		for (when = 12L; when < 500L; when++)
 		    if (!rn2(3)) break;
 
-	} else if (mons[body->corpsenm].mlet == S_TROLL) {
+	} else if (mons[body->corpsenm].mlet == S_TROLL && !body->norevive) {
 		long age;
 		for (age = 2; age <= TAINT_AGE; age++)
 		    if (!rn2(TROLL_REVIVE_CHANCE)) {	/* troll revives */
@@ -668,7 +670,8 @@
 			break;
 		    }
 	}
-
+	
+	if (body->norevive) body->norevive = 0;
 	(void) start_timer(when, TIMER_OBJECT, action, (genericptr_t)body);
 }
 
@@ -819,9 +822,15 @@
 
 		return wt + cwt;
 	}
-	if (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM)
-		return (int)obj->quan * mons[obj->corpsenm].cwt;
-	else if (obj->oclass == GOLD_CLASS)
+	if (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM) {
+		long long_wt = obj->quan * (long) mons[obj->corpsenm].cwt;
+
+		wt = (long_wt > LARGEST_INT) ? LARGEST_INT : (int)long_wt;
+		if (obj->oeaten) wt = eaten_stat(wt, obj);
+		return wt;
+	} else if (obj->oclass == FOOD_CLASS && obj->oeaten) {
+		return eaten_stat((int)obj->quan * wt, obj);
+	} else if (obj->oclass == GOLD_CLASS)
 		return (int)((obj->quan + 50L) / 100L);
 	else if (obj->otyp == HEAVY_IRON_BALL && obj->owt != 0)
 		return((int)(obj->owt));	/* kludge for "very" heavy iron ball */
@@ -832,8 +841,9 @@
 
 struct obj *
 rnd_treefruit_at(x,y)
+int x, y;
 {
-	return mksobj_at(treefruits[rn2(SIZE(treefruits)-1)],x,y,TRUE);
+	return mksobj_at(treefruits[rn2(SIZE(treefruits))], x, y, TRUE, FALSE);
 }
 #endif /* OVL0 */
 #ifdef OVLB
@@ -845,11 +855,12 @@
 {
     register struct obj *gold = g_at(x,y);
 
-    if (amount <= 0L) amount = (long)(1 + rnd(level_difficulty()+2) * rnd(30));
+    if (amount <= 0L)
+	amount = (long)(1 + rnd(level_difficulty()+2) * rnd(30));
     if (gold) {
 	gold->quan += amount;
     } else {
-	gold = mksobj_at(GOLD_PIECE,x,y,TRUE);
+	gold = mksobj_at(GOLD_PIECE, x, y, TRUE, FALSE);
 	gold->quan = amount;
     }
     gold->owt = weight(gold);
@@ -886,7 +897,7 @@
 
 	if (objtype != CORPSE && objtype != STATUE)
 	    impossible("making corpstat type %d", objtype);
-	otmp = mksobj_at(objtype, x, y, init);
+	otmp = mksobj_at(objtype, x, y, init, FALSE);
 	if (otmp) {
 	    if (mtmp) {
 		struct obj *otmp2;
@@ -955,8 +966,9 @@
 	if (otmp && otmp->oxlth) {
 		struct monst *mtmp2 = (struct monst *)otmp->oextra;
 		if (mtmp->data) mtmp2->mnum = monsndx(mtmp->data);
-		/* invalidate pointers and m_id */
-		mtmp2->m_id     = 0;
+		/* invalidate pointers */
+		/* m_id is needed to know if this is a revived quest leader */
+		/* but m_id must be cleared when loading bones */
 		mtmp2->nmon     = (struct monst *)0;
 		mtmp2->data     = (struct permonst *)0;
 		mtmp2->minvent  = (struct obj *)0;
@@ -1007,7 +1019,7 @@
 
 	/* player statues never contain books */
 	initialize_it = (objtype != STATUE);
-	if ((otmp = mksobj_at(objtype, x, y, initialize_it)) != 0) {
+	if ((otmp = mksobj_at(objtype, x, y, initialize_it, FALSE)) != 0) {
 	    /* tt_oname will return null if the scoreboard is empty */
 	    if ((otmp2 = tt_oname(otmp)) != 0) otmp = otmp2;
 	}
@@ -1036,8 +1048,19 @@
 register struct obj *otmp;
 {
 	int otyp = otmp->otyp;
+	int omat = objects[otyp].oc_material;
 
-	if (objects[otyp].oc_oprop == FIRE_RES) return FALSE;
+	if (objects[otyp].oc_oprop == FIRE_RES || otyp == WAN_FIRE)
+		return FALSE;
+
+	return((boolean)((omat <= WOOD && omat != LIQUID) || omat == PLASTIC));
+}
+
+boolean
+is_rottable(otmp)
+register struct obj *otmp;
+{
+	int otyp = otmp->otyp;
 
 	return((boolean)(objects[otyp].oc_material <= WOOD &&
 			objects[otyp].oc_material != LIQUID));
@@ -1062,6 +1085,7 @@
     if (otmp->where != OBJ_FREE)
 	panic("place_object: obj not free");
 
+    obj_no_longer_held(otmp);
     if (otmp->otyp == BOULDER) block_point(x,y);	/* vision */
 
     /* obj goes under boulders */
@@ -1361,17 +1385,30 @@
     return 0;	/* obj on mon's inventory chain */
 }
 
-void
+/*
+ * Add obj to container, make sure obj is "free".  Returns (merged) obj.
+ * The input obj may be deleted in the process.
+ */
+struct obj *
 add_to_container(container, obj)
     struct obj *container, *obj;
 {
+    struct obj *otmp;
+
     if (obj->where != OBJ_FREE)
 	panic("add_to_container: obj not free");
+    if (container->where != OBJ_INVENT && container->where != OBJ_MINVENT)
+	obj_no_longer_held(obj);
+
+    /* merge if possible */
+    for (otmp = container->cobj; otmp; otmp = otmp->nobj)
+	if (merged(&otmp, &obj)) return (otmp);
 
     obj->where = OBJ_CONTAINED;
     obj->ocontainer = container;
     obj->nobj = container->cobj;
     container->cobj = obj;
+    return (obj);
 }
 
 void
diff -Naurd ../nethack-3.3.1/src/mkroom.c ./src/mkroom.c
--- ../nethack-3.3.1/src/mkroom.c Wed Aug 9 16:46:19 2000
+++ ./src/mkroom.c Fri Mar 22 14:40:55 2002
@@ -325,31 +325,35 @@
 			    (void) mk_tt_object(CORPSE, sx, sy);
 			if(!rn2(10))	/* lots of treasure buried with dead */
 			    (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST,
-					     sx, sy, TRUE);
+					     sx, sy, TRUE, FALSE);
 			if (!rn2(5))
 			    make_grave(sx, sy, (char *)0);
 			break;
 		    case BEEHIVE:
 			if(!rn2(3))
-			    (void) mksobj_at(LUMP_OF_ROYAL_JELLY, sx, sy, TRUE);
+			    (void) mksobj_at(LUMP_OF_ROYAL_JELLY,
+					     sx, sy, TRUE, FALSE);
 			break;
 		    case BARRACKS:
 			if(!rn2(20))	/* the payroll and some loot */
 			    (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST,
-					     sx, sy, TRUE);
+					     sx, sy, TRUE, FALSE);
 			break;
 		    case COCKNEST:
 			if(!rn2(3)) {
 			    struct obj *sobj = mk_tt_object(STATUE, sx, sy);
 
-			    if (sobj)
-			    	for (i = rn2(5); i; i--)
-			    	    add_to_container(sobj, mkobj(RANDOM_CLASS, FALSE));
+			    if (sobj) {
+				for (i = rn2(5); i; i--)
+				    (void) add_to_container(sobj,
+						mkobj(RANDOM_CLASS, FALSE));
+				sobj->owt = weight(sobj);
+			    }
 			}
 			break;
 		    case ANTHOLE:
 			if(!rn2(3))
-			    (void) mkobj_at(FOOD_CLASS, sx, sy, TRUE);
+			    (void) mkobj_at(FOOD_CLASS, sx, sy, FALSE);
 			break;
 		}
 	    }
@@ -360,7 +364,8 @@
 		  levl[tx][ty].typ = THRONE;
 		  (void) somexy(sroom, &mm);
 		  (void) mkgold((long) rn1(50 * level_difficulty(),10), mm.x, mm.y);
-		  chest = mksobj_at(CHEST, mm.x, mm.y, TRUE); /* the royal coffers */
+		  /* the royal coffers */
+		  chest = mksobj_at(CHEST, mm.x, mm.y, TRUE, FALSE);
 		  chest->spe = 2; /* so it can be found later */
 		  level.flags.has_court = 1;
 		  break;
diff -Naurd ../nethack-3.3.1/src/mon.c ./src/mon.c
--- ../nethack-3.3.1/src/mon.c Thu Aug 3 20:30:14 2000
+++ ./src/mon.c Fri Mar 22 14:41:04 2002
@@ -12,7 +12,6 @@
 #include "edog.h"
 #include <ctype.h>
 
-STATIC_DCL boolean FDECL(corpse_chance,(struct monst *));
 STATIC_DCL boolean FDECL(restrap,(struct monst *));
 STATIC_DCL long FDECL(mm_aggression, (struct monst *,struct monst *));
 #ifdef OVL2
@@ -21,6 +20,16 @@
 STATIC_DCL void FDECL(kill_eggs, (struct obj *));
 #endif
 
+#ifdef REINCARNATION
+#define LEVEL_SPECIFIC_NOCORPSE(mdat) \
+	 (Is_rogue_level(&u.uz) || \
+	   (level.flags.graveyard && is_undead(mdat) && rn2(3)))
+#else
+#define LEVEL_SPECIFIC_NOCORPSE(mdat) \
+	   (level.flags.graveyard && is_undead(mdat) && rn2(3))
+#endif
+
+
 #if 0
 /* part of the original warning code which was replaced in 3.3.1 */
 #ifdef OVL1
@@ -99,11 +108,9 @@
 		PM_SANDESTIN,
 };
 
-/* return TRUE if the monster tends to revive */
-#define REVIVER(ptr)	(is_rider(ptr) || ptr->mlet == S_TROLL)
-
 #define KEEPTRAITS(mon)	(mon->isshk || mon->mtame || \
-			 (mon->data->geno & G_UNIQ) || REVIVER(mon->data))
+			 (mon->data->geno & G_UNIQ) || is_reviver(mon->data) || \
+			 (mon->m_id == quest_status.leader_m_id))
 
 /* Creates a monster corpse, a "special" corpse, or nothing if it doesn't
  * leave corpses.  Monsters which leave "special" corpses should have
@@ -136,8 +143,8 @@
 		/* Make dragon scales.  This assumes that the order of the */
 		/* dragons is the same as the order of the scales.	   */
 		if (!rn2(mtmp->mrevived ? 20 : 3)) {
-		    obj = mksobj_at(GRAY_DRAGON_SCALES +
-				    monsndx(mdat)-PM_GRAY_DRAGON, x, y, FALSE);
+		    num = GRAY_DRAGON_SCALES + monsndx(mdat) - PM_GRAY_DRAGON;
+		    obj = mksobj_at(num, x, y, FALSE, FALSE);
 		    obj->spe = 0;
 		    obj->cursed = obj->blessed = FALSE;
 		}
@@ -146,10 +153,15 @@
 	    case PM_WHITE_UNICORN:
 	    case PM_GRAY_UNICORN:
 	    case PM_BLACK_UNICORN:
-		(void) mksobj_at(UNICORN_HORN, x, y, TRUE);
+		if (mtmp->mrevived && rn2(20)) {
+			if (canseemon(mtmp))
+			   pline("%s recently regrown horn crumbles to dust.",
+				s_suffix(Monnam(mtmp)));
+		} else
+			(void) mksobj_at(UNICORN_HORN, x, y, TRUE, FALSE);
 		goto default_1;
 	    case PM_LONG_WORM:
-		(void) mksobj_at(WORM_TOOTH, x, y, TRUE);
+		(void) mksobj_at(WORM_TOOTH, x, y, TRUE, FALSE);
 		goto default_1;
 	    case PM_VAMPIRE:
 	    case PM_VAMPIRE_LORD:
@@ -181,17 +193,17 @@
 	    case PM_IRON_GOLEM:
 		num = d(2,6);
 		while (num--)
-			obj = mksobj_at(IRON_CHAIN, x, y, TRUE);
+			obj = mksobj_at(IRON_CHAIN, x, y, TRUE, FALSE);
 		mtmp->mnamelth = 0;
 		break;
 	    case PM_GLASS_GOLEM:
 		num = d(2,4);   /* very low chance of creating all glass gems */
 		while (num--)
-			obj = mksobj_at((LAST_GEM + rnd(9)), x, y, TRUE);
+			obj = mksobj_at((LAST_GEM + rnd(9)), x, y, TRUE, FALSE);
 		mtmp->mnamelth = 0;
 		break;
 	    case PM_CLAY_GOLEM:
-		obj = mksobj_at(ROCK, x, y, FALSE);
+		obj = mksobj_at(ROCK, x, y, FALSE, FALSE);
 		obj->quan = (long)(rn2(20) + 50);
 		obj->owt = weight(obj);
 		mtmp->mnamelth = 0;
@@ -203,18 +215,14 @@
 	    case PM_WOOD_GOLEM:
 		num = d(2,4);
 		while(num--) {
-			obj = mksobj_at(QUARTERSTAFF, x, y, TRUE);
-			if (obj && obj->oartifact) {	/* don't allow this */
-				artifact_exists(obj, ONAME(obj), FALSE);
-				Strcpy(ONAME(obj), "");  obj->onamelth = 0;
-			}
+			obj = mksobj_at(QUARTERSTAFF, x, y, TRUE, FALSE);
 		}
 		mtmp->mnamelth = 0;
 		break;
 	    case PM_LEATHER_GOLEM:
 		num = d(2,4);
 		while(num--)
-			obj = mksobj_at(LEATHER_ARMOR, x, y, TRUE);
+			obj = mksobj_at(LEATHER_ARMOR, x, y, TRUE, FALSE);
 		mtmp->mnamelth = 0;
 		break;
 	    case PM_GOLD_GOLEM:
@@ -223,9 +231,9 @@
 		mtmp->mnamelth = 0;
 		break;
 	    case PM_PAPER_GOLEM:
-		num = d(2,3);
+		num = rnd(4);
 		while (num--)
-			obj = mksobj_at(SCR_BLANK_PAPER, x, y, TRUE);
+			obj = mksobj_at(SCR_BLANK_PAPER, x, y, TRUE, FALSE);
 		mtmp->mnamelth = 0;
 		break;
 	    default_1:
@@ -239,6 +247,10 @@
 	}
 	/* All special cases should precede the G_NOCORPSE check */
 
+	/* if polymorph or undead turning has killed this monster,
+	   prevent the same attack beam from hitting its corpse */
+	if (flags.bypasses) bypass_obj(obj);
+
 	if (mtmp->mnamelth)
 	    obj = oname(obj, NAME(mtmp));
 
@@ -306,19 +318,21 @@
 }
 #endif /* 0 */
 
-/* check mtmp and water for compatibility, 0 (survived), 1 (drowned) */
+/* check mtmp and water/lava for compatibility, 0 (survived), 1 (died) */
 int
-minwater(mtmp)
+minliquid(mtmp)
 register struct monst *mtmp;
 {
-    boolean inpool, infountain;
+    boolean inpool, inlava, infountain;
 
     inpool = is_pool(mtmp->mx,mtmp->my) &&
 	     !is_flyer(mtmp->data) && !is_floater(mtmp->data);
+    inlava = is_lava(mtmp->mx,mtmp->my) &&
+	     !is_flyer(mtmp->data) && !is_floater(mtmp->data);
     infountain = IS_FOUNTAIN(levl[mtmp->mx][mtmp->my].typ);
 
 #ifdef STEED
-	/* Flying and levitation keeps our steed out of the water */
+	/* Flying and levitation keeps our steed out of the liquid */
 	/* (but not water-walking or swimming) */
 	if (mtmp == u.usteed && (Flying || Levitation))
 		return (0);
@@ -347,7 +361,36 @@
 	return (0);
     }
 
-    if (inpool) {
+    if (inlava) {
+	/*
+	 * Lava effects much as water effects. Lava likers are able to
+	 * protect their stuff. Fire resistant monsters can only protect
+	 * themselves  --ALI
+	 */
+	if (!is_clinger(mtmp->data) && !likes_lava(mtmp->data)) {
+	    if (!resists_fire(mtmp)) {
+		if (cansee(mtmp->mx,mtmp->my))
+		    pline("%s burns to a crisp.", Monnam(mtmp));
+		mondead(mtmp);
+	    }
+	    else {
+		if (--mtmp->mhp < 1) {
+		    if (cansee(mtmp->mx,mtmp->my))
+			pline("%s surrenders to the fire.", Monnam(mtmp));
+		    mondead(mtmp);
+		}
+		else if (cansee(mtmp->mx,mtmp->my))
+		    pline("%s burns slightly.", Monnam(mtmp));
+	    }
+	    if (mtmp->mhp > 0) {
+		(void) fire_damage(mtmp->minvent, FALSE, FALSE,
+						mtmp->mx, mtmp->my);
+		rloc(mtmp);
+		return 0;
+	    }
+	    return (1);
+	}
+    } else if (inpool) {
 	/* Most monsters drown in pools.  flooreffects() will take care of
 	 * water damage to dead monsters' inventory, but survivors need to
 	 * be handled here.  Swimmers are able to protect their stuff...
@@ -369,8 +412,7 @@
 	/* but eels have a difficult time outside */
 	if (mtmp->data->mlet == S_EEL && !Is_waterlevel(&u.uz)) {
 	    if(mtmp->mhp > 1) mtmp->mhp--;
-	    mtmp->mflee = 1;
-	    mtmp->mfleetim += 2;
+	    monflee(mtmp, 2, FALSE, FALSE);
 	}
     }
     return (0);
@@ -420,7 +462,7 @@
 
 	/* possibly polymorph shapechangers and lycanthropes */
 	if (mtmp->cham && !rn2(6))
-	    (void) newcham(mtmp, (struct permonst *)0);
+	    (void) newcham(mtmp, (struct permonst *)0, FALSE);
 	were_change(mtmp);
 
 	/* gradually time out temporary problems */
@@ -477,7 +519,9 @@
 	if (mtmp->movement >= NORMAL_SPEED)
 	    somebody_can_move = TRUE;
 
-	if (minwater(mtmp)) continue;
+	if (vision_full_recalc) vision_recalc(0);	/* vision! */
+
+	if (minliquid(mtmp)) continue;
 
 	if (is_hider(mtmp->data)) {
 	    /* unwatched mimics and piercers may hide again  [MRS] */
@@ -541,7 +585,7 @@
  * has young and old forms).
  */
 int
-meatgold(mtmp)
+meatmetal(mtmp)
 	register struct monst *mtmp;
 {
 	register struct obj *otmp;
@@ -553,7 +597,9 @@
 
 	/* Eats topmost metal object if it is there */
 	for (otmp = level.objects[mtmp->mx][mtmp->my];
-						    otmp; otmp = otmp->nexthere)
+						otmp; otmp = otmp->nexthere) {
+	    if (mtmp->data == &mons[PM_RUST_MONSTER] && !is_rustprone(otmp))
+		continue;
 	    if (is_metallic(otmp) && !obj_resists(otmp, 5, 95) &&
 		touch_artifact(otmp,mtmp)) {
 		if (mtmp->data == &mons[PM_RUST_MONSTER] && otmp->oerodeproof) {
@@ -596,7 +642,7 @@
 			delobj(otmp);
 			ptr = mtmp->data;
 			if (poly) {
-			    if (newcham(mtmp, (struct permonst *)0))
+			    if (newcham(mtmp, (struct permonst *)0, FALSE))
 				ptr = mtmp->data;
 			} else if (grow) {
 			    ptr = grow_up(mtmp, (struct monst *)0);
@@ -616,11 +662,13 @@
 			if (!ptr) return 2;		 /* it died */
 		    }
 		    /* Left behind a pile? */
-		    if(rnd(25) < 3) (void) mksobj_at(ROCK, mtmp->mx, mtmp->my, TRUE);
+		    if (rnd(25) < 3)
+			(void)mksobj_at(ROCK, mtmp->mx, mtmp->my, TRUE, FALSE);
 		    newsym(mtmp->mx, mtmp->my);
 		    return 1;
 		}
 	    }
+	}
 	return 0;
 }
 
@@ -679,7 +727,7 @@
 		delobj(otmp);		/* munch */
 		ptr = mtmp->data;
 		if (poly) {
-		    if (newcham(mtmp, (struct permonst *)0)) ptr = mtmp->data;
+		    if (newcham(mtmp, (struct permonst *)0, FALSE)) ptr = mtmp->data;
 		} else if (grow) {
 		    ptr = grow_up(mtmp, (struct monst *)0);
 		} else if (heal) {
@@ -747,16 +795,11 @@
 /*	Nymphs take everything.  Most monsters don't pick up corpses. */
 	    if (!str ? searches_for_item(mtmp,otmp) :
 		  !!(index(str, otmp->oclass))) {
-		if (otmp->otyp == CORPSE && (
-		    is_rider(&mons[otmp->corpsenm]) ||
-		    (touch_petrifies(&mons[otmp->corpsenm])
-			&& !(mtmp->misc_worn_check & W_ARMG)) ||
-		    (mtmp->data->mlet != S_NYMPH
-			&& !touch_petrifies(&mons[otmp->corpsenm])
-			&& otmp->corpsenm != PM_LIZARD
-			&& !acidic(&mons[otmp->corpsenm]))
-		   ))
-			continue;
+		if (otmp->otyp == CORPSE && mtmp->data->mlet != S_NYMPH &&
+			/* let a handful of corpse types thru to can_carry() */
+			!touch_petrifies(&mons[otmp->corpsenm]) &&
+			otmp->corpsenm != PM_LIZARD &&
+			!acidic(&mons[otmp->corpsenm])) continue;
 		if (!touch_artifact(otmp,mtmp)) continue;
 		if (!can_carry(mtmp,otmp)) continue;
 		if (is_pool(mtmp->mx,mtmp->my)) continue;
@@ -840,6 +883,8 @@
 	if (otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]) &&
 		!(mtmp->misc_worn_check & W_ARMG) && !resists_ston(mtmp))
 	    return FALSE;
+	if (otyp == CORPSE && is_rider(&mons[otmp->corpsenm]))
+	    return FALSE;
 	if (objects[otyp].oc_material == SILVER && hates_silver(mdat) &&
 		(otyp != BELL_OF_OPENING || !is_covetous(mdat)))
 	    return FALSE;
@@ -1188,7 +1233,7 @@
 struct monst *mtmp;
 struct permonst *mptr;	/* reflects mtmp->data _prior_ to mtmp's death */
 {
-	if(mtmp->mleashed) m_unleash(mtmp);
+	if (mtmp->mleashed) m_unleash(mtmp, FALSE);
 	    /* to prevent an infinite relobj-flooreffects-hmon-killed loop */
 	mtmp->mtrapped = 0;
 	mtmp->mhp = 0; /* simplify some tests: force mhp to 0 */
@@ -1310,6 +1355,10 @@
 	 */
 	tmp = monsndx(mtmp->data);
 	if (mvitals[tmp].died < 255) mvitals[tmp].died++;
+
+	/* if it's a (possibly polymorphed) quest leader, mark him as dead */
+	if (mtmp->m_id == quest_status.leader_m_id)
+	    quest_status.leader_is_dead = TRUE;
 #ifdef MAIL
 	/* if the mail daemon dies, no more mail delivery.  -3. */
 	if (tmp == PM_MAIL_DAEMON) mvitals[tmp].mvflags |= G_GENOD;
@@ -1337,19 +1386,20 @@
 	m_detach(mtmp, mptr);
 }
 
-STATIC_OVL boolean
-corpse_chance(mon)
+/* TRUE if corpse might be dropped, magr may die if mon was swallowed */
+boolean
+corpse_chance(mon, magr, was_swallowed)
 struct monst *mon;
+struct monst *magr;			/* killer, if swallowed */
+boolean was_swallowed;			/* digestion */
 {
 	struct permonst *mdat = mon->data;
 	int i, tmp;
 
-
 	if (mdat == &mons[PM_VLAD_THE_IMPALER] || mdat->mlet == S_LICH) {
-		if (cansee(mon->mx, mon->my))
-			pline("%s body crumbles into dust.",
-				s_suffix(Monnam(mon)));
-		return FALSE;
+	    if (cansee(mon->mx, mon->my) && !was_swallowed)
+		pline("%s body crumbles into dust.", s_suffix(Monnam(mon)));
+	    return FALSE;
 	}
 
 	/* Gas spores always explode upon death */
@@ -1361,10 +1411,33 @@
 	    	else if(mdat->mattk[i].damd)
 	    	    tmp = d((int)mdat->mlevel+1, (int)mdat->mattk[i].damd);
 	    	else tmp = 0;
+		if (Half_physical_damage) tmp = (tmp+1) / 2;
+		if (was_swallowed && magr) {
+		    if (magr == &youmonst) {
+			There("is an explosion in your %s!",
+			      body_part(STOMACH));
+			Sprintf(killer_buf, "%s explosion",
+				s_suffix(mdat->mname));
+			losehp(tmp, killer_buf, KILLED_BY_AN);
+		    } else {
+			if (flags.soundok) You_hear("an explosion.");
+			magr->mhp -= tmp;
+			if (magr->mhp < 1) mondied(magr);
+			if (magr->mhp < 1) { /* maybe lifesaved */
+			    if (canspotmon(magr))
+				pline("%s rips open!", Monnam(magr));
+			} else if (canseemon(magr))
+			    pline("%s seems to have indigestion.",
+				  Monnam(magr));
+		    }
+
+		    return FALSE;
+		}
+
 	    	Sprintf(killer_buf, "%s explosion", s_suffix(mdat->mname));
 	    	killer = killer_buf;
 	    	killer_format = KILLED_BY_AN;
-	    	explode(mon->mx, mon->my, -1, tmp, MON_EXPLODE); 
+	    	explode(mon->mx, mon->my, -1, tmp, MON_EXPLODE, EXPL_NOXIOUS); 
 	    	return (FALSE);
 	    }
   	}
@@ -1372,11 +1445,7 @@
 	/* must duplicate this below check in xkilled() since it results in
 	 * creating no objects as well as no corpse
 	 */
-	if (
-#ifdef REINCARNATION
-		 Is_rogue_level(&u.uz) ||
-#endif
-	   (level.flags.graveyard && is_undead(mdat) && rn2(3)))
+	if (LEVEL_SPECIFIC_NOCORPSE(mdat))
 		return FALSE;
 
 	if (bigmonst(mdat) || mdat == &mons[PM_LIZARD]
@@ -1396,7 +1465,7 @@
 	mondead(mdef);
 	if (mdef->mhp > 0) return;	/* lifesaved */
 
-	if (corpse_chance(mdef))
+	if (corpse_chance(mdef, (struct monst *)0, FALSE))
 		(void) make_corpse(mdef);
 }
 
@@ -1423,6 +1492,7 @@
 {
 	struct obj *otmp, *obj;
 	xchar x = mdef->mx, y = mdef->my;
+	boolean wasinside = FALSE;
 
 	/* we have to make the statue before calling mondead, to be able to
 	 * put inventory in it, and we have to check for lifesaving before
@@ -1441,6 +1511,9 @@
 		/* some objects may end up outside the statue */
 		while ((obj = mdef->minvent) != 0) {
 		    obj_extract_self(obj);
+		    obj_no_longer_held(obj);
+		    if (obj->owornmask & W_WEP)
+			setmnotwielded(mdef,obj);
 		    obj->owornmask = 0L;
 		    if (obj->otyp == BOULDER ||
 #if 0				/* monsters don't carry statues */
@@ -1451,7 +1524,7 @@
 			place_object(obj, x, y);
 		    } else {
 			if (obj->lamplit) end_burn(obj, TRUE);
-			add_to_container(otmp, obj);
+			(void) add_to_container(otmp, obj);
 		    }
 		}
 		if (mdef->mgold) {
@@ -1459,7 +1532,7 @@
 			au = mksobj(GOLD_PIECE, FALSE, FALSE);
 			au->quan = mdef->mgold;
 			au->owt = weight(au);
-			add_to_container(otmp, au);
+			(void) add_to_container(otmp, au);
 			mdef->mgold = 0;
 		}
 		/* Archeologists should not break unique statues */
@@ -1467,14 +1540,22 @@
 			otmp->spe = 1;
 		otmp->owt = weight(otmp);
 	} else
-		otmp = mksobj_at(ROCK, x, y, TRUE);
+		otmp = mksobj_at(ROCK, x, y, TRUE, FALSE);
 
 	stackobj(otmp);
 	/* mondead() already does this, but we must do it before the newsym */
 	if(glyph_is_invisible(levl[x][y].glyph))
 	    unmap_object(x, y);
 	if (cansee(x, y)) newsym(x,y);
+	/* We don't currently trap the hero in the statue in this case but we could */
+	if (u.uswallow && u.ustuck == mdef) wasinside = TRUE;
 	mondead(mdef);
+	if (wasinside) {
+		if (is_animal(mdef->data))
+			You("%s through an opening in the new %s.",
+				locomotion(youmonst.data, "jump"),
+				xname(otmp));
+	}
 }
 
 /* another monster has killed the monster mdef */
@@ -1554,21 +1635,25 @@
 	u.uconduct.killer++;
 
 	if (dest & 1) {
-	    if(!wasinside && !canspotmon(mtmp))
-		You("destroy it!");
+	    const char *verb = nonliving(mtmp->data) ? "destroy" : "kill";
+
+	    if (!wasinside && !canspotmon(mtmp))
+		You("%s it!", verb);
 	    else {
-		You("destroy %s!",
-			mtmp->mtame
-			    ? x_monnam(mtmp, ARTICLE_THE, "poor",
-				mtmp->mnamelth ? SUPPRESS_SADDLE : 0, FALSE)
-			    : mon_nam(mtmp));
+		You("%s %s!", verb,
+		    !mtmp->mtame ? mon_nam(mtmp) :
+			x_monnam(mtmp,
+				 mtmp->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 "poor",
+				 mtmp->mnamelth ? SUPPRESS_SADDLE : 0,
+				 FALSE));
 	    }
 	}
 
-	if (mtmp->mtrapped &&
-	    ((t = t_at(x, y)) && (t->ttyp == PIT || t->ttyp == SPIKED_PIT)) &&
-	    sobj_at(BOULDER, x, y))
-		dest ^= 2; /*
+	if (mtmp->mtrapped && (t = t_at(x, y)) != 0 &&
+		(t->ttyp == PIT || t->ttyp == SPIKED_PIT) &&
+		sobj_at(BOULDER, x, y))
+	    dest |= 2;     /*
 			    * Prevent corpses/treasure being created "on top"
 			    * of the boulder that is about to fall in. This is
 			    * out of order, but cannot be helped unless this
@@ -1601,16 +1686,12 @@
 		goto cleanup;
 	}
 
-	if((dest & 2)
-#ifdef REINCARNATION
-		 || Is_rogue_level(&u.uz)
-#endif
-	   || (level.flags.graveyard && is_undead(mdat) && rn2(3)))
+	if((dest & 2) || LEVEL_SPECIFIC_NOCORPSE(mdat))
 		goto cleanup;
 
 #ifdef MAIL
 	if(mdat == &mons[PM_MAIL_DAEMON]) {
-		stackobj(mksobj_at(SCR_MAIL, x, y, FALSE));
+		stackobj(mksobj_at(SCR_MAIL, x, y, FALSE, FALSE));
 		redisp = TRUE;
 	}
 #endif
@@ -1644,7 +1725,7 @@
 		 * different from whether or not the corpse is "special";
 		 * if we want both, we have to specify it explicitly.
 		 */
-		if (corpse_chance(mtmp))
+		if (corpse_chance(mtmp, (struct monst *)0, FALSE))
 			(void) make_corpse(mtmp);
 	}
 	if(redisp) newsym(x,y);
@@ -1672,7 +1753,7 @@
 	newexplevel();		/* will decide if you go up */
 
 	/* adjust alignment points */
-	if (mdat->msound == MS_LEADER) {		/* REAL BAD! */
+	if (mtmp->m_id == quest_status.leader_m_id) {		/* REAL BAD! */
 	    adjalign(-(u.ualign.record+(int)ALIGNLIM/2));
 	    pline("That was %sa bad idea...",
 	    		u.uevent.qcompleted ? "probably " : "");
@@ -1710,7 +1791,7 @@
 	/* it's a golem, and not a stone golem */
 	if(canseemon(mtmp))
 	    pline("%s solidifies...", Monnam(mtmp));
-	if (newcham(mtmp, &mons[PM_STONE_GOLEM])) {
+	if (newcham(mtmp, &mons[PM_STONE_GOLEM], FALSE)) {
 	    if(canseemon(mtmp))
 		pline("Now it's %s.", an(mtmp->data->mname));
 	} else {
@@ -1871,7 +1952,7 @@
 	}
 	aggravate();
     }
-    if(mtmp->data == &mons[PM_MEDUSA] && !mtmp->mcan) {
+    if(mtmp->data == &mons[PM_MEDUSA]) {
 	register int i;
 	for(i = 0; i < NATTK; i++)
 	     if(mtmp->data->mattk[i].aatyp == AT_GAZE) {
@@ -1993,7 +2074,7 @@
 		mcham = (int) mtmp->cham;
 		if (mcham) {
 			mtmp->cham = CHAM_ORDINARY;
-			(void) newcham(mtmp, &mons[cham_to_pm[mcham]]);
+			(void) newcham(mtmp, &mons[cham_to_pm[mcham]], FALSE);
 		}
 		if(is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN)
 			new_were(mtmp);
@@ -2036,11 +2117,11 @@
 	    mcham = (int) mon->cham;
 	    if (mcham) {
 		mon->cham = CHAM_ORDINARY;
-		(void) newcham(mon, &mons[cham_to_pm[mcham]]);
+		(void) newcham(mon, &mons[cham_to_pm[mcham]], FALSE);
 	    } else if (is_were(mon->data) && !is_human(mon->data)) {
 		new_were(mon);
 	    }
-	} else {
+	} else if (mon->cham == CHAM_ORDINARY) {
 	    mon->cham = pm_to_cham(monsndx(mon->data));
 	}
 }
@@ -2051,7 +2132,8 @@
 register struct monst *mtmp;
 {
 	if(mtmp->cham || mtmp->mcan || mtmp->m_ap_type ||
-	   cansee(mtmp->mx, mtmp->my) || rn2(3) || (mtmp == u.ustuck))
+	   cansee(mtmp->mx, mtmp->my) || rn2(3) || (mtmp == u.ustuck) ||
+	   (sensemon(mtmp) && distu(mtmp->mx, mtmp->my) <= 2))
 		return(FALSE);
 
 	if(mtmp->data->mlet == S_MIMIC) {
@@ -2121,17 +2203,44 @@
 		if (!rn2(3)) mndx = pick_animal();
 		break;
 	    case CHAM_ORDINARY:
+	      {
+		struct obj *m_armr = which_armor(mon, W_ARM);
+
+		if (m_armr && Is_dragon_scales(m_armr))
+		    mndx = Dragon_scales_to_pm(m_armr) - mons;
+		else if (m_armr && Is_dragon_mail(m_armr))
+		    mndx = Dragon_mail_to_pm(m_armr) - mons;
+	      }
 		break;
 	}
+#ifdef WIZARD
+	/* For debugging only: allow control of polymorphed monster; not saved */
+	if (wizard && iflags.mon_polycontrol) {
+		char pprompt[BUFSZ], buf[BUFSZ];
+		int tries = 0;
+		do {
+			Sprintf(pprompt,
+				"Change %s into what kind of monster? [type the name]",
+				mon_nam(mon));
+			getlin(pprompt,buf);
+			mndx = name_to_mon(buf);
+			if (mndx < LOW_PM)
+				You("cannot polymorph %s into that.", mon_nam(mon));
+			else break;
+		} while(++tries < 5);
+		if (tries==5) pline(thats_enough_tries);
+	}
+#endif /*WIZARD*/
 	if (mndx == NON_PM) mndx = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
 	return mndx;
 }
 
 /* make a chameleon look like a new monster; returns 1 if it actually changed */
 int
-newcham(mtmp, mdat)
+newcham(mtmp, mdat, polyspot)
 struct monst *mtmp;
 struct permonst *mdat;
+boolean polyspot;	/* change is the result of wand or spell of polymorph */
 {
 	int mhp, hpn, hpd;
 	int mndx, tryct;
@@ -2223,7 +2332,8 @@
 		new_light_source(mtmp->mx, mtmp->my, emits_light(mtmp->data),
 				 LS_MONSTER, (genericptr_t)mtmp);
 	}
-	mtmp->perminvis = pm_invisible(mdat);
+	if (!mtmp->perminvis || pm_invisible(olddata))
+	    mtmp->perminvis = pm_invisible(mdat);
 	mtmp->minvis = mtmp->invis_blkd ? 0 : mtmp->perminvis;
 	if (!(hides_under(mdat) && OBJ_AT(mtmp->mx, mtmp->my)) &&
 			!(mdat->mlet == S_EEL && is_pool(mtmp->mx, mtmp->my)))
@@ -2263,7 +2373,7 @@
 
 	newsym(mtmp->mx,mtmp->my);
 
-	mon_break_armor(mtmp);
+	mon_break_armor(mtmp, polyspot);
 	if (!(mtmp->misc_worn_check & W_ARMG))
 	    mselftouch(mtmp, "No longer petrify-resistant, ",
 			!flags.mon_moving);
@@ -2282,6 +2392,9 @@
 	    for (otmp = mtmp->minvent; otmp; otmp = otmp2) {
 		otmp2 = otmp->nobj;
 		if (otmp->otyp == BOULDER) {
+		    /* this keeps otmp from being polymorphed in the
+		       same zap that the monster that held it is polymorphed */
+		    if (polyspot) bypass_obj(otmp);
 		    obj_extract_self(otmp);
 		    /* probably ought to give some "drop" message here */
 		    if (flooreffects(otmp, mtmp->mx, mtmp->my, "")) continue;
@@ -2410,7 +2523,7 @@
 	    mndx = monsndx(mtmp->data);
 	    if ((mvitals[mndx].mvflags & G_GENOD) || kill_cham[mtmp->cham]) {
 		if (mtmp->cham && !kill_cham[mtmp->cham])
-		    (void) newcham(mtmp, (struct permonst *)0);
+		    (void) newcham(mtmp, (struct permonst *)0, FALSE);
 		else
 		    mondead(mtmp);
 	    }
@@ -2442,13 +2555,8 @@
 	return;
     }
     if (slow) {
-	if (mon->mspeed != MSLOW) {
-	    unsigned int oldspeed = mon->mspeed;
-
-	    mon_adjust_speed(mon, -1);
-	    if (mon->mspeed != oldspeed && cansee(mon->mx, mon->my))
-		pline("%s seems to be moving slower.", Monnam(mon));
-	}
+	if (mon->mspeed != MSLOW)
+	    mon_adjust_speed(mon, -1, (struct obj *)0);
     }
     if (heal) {
 	if (mon->mhp < mon->mhpmax) {
diff -Naurd ../nethack-3.3.1/src/mondata.c ./src/mondata.c
--- ../nethack-3.3.1/src/mondata.c Sat Jul 15 19:31:49 2000
+++ ./src/mondata.c Fri Mar 22 14:40:55 2002
@@ -6,6 +6,10 @@
 #include "eshk.h"
 #include "epri.h"
 
+/* fake attack and damage types */
+#define AT_ANY (-1)
+#define AD_ANY (-1)
+
 /*	These routines provide basic data for any type of monster. */
 
 #ifdef OVLB
@@ -29,17 +33,26 @@
 #endif /* OVLB */
 #ifdef OVL0
 
-boolean
-attacktype(ptr, atyp)
-	register struct	permonst	*ptr;
-	register int atyp;
+struct attack *
+attacktype_fordmg(ptr, atyp, dtyp)
+struct permonst *ptr;
+int atyp, dtyp;
 {
-	int	i;
+    struct attack *a;
 
-	for(i = 0; i < NATTK; i++)
-	    if(ptr->mattk[i].aatyp == atyp) return(TRUE);
+    for (a = &ptr->mattk[0]; a < &ptr->mattk[NATTK]; a++)
+	if (a->aatyp == atyp && (dtyp == AD_ANY || a->adtyp == dtyp))
+	    return a;
 
-	return(FALSE);
+    return (struct attack *)0;
+}
+
+boolean
+attacktype(ptr, atyp)
+struct permonst *ptr;
+int atyp;
+{
+    return attacktype_fordmg(ptr, atyp, AD_ANY) ? TRUE : FALSE;
 }
 
 #endif /* OVL0 */
@@ -105,9 +118,9 @@
 			    paralysis does too, we can't check it */
 		    mon->msleeping))
 	    return TRUE;
-	/* AD_BLND => yellow light, Archon, !cobra, !raven */
-	if (dmgtype(ptr, AD_BLND) &&
-	    !attacktype(ptr, AT_SPIT) && !attacktype(ptr, AT_CLAW))
+	/* yellow light, Archon; !dust vortex, !cobra, !raven */
+	if (dmgtype_fromattack(ptr, AD_BLND, AT_EXPL) ||
+		dmgtype_fromattack(ptr, AD_BLND, AT_GAZE))
 	    return TRUE;
 	o = is_you ? uwep : MON_WEP(mon);
 	if (o && o->oartifact && defends(AD_BLND, o))
@@ -282,17 +295,26 @@
 		attacktype(ptr,AT_HUGS)));
 }
 
-boolean
-dmgtype(ptr, dtyp)
-	register struct	permonst	*ptr;
-	register int dtyp;
+struct attack *
+dmgtype_fromattack(ptr, dtyp, atyp)
+struct permonst *ptr;
+int dtyp, atyp;
 {
-	int	i;
+    struct attack *a;
 
-	for(i = 0; i < NATTK; i++)
-	    if(ptr->mattk[i].adtyp == dtyp) return TRUE;
+    for (a = &ptr->mattk[0]; a < &ptr->mattk[NATTK]; a++)
+	if (a->adtyp == dtyp && (atyp == AT_ANY || a->aatyp == atyp))
+	    return a;
 
-	return FALSE;
+    return (struct attack *)0;
+}
+
+boolean
+dmgtype(ptr, dtyp)
+struct permonst *ptr;
+int dtyp;
+{
+    return dmgtype_fromattack(ptr, dtyp, AT_ANY) ? TRUE : FALSE;
 }
 
 /* returns the maximum damage a defender can do to the attacker via
@@ -583,11 +605,13 @@
 	return montype;
 }
 
-static const char *levitate[2]	= { "float", "Float" };
-static const char *fly[2]	= { "fly", "Fly" };
-static const char *slither[2]	= { "slither", "Slither" };
-static const char *ooze[2]	= { "ooze", "Ooze" };
-static const char *crawl[2]	= { "crawl", "Crawl" };
+static const char *levitate[4]	= { "float", "Float", "wobble", "Wobble" };
+static const char *flys[4]	= { "fly", "Fly", "flutter", "Flutter" };
+static const char *flyl[4]	= { "fly", "Fly", "stagger", "Stagger" };
+static const char *slither[4]	= { "slither", "Slither", "falter", "Falter" };
+static const char *ooze[4]	= { "ooze", "Ooze", "tremble", "Tremble" };
+static const char *immobile[4]	= { "wiggle", "Wiggle", "pulsate", "Pulsate" };
+static const char *crawl[4]	= { "crawl", "Crawl", "falter", "Falter" };
 
 const char *
 locomotion(ptr, def)
@@ -598,9 +622,31 @@
 
 	return (
 		is_floater(ptr) ? levitate[capitalize] :
-		is_flyer(ptr)   ? fly[capitalize] :
+		(is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[capitalize] :
+		(is_flyer(ptr) && ptr->msize > MZ_SMALL)  ? flyl[capitalize] :
+		slithy(ptr)     ? slither[capitalize] :
+		amorphous(ptr)  ? ooze[capitalize] :
+		!ptr->mmove	? immobile[capitalize] :
+		nolimbs(ptr)    ? crawl[capitalize] :
+		def
+	       );
+
+}
+
+const char *
+stagger(ptr, def)
+const struct permonst *ptr;
+const char *def;
+{
+	int capitalize = 2 + (*def == highc(*def));
+
+	return (
+		is_floater(ptr) ? levitate[capitalize] :
+		(is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[capitalize] :
+		(is_flyer(ptr) && ptr->msize > MZ_SMALL)  ? flyl[capitalize] :
 		slithy(ptr)     ? slither[capitalize] :
 		amorphous(ptr)  ? ooze[capitalize] :
+		!ptr->mmove	? immobile[capitalize] :
 		nolimbs(ptr)    ? crawl[capitalize] :
 		def
 	       );
diff -Naurd ../nethack-3.3.1/src/monmove.c ./src/monmove.c
--- ../nethack-3.3.1/src/monmove.c Thu Aug 3 20:30:14 2000
+++ ./src/monmove.c Fri Mar 22 14:41:04 2002
@@ -69,6 +69,9 @@
 		  }
 		  stop_occupation();
 		}
+	    } else if (is_digging()) {
+		/* chewing, wand/spell of digging are checked elsewhere */
+		watch_dig(mtmp, digging.pos.x, digging.pos.y, FALSE);
 	    }
 	}
 }
@@ -198,6 +201,41 @@
 	return(0);
 }
 
+/* monster begins fleeing for the specified time, 0 means untimed flee
+ * if first, only adds fleetime if monster isn't already fleeing
+ * if fleemsg, prints a message about new flight, otherwise, caller should */
+void
+monflee(mtmp, fleetime, first, fleemsg)
+struct monst *mtmp;
+int fleetime;
+boolean first;
+boolean fleemsg;
+{
+	if (u.ustuck == mtmp) {
+	    if (u.uswallow)
+		expels(mtmp, mtmp->data, TRUE);
+	    else if (!sticks(youmonst.data)) {
+		unstuck(mtmp);	/* monster lets go when fleeing */
+		You("get released!");
+	    }
+	}
+
+	if (!first || !mtmp->mflee) {
+	    /* don't lose untimed scare */
+	    if (!fleetime)
+		mtmp->mfleetim = 0;
+	    else if (!mtmp->mflee || mtmp->mfleetim) {
+		fleetime += mtmp->mfleetim;
+		/* ensure monster flees long enough to visibly stop fighting */
+		if (fleetime == 1) fleetime++;
+		mtmp->mfleetim = min(fleetime, 127);
+	    }
+	    if (!mtmp->mflee && fleemsg && canseemon(mtmp))
+		pline("%s turns to flee!", (Monnam(mtmp)));
+	    mtmp->mflee = 1;
+	}
+}
+
 STATIC_OVL void
 distfleeck(mtmp,inrange,nearby,scared)
 register struct monst *mtmp;
@@ -227,18 +265,11 @@
 			       (!mtmp->mpeaceful &&
 				    in_your_sanctuary(mtmp, 0, 0))));
 
-	if(*scared && !mtmp->mflee) {
-		if (!sticks(youmonst.data))
-			unstuck(mtmp);	/* monster lets go when fleeing */
-		mtmp->mflee = 1;
-#ifdef STUPID
+	if(*scared) {
 		if (rn2(7))
-		    mtmp->mfleetim = rnd(10);
+		    monflee(mtmp, rnd(10), TRUE, TRUE);
 		else
-		    mtmp->mfleetim = rnd(100);
-#else
-		mtmp->mfleetim = rnd(rn2(7) ? 10 : 100);
-#endif
+		    monflee(mtmp, rnd(100), TRUE, TRUE);
 	}
 
 }
@@ -314,7 +345,7 @@
 	}
 	if (mdat->msound == MS_SHRIEK && !um_dist(mtmp->mx, mtmp->my, 1))
 	    m_respond(mtmp);
-	if (mdat == &mons[PM_MEDUSA] && cansee(mtmp->mx, mtmp->my))
+	if (mdat == &mons[PM_MEDUSA] && couldsee(mtmp->mx, mtmp->my))
 	    m_respond(mtmp);
 	if (mtmp->mhp <= 0) return(1); /* m_respond gaze can kill medusa */
 
@@ -406,10 +437,13 @@
 				m2->mhp -= rnd(15);
 				if (m2->mhp <= 0)
 				    monkilled(m2, "", AD_DRIN);
+				else
+				    m2->msleeping = 0;
 			}
 		}
 	}
 toofar:
+
 	/* If monster is nearby you, and has to wield a weapon, do so.   This
 	 * costs the monster a move, of course.
 	 */
@@ -437,6 +471,24 @@
 	   (mdat->mlet == S_LEPRECHAUN && !u.ugold && (mtmp->mgold || rn2(2))) ||
 	   (is_wanderer(mdat) && !rn2(4)) || (Conflict && !mtmp->iswiz) ||
 	   (!mtmp->mcansee && !rn2(4)) || mtmp->mpeaceful) {
+		/* Possibly cast an undirected spell if not attacking you */
+		/* note that most of the time castmu() will pick a directed
+		   spell and do nothing, so the monster moves normally */
+		/* arbitrary distance restriction to keep monster far away
+		   from you from having cast dozens of sticks-to-snakes
+		   or similar spells by the time you reach it */
+		if (dist2(mtmp->mx, mtmp->my, u.ux, u.uy) <= 64 && !mtmp->mspec_used) {
+		    struct attack *a;
+
+		    for (a = &mdat->mattk[0]; a < &mdat->mattk[NATTK]; a++) {
+			if (a->aatyp == AT_MAGC && (a->adtyp == AD_SPEL || a->adtyp == AD_CLRC)) {
+			    if (castmu(mtmp, a, FALSE, FALSE)) {
+				tmp = 3;
+				break;
+			    }
+			}
+		    }
+		}
 
 		tmp = m_move(mtmp, 0);
 		distfleeck(mtmp,&inrange,&nearby,&scared);	/* recalc */
@@ -445,6 +497,10 @@
 		    case 0:	/* no movement, but it can still attack you */
 		    case 3:	/* absolutely no movement */
 				/* for pets, case 0 and 3 are equivalent */
+			/* vault guard might have vanished */
+			if (mtmp->isgd && (mtmp->mhp < 1 ||
+					    (mtmp->mx == 0 && mtmp->my == 0)))
+			    return 1;	/* behave as if it died */
 			/* During hallucination, monster appearance should
 			 * still change - even if it doesn't move.
 			 */
@@ -526,7 +582,8 @@
 	boolean likegold=0, likegems=0, likeobjs=0, likemagic=0, conceals=0;
 	boolean likerock=0, can_tunnel=0;
 	boolean can_open=0, can_unlock=0, doorbuster=0;
-	boolean uses_items=0;
+	boolean uses_items=0, setlikes=0;
+	boolean avoid=FALSE;
 	struct permonst *ptr;
 	struct monst *mtoo;
 	schar mmoved = 0;	/* not strictly nec.: chi >= 0 will do */
@@ -699,6 +756,7 @@
 		likemagic = (likes_magic(ptr) && pctload < 85);
 		likerock = (throws_rocks(ptr) && pctload < 50 && !In_sokoban(&u.uz));
 		conceals = hides_under(ptr);
+		setlikes = TRUE;
 	    }
 	}
 
@@ -807,7 +865,8 @@
 	    flag |= (ALLOW_SANCT | ALLOW_SSM);
 	else flag |= ALLOW_U;
 	if (is_minion(ptr) || is_rider(ptr)) flag |= ALLOW_SANCT;
-	if (is_unicorn(ptr)) flag |= NOTONL;
+	/* unicorn may not be able to avoid hero on a noteleport level */
+	if (is_unicorn(ptr) && !level.flags.noteleport) flag |= NOTONL;
 	if (passes_walls(ptr)) flag |= (ALLOW_WALL | ALLOW_ROCK);
 	if (can_tunnel) flag |= ALLOW_DIG;
 	if (is_human(ptr) || ptr == &mons[PM_MINOTAUR]) flag |= ALLOW_SSM;
@@ -831,8 +890,14 @@
 	    /* allow monsters be shortsighted on some levels for balance */
 	    if(!mtmp->mpeaceful && level.flags.shortsighted &&
 	       nidist > (couldsee(nix,niy) ? 144 : 36) && appr == 1) appr = 0;
+	    if (is_unicorn(ptr) && level.flags.noteleport) {
+		/* on noteleport levels, perhaps we cannot avoid hero */
+		for(i = 0; i < cnt; i++)
+		    if(!(info[i] & NOTONL)) avoid=TRUE;
+	    }
 
 	    for(i=0; i < cnt; i++) {
+		if (avoid && (info[i] & NOTONL)) continue;
 		nx = poss[i].x;
 		ny = poss[i].y;
 
@@ -1042,9 +1107,27 @@
 		newsym(mtmp->mx,mtmp->my);
 	    }
 	    if(OBJ_AT(mtmp->mx, mtmp->my) && mtmp->mcanmove) {
+		/* recompute the likes tests, in case we polymorphed
+		 * or if the "likegold" case got taken above */
+		if (setlikes) {
+		    register int pctload = (curr_mon_load(mtmp) * 100) /
+			max_mon_load(mtmp);
+
+		    /* look for gold or jewels nearby */
+		    likegold = (likes_gold(ptr) && pctload < 95);
+		    likegems = (likes_gems(ptr) && pctload < 85);
+		    uses_items = (!mindless(ptr) && !is_animal(ptr)
+				  && pctload < 75);
+		    likeobjs = (likes_objs(ptr) && pctload < 75);
+		    likemagic = (likes_magic(ptr) && pctload < 85);
+		    likerock = (throws_rocks(ptr) && pctload < 50 &&
+				!In_sokoban(&u.uz));
+		    conceals = hides_under(ptr);
+		}
+
 		/* Maybe a rock mole just ate some metal object */
 		if (metallivorous(ptr)) {
-		    if (meatgold(mtmp) == 2) return 2;	/* it died */
+		    if (meatmetal(mtmp) == 2) return 2;	/* it died */
 		}
 
 		if(g_at(mtmp->mx,mtmp->my) && likegold) mpickgold(mtmp);
@@ -1128,8 +1211,17 @@
 
 	notseen = (!mtmp->mcansee || (Invis && !perceives(mtmp->data)));
 	/* add cases as required.  eg. Displacement ... */
-	disp = ((notseen || Underwater) ? 1 :
-		Displaced ? (couldsee(mx, my) ? 2 : 1) : 0);
+	if (notseen || Underwater) {
+	    /* Xorns can smell valuable metal like gold, treat as seen */
+	    if ((mtmp->data == &mons[PM_XORN]) &&
+			u.ugold
+			&& !Underwater)
+		disp = 0;
+	    else
+		disp = 1;
+	} else if (Displaced) {
+	    disp = couldsee(mx, my) ? 2 : 1;
+	} else disp = 0;
 	if (!disp) goto found_you;
 
 	/* without something like the following, invis. and displ.
@@ -1152,7 +1244,8 @@
 		  || ((mx != u.ux || my != u.uy) &&
 		      !passes_walls(mtmp->data) &&
 		      (!ACCESSIBLE(levl[mx][my].typ) ||
-			(closed_door(mx, my) && !can_ooze(mtmp)))));
+		       (closed_door(mx, my) && !can_ooze(mtmp))))
+		  || !couldsee(mx, my));
 	} else {
 found_you:
 	    mx = u.ux;
diff -Naurd ../nethack-3.3.1/src/monst.c ./src/monst.c
--- ../nethack-3.3.1/src/monst.c Thu Aug 3 21:51:43 2000
+++ ./src/monst.c Fri Mar 22 14:40:55 2002
@@ -174,23 +174,24 @@
 	  ATTK(AT_NONE, AD_STON, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(10, 10, 0, MS_HISS, MZ_TINY),
 	MR_POISON|MR_STONE, MR_POISON|MR_STONE,
-	M1_ANIMAL|M1_NOHANDS, M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN),
+	M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE, M2_HOSTILE,
+	M3_INFRAVISIBLE, CLR_BROWN),
     MON("cockatrice", S_COCKATRICE,
 	LVL(5, 6, 6, 30, 0), (G_GENO|5),
 	A(ATTK(AT_BITE, AD_PHYS, 1, 3), ATTK(AT_TUCH, AD_STON, 0, 0),
 	  ATTK(AT_NONE, AD_STON, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(30, 30, 0, MS_HISS, MZ_SMALL),
 	MR_POISON|MR_STONE, MR_POISON|MR_STONE,
-	M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS, M2_HOSTILE, M3_INFRAVISIBLE,
-	CLR_YELLOW),
+	M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE|M1_OVIPAROUS, M2_HOSTILE,
+	M3_INFRAVISIBLE, CLR_YELLOW),
     MON("pyrolisk", S_COCKATRICE, 
 	LVL(6, 6, 6, 30, 0), (G_GENO|1),
 	A(ATTK(AT_GAZE, AD_FIRE, 2, 6), NO_ATTK,
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), 
 	SIZ(30, 30, 0, MS_HISS, MZ_SMALL),
 	MR_POISON|MR_FIRE, MR_POISON|MR_FIRE,
-	M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS, M2_HOSTILE, M3_INFRAVISIBLE,
-	CLR_RED),
+	M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE|M1_OVIPAROUS, M2_HOSTILE,
+	M3_INFRAVISIBLE, CLR_RED),
 /*
  * dogs & other canines
  */
@@ -254,7 +255,7 @@
 	LVL(5, 12, 4, 0, 0), (G_GENO|G_SGROUP|2),
 	A(ATTK(AT_BITE, AD_PHYS, 2, 4),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
-	SIZ(500, 250, 0, MS_BARK, MZ_SMALL), 0, 0,
+	SIZ(500, 250, 0, MS_BARK, MZ_MEDIUM), 0, 0,
 	M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE,
 	CLR_BROWN),
     MON("werewolf", S_DOG,
@@ -316,7 +317,7 @@
 	A(ATTK(AT_BOOM, AD_PHYS, 4, 6), NO_ATTK, NO_ATTK,
 	  NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), 0, 0,
-	M1_FLY|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS,
+	M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS,
 	M2_HOSTILE|M2_NEUTER, 0, CLR_GRAY),
     MON("floating eye", S_EYE,
 	LVL(2, 1, 9, 10, 0), (G_GENO|5),
@@ -330,21 +331,21 @@
 	A(ATTK(AT_EXPL, AD_COLD, 4, 6),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_COLD, MR_COLD,
-	M1_FLY|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE,
+	M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE,
 	M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_WHITE),
     MON("flaming sphere", S_EYE, 
 	LVL(6, 13, 4, 0, 0), (G_NOCORPSE|G_GENO|2),
 	A(ATTK(AT_EXPL, AD_FIRE, 4, 6), NO_ATTK, NO_ATTK,
 	    NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_FIRE, MR_FIRE,
-	M1_FLY|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS,
+	M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS,
 	M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_RED),
     MON("shocking sphere", S_EYE, 
 	LVL(6, 13, 4, 0, 0), (G_NOCORPSE|G_GENO|2),
 	A(ATTK(AT_EXPL, AD_ELEC, 4, 6), NO_ATTK, NO_ATTK,
 	  NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_ELEC, MR_ELEC,
-	M1_FLY|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS,
+	M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS,
 	M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, HI_ZAP),
 #if 0		/* not yet implemented */
     MON("beholder", S_EYE,
@@ -384,7 +385,7 @@
 	LVL(5, 15, 6, 0, 0), (G_GENO|1),
 	A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4),
 	  ATTK(AT_BITE, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK),
-	SIZ(600, 300, 0, MS_GROWL, MZ_LARGE), 0, 0,
+	SIZ(600, 300, 0, MS_GROWL, MZ_SMALL), 0, 0,
 	M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE,M2_HOSTILE, M3_INFRAVISIBLE,
 	CLR_CYAN),
     MON("panther", S_FELINE, 
@@ -477,7 +478,7 @@
 	LVL(9, 12, 5, 90, -8), (G_GENO|1),
 	A(ATTK(AT_WEAP, AD_PHYS, 1, 4), ATTK(AT_TENT, AD_DRIN, 2, 1),
 	  ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1),
-	  ATTK(AT_TENT, AD_DRIN, 2, 1), NO_ATTK),
+	  NO_ATTK, NO_ATTK),
 	SIZ(1450, 400, 0, MS_HISS, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_FLY|M1_SEE_INVIS|M1_OMNIVORE,
 	M2_HOSTILE|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_COLLECT,
@@ -486,7 +487,7 @@
 	LVL(13, 12, 0, 90, -8), (G_GENO|1),
 	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_TENT, AD_DRIN, 2, 1),
 	  ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1),
-	  ATTK(AT_TENT, AD_DRIN, 2, 1), NO_ATTK),
+	  ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1)),
 	SIZ(1450, 400, 0, MS_HISS, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_FLY|M1_SEE_INVIS|M1_OMNIVORE,
 	M2_HOSTILE|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_COLLECT,
@@ -834,7 +835,7 @@
 	A(ATTK(AT_BITE, AD_PHYS, 1, 6),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(30, 30, 0, MS_SILENT, MZ_SMALL), 0, 0,
-	M1_TUNNEL/*LOGGING*/|M1_ANIMAL|M1_NOHANDS|M1_SWIM,
+	M1_TUNNEL/*LOGGING*/|M1_ANIMAL|M1_NOHANDS|M1_SWIM|M1_HERBIVORE,
 		/* In reality, they tunnel instead of cutting lumber.  Oh, well. */
 	M2_WANDER|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN),
 /*
@@ -1085,12 +1086,12 @@
 	CLR_YELLOW),
     MON("Angel", S_ANGEL,
 	LVL(14, 10, -4, 55, 12), (G_NOHELL|G_NOCORPSE|1),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 4),
-	  ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_MAGM, 2, 6),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), 
+	  ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_MAGC, AD_MAGM, 2, 6),
 	  NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, sizeof(struct epri), MS_CUSS, MZ_HUMAN),
 	MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, 0,
-	M1_HUMANOID|M1_SEE_INVIS,
+	M1_FLY|M1_HUMANOID|M1_SEE_INVIS,
 	M2_NOPOLY|M2_MINION|M2_STALK|M2_STRONG|M2_NASTY|M2_COLLECT,
 	M3_INFRAVISIBLE|M3_INFRAVISION, CLR_WHITE),
     MON("ki-rin", S_ANGEL,
@@ -1104,8 +1105,8 @@
 	M3_INFRAVISIBLE|M3_INFRAVISION, HI_GOLD),
     MON("Archon", S_ANGEL,
 	LVL(19, 16, -6, 80, 15), (G_NOHELL|G_NOCORPSE|1),
-	A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_GAZE, AD_BLND, 2, 6),
-	  ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 1, 8),
+	A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4),
+	  ATTK(AT_GAZE, AD_BLND, 2, 6), ATTK(AT_CLAW, AD_PHYS, 1, 8),
 	  ATTK(AT_MAGC, AD_SPEL, 4, 6), NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_CUSS, MZ_LARGE),
 	MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, 0,
@@ -1142,8 +1143,8 @@
 	A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRST, 0, 0),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(30, 20, 0, MS_SQEEK, MZ_SMALL), MR_SLEEP|MR_POISON, 0,
-	M1_FLY|M1_BREATHLESS|M1_ANIMAL|M1_NOHANDS|M1_POIS|M1_REGEN,
-	M2_UNDEAD|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BLACK),
+	M1_FLY|M1_ANIMAL|M1_NOHANDS|M1_POIS|M1_REGEN|M1_OMNIVORE,
+	M2_HOSTILE, M3_INFRAVISIBLE, CLR_BLACK),
 /*
  * Centaurs
  */
@@ -1362,7 +1363,7 @@
 	M2_WANDER|M2_STALK|M2_HOSTILE|M2_STRONG, M3_INFRAVISION, CLR_WHITE),
     MON("air elemental", S_ELEMENTAL,
 	LVL(8, 36, 2, 30, 0), (G_NOCORPSE|1),
-	A(ATTK(AT_ENGL, AD_PHYS, 2,10),
+	A(ATTK(AT_ENGL, AD_PHYS, 1, 10),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), MR_POISON|MR_STONE, 0,
 	M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_UNSOLID|M1_FLY,
@@ -1832,7 +1833,7 @@
 	M2_HOSTILE|M2_NEUTER, 0, CLR_BROWN),
     MON("black pudding", S_PUDDING,
 	LVL(10, 6, 6, 0, 0), (G_GENO|1),
-	A(ATTK(AT_BITE, AD_CORRODE, 3, 8), ATTK(AT_NONE, AD_CORRODE, 0, 0),
+	A(ATTK(AT_BITE, AD_CORR, 3, 8), ATTK(AT_NONE, AD_CORR, 0, 0),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(900, 250, 0, MS_SILENT, MZ_LARGE),
 	MR_COLD|MR_ELEC|MR_POISON|MR_ACID|MR_STONE, MR_COLD|MR_ELEC|MR_POISON,
@@ -1934,7 +1935,7 @@
  */
     MON("troll", S_TROLL,
 	LVL(7, 12, 4, 0, -3), (G_GENO|2),
-	A(ATTK(AT_CLAW, AD_PHYS, 4, 2), ATTK(AT_WEAP, AD_PHYS, 4, 2),
+	A(ATTK(AT_WEAP, AD_PHYS, 4, 2), ATTK(AT_CLAW, AD_PHYS, 4, 2),
 	  ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(800, 350, 0, MS_GRUNT, MZ_LARGE), 0, 0,
 	M1_HUMANOID|M1_REGEN|M1_CARNIVORE,
@@ -1942,7 +1943,7 @@
 	CLR_BROWN),
     MON("ice troll", S_TROLL,
 	LVL(9, 10, 2, 20, -3), (G_NOHELL|G_GENO|1),
-	A(ATTK(AT_CLAW, AD_COLD, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6),
+	A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_COLD, 2, 6),
 	  ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(1000, 300, 0, MS_GRUNT, MZ_LARGE), MR_COLD, MR_COLD,
 	M1_HUMANOID|M1_REGEN|M1_CARNIVORE,
@@ -1950,7 +1951,7 @@
 	CLR_WHITE),
     MON("rock troll", S_TROLL,
 	LVL(9, 12, 0, 0, -3), (G_GENO|1),
-	A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 3, 6),
+	A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 2, 8),
 	  ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(1200, 300, 0, MS_GRUNT, MZ_LARGE), 0, 0,
 	M1_HUMANOID|M1_REGEN|M1_CARNIVORE,
@@ -1958,7 +1959,7 @@
 	M3_INFRAVISIBLE|M3_INFRAVISION, CLR_CYAN),
     MON("water troll", S_TROLL,
 	LVL(11, 14, 4, 40, -3), (G_NOGEN|G_GENO),
-	A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 2, 8),
+	A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8),
 	  ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(1200, 350, 0, MS_GRUNT, MZ_LARGE), 0, 0,
 	M1_HUMANOID|M1_REGEN|M1_CARNIVORE|M1_SWIM,
@@ -1966,7 +1967,7 @@
 	CLR_BLUE),
     MON("Olog-hai", S_TROLL,
 	LVL(13, 12, -4, 0, -7), (G_GENO|1),
-	A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 3, 6),
+	A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 2, 8),
 	  ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(1500, 400, 0, MS_GRUNT, MZ_LARGE), 0, 0,
 	M1_HUMANOID|M1_REGEN|M1_CARNIVORE,
@@ -1986,7 +1987,7 @@
  * Vampires
  */
     MON("vampire", S_VAMPIRE,
-	LVL(10, 12, 1, 25, -8), (G_GENO|1),
+	LVL(10, 12, 1, 25, -8), (G_GENO|G_NOCORPSE|1),
 	A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRLI, 1, 6),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0,
@@ -1994,7 +1995,7 @@
 	M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY, M3_INFRAVISIBLE,
 	CLR_RED),
     MON("vampire lord", S_VAMPIRE,
-	LVL(12, 14, 0, 50, -9), (G_GENO|1),
+	LVL(12, 14, 0, 50, -9), (G_GENO|G_NOCORPSE|1),
 	A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_DRLI, 1, 8),
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0,
@@ -2003,7 +2004,7 @@
 	M3_INFRAVISIBLE, CLR_BLUE),
 #if 0	/* DEFERRED */
     MON("vampire mage", S_VAMPIRE,
-	LVL(20, 14, -4, 50, -9), (G_GENO|1),
+	LVL(20, 14, -4, 50, -9), (G_GENO|G_NOCORPSE|1),
 	A(ATTK(AT_CLAW, AD_DRLI, 2, 8), ATTK(AT_BITE, AD_DRLI, 1, 8),
 	  ATTK(AT_MAGC, AD_SPEL, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0,
@@ -2019,14 +2020,14 @@
 	M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN,
 	M2_NOPOLY|M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|
 	  M2_NASTY|M2_PRINCE|M2_MALE,
-	M3_WAITFORU|M3_WANTSBOOK|M3_INFRAVISIBLE, HI_LORD),
+	M3_WAITFORU|M3_WANTSCAND|M3_INFRAVISIBLE, HI_LORD),
 /*
  * Wraiths
  */
     MON("barrow wight", S_WRAITH,
 	LVL(3, 12, 5, 5, -3), (G_GENO|G_NOCORPSE|1),
-	A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0),
-	  ATTK(AT_WEAP, AD_DRLI, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_DRLI, 0, 0), ATTK(AT_MAGC, AD_SPEL, 0, 0),
+	  ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(1200, 0, 0, MS_SPELL, MZ_HUMAN), MR_COLD|MR_SLEEP|MR_POISON, 0,
 	M1_BREATHLESS|M1_HUMANOID,
 	M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_COLLECT, 0, CLR_GRAY),
@@ -2044,7 +2045,7 @@
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 0, 0, MS_SPELL, MZ_HUMAN),
 	MR_COLD|MR_SLEEP|MR_POISON, 0, M1_BREATHLESS|M1_HUMANOID,
-	M2_UNDEAD|M2_STALK|M2_STRONG|M2_HOSTILE|M2_MALE|M2_COLLECT,
+	M2_NOPOLY|M2_UNDEAD|M2_STALK|M2_STRONG|M2_HOSTILE|M2_MALE|M2_COLLECT,
 	0, HI_LORD),
 /*
  * Xorn
@@ -2054,7 +2055,7 @@
 	A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3),
 	  ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 4, 6),
 	  NO_ATTK, NO_ATTK),
-	SIZ(1200, 700, 0, MS_SILENT, MZ_MEDIUM),
+	SIZ(1200, 700, 0, MS_ROAR, MZ_MEDIUM),
 	MR_FIRE|MR_COLD|MR_STONE, MR_STONE,
 	M1_BREATHLESS|M1_WALLWALK|M1_THICK_HIDE|M1_METALLIVORE,
 	M2_HOSTILE|M2_STRONG, 0, CLR_BROWN),
@@ -2465,8 +2466,8 @@
 	 */
     MON("Medusa", S_HUMAN,
 	LVL(20, 12, 2, 50, -15), (G_NOGEN|G_UNIQ),
-	A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_GAZE, AD_STON, 0, 0),
-	  ATTK(AT_BITE, AD_DRST, 1, 6), ATTK(AT_WEAP, AD_PHYS, 2, 4),
+	A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 1, 8),
+	  ATTK(AT_GAZE, AD_STON, 0, 0), ATTK(AT_BITE, AD_DRST, 1, 6),
 	  NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HISS, MZ_LARGE),
 	MR_POISON|MR_STONE, MR_POISON|MR_STONE,
@@ -2578,7 +2579,8 @@
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_HUMAN), MR_FIRE|MR_POISON, 0,
 	M1_HUMANOID|M1_POIS,
-	M2_DEMON|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_FEMALE|M2_COLLECT,
+	M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_FEMALE|
+	  M2_COLLECT,
 	M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED),
     MON("barbed devil", S_DEMON,
 	LVL(8, 12, 0, 35, 8), (G_HELL|G_NOCORPSE|G_SGROUP|2),
@@ -2590,8 +2592,8 @@
     MON("marilith", S_DEMON,
 	LVL(7, 12, -6, 80, -12), (G_HELL|G_NOCORPSE|1),
 	A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4),
-	  ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4),
-	  ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4)),
+	  ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4),
+	  ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4)),
 	SIZ(WT_HUMAN, 400, 0, MS_CUSS, MZ_LARGE), MR_FIRE|MR_POISON, 0,
 	M1_HUMANOID|M1_SLITHY|M1_SEE_INVIS|M1_POIS,
 	M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_FEMALE|M2_COLLECT,
@@ -2668,7 +2670,7 @@
     MON("Yeenoghu", S_DEMON,
 	LVL(56, 18, -5, 80, -15), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ),
 	A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_WEAP, AD_CONF, 2, 8),
-	  ATTK(AT_WEAP, AD_PLYS, 1, 6), ATTK(AT_MAGC, AD_MAGM, 2, 6),
+	  ATTK(AT_CLAW, AD_PLYS, 1, 6), ATTK(AT_MAGC, AD_MAGM, 2, 6),
 	  NO_ATTK, NO_ATTK),
 	SIZ(900, 500, 0, MS_ORC, MZ_LARGE), MR_FIRE|MR_POISON, 0,
 	M1_FLY|M1_SEE_INVIS|M1_POIS,
@@ -2677,8 +2679,8 @@
 	M3_WANTSAMUL|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD),
     MON("Orcus", S_DEMON,
 	LVL(66, 9, -6, 85, -20), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ),
-	A(ATTK(AT_MAGC, AD_SPEL, 8, 6), ATTK(AT_WEAP, AD_PHYS, 3, 6),
-	  ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4),
+	A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 4),
+	  ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_MAGC, AD_SPEL, 8, 6),
 	  ATTK(AT_STNG, AD_DRST, 2, 4), NO_ATTK),
 	SIZ(1500, 500, 0, MS_ORC, MZ_HUGE), MR_FIRE|MR_POISON, 0,
 	M1_FLY|M1_SEE_INVIS|M1_POIS,
@@ -2811,7 +2813,7 @@
 	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(500, 350, 0, MS_SILENT, MZ_LARGE), 0, 0,
 	M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_SLITHY|M1_NOLIMBS|
-	  M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE,
+	  M1_CARNIVORE|M1_OVIPAROUS|M1_THICK_HIDE|M1_NOTAKE,
 	M2_HOSTILE, 0, CLR_GRAY),
     MON("giant eel", S_EEL,
 	LVL(5, 9, -1, 0, 0), (G_GENO|G_NOGEN),
@@ -2917,15 +2919,15 @@
  */
     MON("archeologist", S_HUMAN,
 	LVL(10, 12, 10, 1, 3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_TUNNEL|M1_NEEDPICK|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("barbarian", S_HUMAN,
 	LVL(10, 12, 10, 1, 0), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
@@ -2954,15 +2956,15 @@
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("knight", S_HUMAN,
 	LVL(10, 12, 10, 1, 3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("monk", S_HUMAN, 
 	LVL(10, 12, 10, 2, 0), G_NOGEN,
-	A(ATTK(AT_KICK, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_HERBIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT|M2_MALE,
@@ -2993,32 +2995,32 @@
 	HI_DOMESTIC),
     MON("rogue", S_HUMAN,
 	LVL(10, 12, 10, 1, -3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT,
 	M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("samurai", S_HUMAN,
 	LVL(10, 12, 10, 1, 3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
 #ifdef TOURIST
     MON("tourist", S_HUMAN,
 	LVL(10, 12, 10, 1, 0), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
 #endif
     MON("valkyrie", S_HUMAN,
 	LVL(10, 12, 10, 1, -1), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), MR_COLD, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE,
@@ -3094,8 +3096,8 @@
 	M3_CLOSE|M3_INFRAVISIBLE, HI_LORD),
     MON("King Arthur", S_HUMAN,
 	LVL(20, 12, 0, 40, 20), (G_NOGEN|G_UNIQ),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE|
@@ -3143,8 +3145,8 @@
 	M3_CLOSE|M3_INFRAVISIBLE, HI_LORD),
     MON("Lord Sato", S_HUMAN,
 	LVL(20, 12, 0, 30, 20), (G_NOGEN|G_UNIQ),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE|
@@ -3153,8 +3155,8 @@
 #ifdef TOURIST
     MON("Twoflower", S_HUMAN,
 	LVL(20, 12, 10, 20, 0), (G_NOGEN|G_UNIQ),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE|
@@ -3163,21 +3165,22 @@
 #endif
     MON("Norn", S_HUMAN,
 	LVL(20, 12, 0, 80, 0), (G_NOGEN|G_UNIQ),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), MR_COLD, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_FEMALE|
 	  M2_COLLECT|M2_MAGIC,
 	M3_CLOSE|M3_INFRAVISIBLE, HI_LORD),
-    MON("Wizard of Balance", S_HUMAN,
+    MON("Neferet the Green", S_HUMAN,
 	LVL(20, 12, 0, 60, 0), (G_NOGEN|G_UNIQ),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 2, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
-	M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT|M2_MAGIC,
-	M3_CLOSE|M3_INFRAVISIBLE, HI_LORD),
+	M2_NOPOLY|M2_HUMAN|M2_FEMALE|M2_PNAME|M2_PEACEFUL|
+	  M2_STRONG|M2_COLLECT|M2_MAGIC,
+	M3_CLOSE|M3_INFRAVISIBLE, CLR_GREEN),
 /*
  * quest nemeses
  */
@@ -3192,8 +3195,8 @@
 	M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, CLR_RED),
     MON("Thoth Amon", S_HUMAN,
 	LVL(16, 12, 0, 10, -14), (G_NOGEN|G_UNIQ|G_NOCORPSE),
-	A(ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_MAGC, AD_SPEL, 0, 0),
-	  ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_SAMU, 1, 4),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0),
+	  ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4),
 	  NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), MR_POISON, 0,
 	M1_HUMANOID|M1_OMNIVORE,
@@ -3311,8 +3314,8 @@
 	M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, HI_LORD),
     MON("Dark One", S_HUMAN,
 	LVL(15, 12, 0, 80, -10), (G_NOGEN|G_UNIQ|G_NOCORPSE),
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0),
-	  ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_SAMU, 1, 4),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  ATTK(AT_CLAW, AD_SAMU, 1, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0),
 	  NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
@@ -3366,8 +3369,8 @@
 	HI_DOMESTIC),
     MON("page", S_HUMAN,
 	LVL(5, 12, 10, 10, 3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE,
@@ -3398,24 +3401,24 @@
 	M3_INFRAVISION|M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("thug", S_HUMAN,
 	LVL(5, 12, 10, 10, -3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_GREEDY|M2_COLLECT,
 	M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("ninja", S_HUMAN,
 	LVL(5, 12, 10, 10, 3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_HOSTILE|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE,
 	HI_DOMESTIC),
     MON("roshi", S_HUMAN,
 	LVL(5, 12, 10, 10, 3), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE,
@@ -3432,8 +3435,8 @@
 #endif
     MON("warrior", S_HUMAN,
 	LVL(5, 12, 10, 10, -1), G_NOGEN,
-	A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
-	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+	A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8),
+	  NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
 	SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0,
 	M1_HUMANOID|M1_OMNIVORE,
 	M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE,
diff -Naurd ../nethack-3.3.1/src/mplayer.c ./src/mplayer.c
--- ../nethack-3.3.1/src/mplayer.c Sun Jul 16 02:46:47 2000
+++ ./src/mplayer.c Fri Mar 22 14:41:04 2002
@@ -9,7 +9,7 @@
 STATIC_DCL void FDECL(mk_mplayer_armor, (struct monst *, SHORT_P));
 
 /* These are the names of those who
- * contributed to the development of NetHack 3.2/3.3.
+ * contributed to the development of NetHack 3.2/3.3/3.4.
  *
  * Keep in alphabetical order within teams.
  * Same first name is entered once within each team.
@@ -18,17 +18,18 @@
 	/* devteam */
 	"Dave", "Dean", "Eric", "Izchak", "Janet", "Jessie",
 	"Ken", "Kevin", "Michael", "Mike", "Pat", "Paul", "Steve", "Timo",
+	"Warwick",
 	/* PC team */
 	"Bill", "Eric", "Keizo", "Ken", "Kevin", "Michael", "Mike", "Paul",
 	"Stephen", "Steve", "Timo", "Yitzhak",
 	/* Amiga team */
-	"Andy", "Gregg", "Keni", "Mike", "Olaf", "Richard",
+	"Andy", "Gregg", "Janne", "Keni", "Mike", "Olaf", "Richard",
 	/* Mac team */
 	"Andy", "Chris", "Dean", "Jon", "Jonathan", "Kevin", "Wang",
 	/* Atari team */
-	"Eric", "Warwick",
+	"Eric", "Marvin", "Warwick",
 	/* NT team */
-	"Michael",
+	"Alex", "Dion", "Michael",
 	/* OS/2 team */
 	"Helge", "Ron", "Timo",
 	/* VMS team */
diff -Naurd ../nethack-3.3.1/src/mthrowu.c ./src/mthrowu.c
--- ../nethack-3.3.1/src/mthrowu.c Sun Jul 16 02:18:53 2000
+++ ./src/mthrowu.c Fri Mar 22 14:40:55 2002
@@ -32,9 +32,9 @@
 				"strange breath #9"
 };
 
-
+/* hero is hit by something other than a monster */
 int
-thitu(tlev, dam, obj, name)	/* u is hit by sth, but not a monster */
+thitu(tlev, dam, obj, name)
 int tlev, dam;
 struct obj *obj;
 const char *name;	/* if null, then format `obj' */
@@ -48,7 +48,8 @@
 	    unsigned save_ocknown;
 
 	    if (!obj) panic("thitu: name & obj both null?");
-	    name = strcpy(onmbuf, (obj->quan > 1L) ? doname(obj) : xname(obj));
+	    name = strcpy(onmbuf,
+			 (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
 	    /* killer name should be more specific; however, exact info
 	       like blessed/cursed and rustproof make things too verbose */
 	    otmp = *obj;
@@ -71,11 +72,11 @@
 
 	if(u.uac + tlev <= rnd(20)) {
 		if(Blind || !flags.verbose) pline("It misses.");
-		else You("are almost hit by %s!", onm);
+		else You("are almost hit by %s.", onm);
 		return(0);
 	} else {
 		if(Blind || !flags.verbose) You("are hit!");
-		else You("are hit by %s!", onm);
+		else You("are hit by %s%s", onm, exclam(dam));
 
 		if (obj && objects[obj->otyp].oc_material == SILVER
 				&& hates_silver(youmonst.data)) {
@@ -129,6 +130,10 @@
 		if (!objgone) {
 			if (!flooreffects(obj,x,y,"fall")) { /* don't double-dip on damage */
 			    place_object(obj, x, y);
+			    if (!mtmp && x == u.ux && y == u.uy)
+				mtmp = &youmonst;
+			    if (mtmp && ohit)
+				passive_obj(mtmp, obj, (struct attack *)0);
 			    stackobj(obj);
 			    retvalu = 0;
 			}
@@ -161,7 +166,7 @@
 	tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
 	if (tmp < rnd(20)) {
 	    if (!ismimic) {
-		if (vis) miss(distant_name(otmp, xname), mtmp);
+		if (vis) miss(distant_name(otmp, mshot_xname), mtmp);
 		else if (verbose) pline("It is missed.");
 	    }
 	    if (!range) { /* Last position; object drops */
@@ -180,7 +185,7 @@
 		damage = 0;
 	    if (ismimic) seemimic(mtmp);
 	    mtmp->msleeping = 0;
-	    if (vis) hit(distant_name(otmp,xname), mtmp, exclam(damage));
+	    if (vis) hit(distant_name(otmp,mshot_xname), mtmp, exclam(damage));
 	    else if (verbose) pline("It is hit%s", exclam(damage));
 
 	    if (otmp->opoisoned) {
@@ -218,7 +223,8 @@
 		    pline("%s is %s!", Monnam(mtmp),
 			(nonliving(mtmp->data) || !vis)
 			? "destroyed" : "killed");
-		mondied(mtmp);
+		if (!flags.mon_moving) xkilled(mtmp,0);
+		else mondied(mtmp);
 	    }
 
 	    if (can_blnd((struct monst*)0, mtmp,
@@ -270,14 +276,14 @@
 	    /* not possibly_unwield, which checks the object's */
 	    /* location, not its existence */
 	    if (MON_WEP(mon) == obj) {
-		    obj->owornmask &= ~W_WEP;
+		    setmnotwielded(mon,obj);
 		    MON_NOWEP(mon);
 	    }
 	    obj_extract_self(obj);
 	    singleobj = obj;
 	    obj = (struct obj *) 0;
 	} else {
-	    singleobj = splitobj(obj, obj->quan - 1L);
+	    singleobj = splitobj(obj, 1L);
 	    obj_extract_self(singleobj);
 	}
 
@@ -288,8 +294,8 @@
 		if(is_ammo(singleobj))
 		    pline("%s misfires!", Monnam(mon));
 		else
-		    pline("%s slips as %s throws it!",
-			  The(xname(singleobj)), mon_nam(mon));
+		    pline("%s as %s throws it!",
+			  Tobjnam(singleobj, "slip"), mon_nam(mon));
 	    }
 	    dx = rn2(3)-1;
 	    dy = rn2(3)-1;
@@ -389,26 +395,33 @@
 			poisoned(onmbuf, A_STR, knmbuf, 10);
 			objects[otmp.otyp].oc_name_known = save_ocknown;
 		    }
-		    if(hitu && (singleobj->otyp == CREAM_PIE ||
-				 singleobj->otyp == BLINDING_VENOM)) {
+		    if(hitu &&
+		       can_blnd((struct monst*)0, &youmonst,
+				(uchar)(singleobj->otyp == BLINDING_VENOM ?
+					AT_SPIT : AT_WEAP), singleobj)) {
 			blindinc = rnd(25);
 			if(singleobj->otyp == CREAM_PIE) {
 			    if(!Blind) pline("Yecch!  You've been creamed.");
-			    else	pline("There's %s sticky all over your %s.",
-					    something,
-					    body_part(FACE));
-			} else {	/* venom in the eyes */
-			    if(ublindf) /* nothing */ ;
-			    else if(!Blind) pline_The("venom blinds you.");
-			    else Your("%s sting.", makeplural(body_part(EYE)));
+			    else pline("There's %s sticky all over your %s.",
+				       something,
+				       body_part(FACE));
+			} else if(singleobj->otyp == BLINDING_VENOM) {
+			    int num_eyes = eyecount(youmonst.data);
+			    /* venom in the eyes */
+			    if(!Blind) pline_The("venom blinds you.");
+			    else Your("%s sting%s.",
+				      (num_eyes == 1) ? body_part(EYE) :
+						makeplural(body_part(EYE)),
+				      (num_eyes == 1) ? "s" : "");
 			}
 		    }
 		    if (hitu && singleobj->otyp == EGG) {
 			if (!Stone_resistance
-				&& !(poly_when_stoned(youmonst.data) &&
-				    polymon(PM_STONE_GOLEM)))
+			    && !(poly_when_stoned(youmonst.data) &&
+				 polymon(PM_STONE_GOLEM))) {
 			    Stoned = 5;
 			    killer = (char *) 0;
+			}
 		    }
 		    stop_occupation();
 		    if (hitu || !range) {
@@ -434,10 +447,11 @@
 	tmp_at(bhitpos.x, bhitpos.y);
 	delay_output();
 	tmp_at(DISP_END, 0);
-	/* blindfolds, towels, & lenses keep substances out of your eyes */
-	if (blindinc && !ublindf) {
+
+	if (blindinc) {
 		u.ucreamed += blindinc;
-		make_blinded(Blinded + blindinc,FALSE);
+		make_blinded(Blinded + (long)blindinc, FALSE);
+		if (!Blind) Your(vision_clears);
 	}
 }
 
@@ -452,6 +466,7 @@
 {
 	if (obj->quan > 1L) {
 		obj->quan--;
+		obj->owt = weight(obj);
 	} else {
 		obj_extract_self(obj);
 		possibly_unwield(mon);
@@ -459,22 +474,23 @@
 		    mon->misc_worn_check &= ~obj->owornmask;
 		    update_mon_intrinsics(mon, obj, FALSE);
 		}
-		dealloc_obj(obj);
+		obfree(obj, (struct obj*) 0);
 	}
 }
 
 #endif /* OVLB */
 #ifdef OVL1
 
+/* monster attempts ranged weapon attack against player */
 void
-thrwmu(mtmp)	/* monster throws item at you */
-register struct monst *mtmp;
+thrwmu(mtmp)
+struct monst *mtmp;
 {
 	struct obj *otmp, *mwep;
-	register xchar x, y;
-	boolean ispole;
+	xchar x, y;
 	schar skill;
-	int multishot = 1;
+	int multishot;
+	const char *onm;
 
 	/* Rearranged beginning so monsters can use polearms not in a line */
 	if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
@@ -486,102 +502,114 @@
 	/* Pick a weapon */
 	otmp = select_rwep(mtmp);
 	if (!otmp) return;
-	ispole = is_pole(otmp);
-	skill = objects[otmp->otyp].oc_skill;
-	mwep = MON_WEP(mtmp);		/* wielded weapon */
 
-	if(ispole || lined_up(mtmp)) {
-		/* If you are coming toward the monster, the monster
-		 * should try to soften you up with missiles.  If you are
-		 * going away, you are probably hurt or running.  Give
-		 * chase, but if you are getting too far away, throw.
-		 */
-		x = mtmp->mx;
-		y = mtmp->my;
-		if(ispole || !URETREATING(x,y) ||
-		   !rn2(BOLT_LIM-distmin(x,y,mtmp->mux,mtmp->muy)))
-		{
-		    const char *verb = "throws";
+	if (is_pole(otmp)) {
+	    int dam, hitv;
 
-		    if (otmp->otyp == ARROW
-			|| otmp->otyp == ELVEN_ARROW
-			|| otmp->otyp == ORCISH_ARROW
-			|| otmp->otyp == YA
-			|| otmp->otyp == CROSSBOW_BOLT) verb = "shoots";
-		    if (ispole) {
-			if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <=
-				POLE_LIM && couldsee(mtmp->mx, mtmp->my))
-			    verb = "thrusts";
-			else return; /* Out of range, or intervening wall */
-		    }
+	    if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM ||
+		    !couldsee(mtmp->mx, mtmp->my))
+		return;	/* Out of range, or intervening wall */
 
-		    if (canseemon(mtmp)) {
-			pline("%s %s %s!", Monnam(mtmp), verb,
-			      obj_is_pname(otmp) ?
-			      the(singular(otmp, xname)) :
-			      an(singular(otmp, xname)));
-		    }
+	    if (canseemon(mtmp)) {
+		onm = xname(otmp);
+		pline("%s thrusts %s.", Monnam(mtmp),
+		      obj_is_pname(otmp) ? the(onm) : an(onm));
+	    }
 
-			/* Use a pole */
-			if (ispole) {
-				int dam = dmgval(otmp, &youmonst);
-				int hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my);
+	    dam = dmgval(otmp, &youmonst);
+	    hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my);
+	    if (hitv < -4) hitv = -4;
+	    if (bigmonst(youmonst.data)) hitv++;
+	    hitv += 8 + otmp->spe;
+	    if (dam < 1) dam = 1;
 
-				if (hitv < -4) hitv = -4;
-				if (bigmonst(youmonst.data)) hitv++;
-				hitv += 8 + otmp->spe;
-				if (dam < 1) dam = 1;
-				(void) thitu(hitv, dam, otmp, (char *)0);
+	    (void) thitu(hitv, dam, otmp, (char *)0);
+	    stop_occupation();
+	    return;
+	}
 
-				return;
-			}
+	x = mtmp->mx;
+	y = mtmp->my;
+	/* If you are coming toward the monster, the monster
+	 * should try to soften you up with missiles.  If you are
+	 * going away, you are probably hurt or running.  Give
+	 * chase, but if you are getting too far away, throw.
+	 */
+	if (!lined_up(mtmp) ||
+		(URETREATING(x,y) &&
+			rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy))))
+	    return;
 
-		    /* Multishot calculations */
-		    if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER ||
-				skill == -P_DART || skill == -P_SHURIKEN) &&
-			    !mtmp->mconf) {
-			/* Assumes lords are skilled, princes are expert */
-			if (is_lord(mtmp->data)) multishot++;
-			if (is_prince(mtmp->data)) multishot += 2;
+	skill = objects[otmp->otyp].oc_skill;
+	mwep = MON_WEP(mtmp);		/* wielded weapon */
 
-			switch (monsndx(mtmp->data)) {
-			case PM_RANGER:
-			    multishot++;
-			    break;
-			case PM_ROGUE:
-			    if (skill == P_DAGGER) multishot++;
-			    break;
-			case PM_SAMURAI:
-			    if (otmp->otyp == YA && mwep &&
-				    mwep->otyp == YUMI) multishot++;
-			    break;
-			default:
-			    break;
-			}
-			{	/* racial bonus */
-			    if (is_elf(mtmp->data) &&
-				    otmp->otyp == ELVEN_ARROW &&
-				    mwep && mwep->otyp == ELVEN_BOW)
-				multishot++;
-			    else if (is_orc(mtmp->data) &&
-				    otmp->otyp == ORCISH_ARROW &&
-				    mwep && mwep->otyp == ORCISH_BOW)
-				multishot++;
-			}
-		    }
-		    if (otmp->quan < multishot) multishot = (int)otmp->quan;
-		    if (multishot < 1) multishot = 1;
-		    else multishot = rnd(multishot);
-		    while (multishot-- > 0)
-			m_throw(mtmp, mtmp->mx, mtmp->my,
-				sgn(tbx), sgn(tby),
-				distmin(mtmp->mx, mtmp->my,
-					mtmp->mux, mtmp->muy),
-				otmp);
-		    nomul(0);
-		    return;
-		}
+	/* Multishot calculations */
+	multishot = 1;
+	if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER ||
+		skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) {
+	    /* Assumes lords are skilled, princes are expert */
+	    if (is_prince(mtmp->data)) multishot += 2;
+	    else if (is_lord(mtmp->data)) multishot++;
+
+	    switch (monsndx(mtmp->data)) {
+	    case PM_RANGER:
+		    multishot++;
+		    break;
+	    case PM_ROGUE:
+		    if (skill == P_DAGGER) multishot++;
+		    break;
+	    case PM_NINJA:
+	    case PM_SAMURAI:
+		    if (otmp->otyp == YA && mwep &&
+			mwep->otyp == YUMI) multishot++;
+		    break;
+	    default:
+		break;
+	    }
+	    /* racial bonus */
+	    if ((is_elf(mtmp->data) &&
+		    otmp->otyp == ELVEN_ARROW &&
+		    mwep && mwep->otyp == ELVEN_BOW) ||
+		(is_orc(mtmp->data) &&
+		    otmp->otyp == ORCISH_ARROW &&
+		    mwep && mwep->otyp == ORCISH_BOW))
+		multishot++;
+
+	    if ((long)multishot > otmp->quan) multishot = (int)otmp->quan;
+	    if (multishot < 1) multishot = 1;
+	    else multishot = rnd(multishot);
 	}
+
+	if (canseemon(mtmp)) {
+	    char onmbuf[BUFSZ];
+
+	    if (multishot > 1) {
+		/* "N arrows"; multishot > 1 implies otmp->quan > 1, so
+		   xname()'s result will already be pluralized */
+		Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
+		onm = onmbuf;
+	    } else {
+		/* "an arrow" */
+		onm = singular(otmp, xname);
+		onm = obj_is_pname(otmp) ? the(onm) : an(onm);
+	    }
+	    m_shot.s = ammo_and_launcher(otmp,mwep) ? TRUE : FALSE;
+	    pline("%s %s %s!", Monnam(mtmp),
+		  m_shot.s ? "shoots" : "throws", onm);
+	    m_shot.o = otmp->otyp;
+	} else {
+	    m_shot.o = STRANGE_OBJECT;	/* don't give multishot feedback */
+	}
+
+	m_shot.n = multishot;
+	for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++)
+	    m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
+		    distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
+	m_shot.n = m_shot.i = 0;
+	m_shot.o = STRANGE_OBJECT;
+	m_shot.s = FALSE;
+
+	nomul(0);
 }
 
 #endif /* OVL1 */
diff -Naurd ../nethack-3.3.1/src/muse.c ./src/muse.c
--- ../nethack-3.3.1/src/muse.c Mon Jul 17 01:16:28 2000
+++ ./src/muse.c Fri Mar 22 14:40:55 2002
@@ -19,6 +19,7 @@
  * don't know not to read scrolls, etc....
  */
 
+STATIC_DCL struct permonst *FDECL(muse_newcham_mon, (struct monst *));
 STATIC_DCL int FDECL(precheck, (struct monst *,struct obj *));
 STATIC_DCL void FDECL(mzapmsg, (struct monst *,struct obj *,BOOLEAN_P));
 STATIC_DCL void FDECL(mreadmsg, (struct monst *,struct obj *));
@@ -28,6 +29,8 @@
 	(struct monst *,int,int FDECL((*),(MONST_P,OBJ_P)),
 	int FDECL((*),(OBJ_P,OBJ_P)),struct obj *));
 STATIC_DCL void FDECL(you_aggravate, (struct monst *));
+STATIC_DCL void FDECL(mon_consume_unstone, (struct monst *,struct obj *,
+	BOOLEAN_P,BOOLEAN_P));
 
 static struct musable {
 	struct obj *offensive;
@@ -153,7 +156,7 @@
 					"nearby" : "distant");
 	} else if (self)
 		pline("%s zaps %sself with %s!",
-		      Monnam(mtmp), him[pronoun_gender(mtmp)], doname(otmp));
+		      Monnam(mtmp), mhim(mtmp), doname(otmp));
 	else {
 		pline("%s zaps %s!", Monnam(mtmp), an(xname(otmp)));
 		stop_occupation();
@@ -196,7 +199,7 @@
 
 	if (mtmp->mconf)
 	    pline("Being confused, %s mispronounces the magic words...",
-		  vismon ? mon_nam(mtmp) : he[pronoun_gender(mtmp)]);
+		  vismon ? mon_nam(mtmp) : mhe(mtmp));
 }
 
 STATIC_OVL void
@@ -234,6 +237,7 @@
 #define MUSE_BUGLE 16
 #define MUSE_UNICORN_HORN 17
 #define MUSE_POT_FULL_HEALING 18
+#define MUSE_LIZARD_CORPSE 19
 /*
 #define MUSE_INNATE_TPT 9999
  * We cannot use this.  Since monsters get unlimited teleportation, if they
@@ -281,6 +285,16 @@
 	    }
 	}
 
+	if (mtmp->mconf) {
+	    for(obj = mtmp->minvent; obj; obj = obj->nobj) {
+		if (obj->otyp == CORPSE && obj->corpsenm == PM_LIZARD) {
+		    m.defensive = obj;
+		    m.has_defense = MUSE_LIZARD_CORPSE;
+		    return TRUE;
+		}
+	    }
+	}
+
 	/* It so happens there are two unrelated cases when we might want to
 	 * check specifically for healing alone.  The first is when the monster
 	 * is blind (healing cures blindness).  The second is when the monster
@@ -419,6 +433,8 @@
 		if (obj->otyp == WAN_DIGGING && obj->spe > 0 && !stuck && !t
 		    && !mtmp->isshk && !mtmp->isgd && !mtmp->ispriest
 		    && !is_floater(mtmp->data)
+		    /* monsters digging in Sokoban can ruin things */
+		    && !In_sokoban(&u.uz)
 		    /* digging wouldn't be effective; assume they know that */
 		    && !(levl[x][y].wall_info & W_NONDIGGABLE)
 		    && !(Is_botlevel(&u.uz) || In_endgame(&u.uz))
@@ -509,7 +525,7 @@
 	   rushing right straight back; don't override if already scared */
 	fleetim = !mtmp->mflee ? (33 - (30 * mtmp->mhp / mtmp->mhpmax)) : 0;
 #define m_flee(m)	if (fleetim && !m->iswiz) \
-			{ m->mflee = 1; m->mfleetim = fleetim; }
+			{ monflee(m, fleetim, FALSE, FALSE); }
 
 	switch(m.has_defense) {
 	case MUSE_UNICORN_HORN:
@@ -701,6 +717,10 @@
 			pline("%s %s into a %s!", Monnam(mtmp),
 			makeplural(locomotion(mtmp->data, "jump")),
 			t->ttyp == TRAPDOOR ? "trap door" : "hole");
+			if (levl[trapx][trapy].typ == SCORR) {
+			    levl[trapx][trapy].typ = CORR;
+			    unblock_point(trapx, trapy);
+			}
 			seetrap(t_at(trapx,trapy));
 		}
 
@@ -734,7 +754,7 @@
 			(dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz) - 3)) {
 		    if (vismon) pline(
      "As %s climbs the stairs, a mysterious force momentarily surrounds %s...",
-				     mon_nam(mtmp), him[pronoun_gender(mtmp)]);
+				     mon_nam(mtmp), mhim(mtmp));
 		    /* simpler than for the player; this will usually be
 		       the Wizard and he'll immediately go right to the
 		       upstairs, so there's not much point in having any
@@ -787,6 +807,10 @@
 		if (vis) {
 			pline("%s %s onto a teleport trap!", Monnam(mtmp),
 				makeplural(locomotion(mtmp->data, "jump")));
+			if (levl[trapx][trapy].typ == SCORR) {
+			    levl[trapx][trapy].typ = CORR;
+			    unblock_point(trapx, trapy);
+			}
 			seetrap(t_at(trapx,trapy));
 		}
 		/*  don't use rloc_to() because worm tails must "move" */
@@ -839,6 +863,10 @@
 		if (oseen) makeknown(otmp->otyp);
 		m_useup(mtmp, otmp);
 		return 2;
+	case MUSE_LIZARD_CORPSE:
+		/* not actually called for its unstoning effect */
+		mon_consume_unstone(mtmp, otmp, FALSE, FALSE);
+		return 2;
 	case 0: return 0; /* i.e. an exploded wand */
 	default: impossible("%s wanted to perform action %d?", Monnam(mtmp),
 			m.has_defense);
@@ -987,7 +1015,7 @@
 			m.has_offense = MUSE_POT_PARALYSIS;
 		}
 		nomore(MUSE_POT_BLINDNESS);
-		if(obj->otyp == POT_BLINDNESS) {
+		if(obj->otyp == POT_BLINDNESS && !attacktype(mtmp->data, AT_GAZE)) {
 			m.offensive = obj;
 			m.has_offense = MUSE_POT_BLINDNESS;
 		}
@@ -1332,7 +1360,7 @@
 					if (canspotmon(mtmp2))
 					    pline("%s's %s does not protect %s.",
 						Monnam(mtmp2), xname(helmet),
-						him[pronoun_gender(mtmp2)]);
+						mhim(mtmp2));
 				    }
 				}
 	    	    	    	mtmp2->mhp -= mdmg;
@@ -1352,7 +1380,7 @@
 		}
 		m_useup(mtmp, otmp);
 		/* Attack the player */
-		if (dist2(mmx, mmy, u.ux, u.uy) == 1 && !otmp->cursed) {
+		if (distmin(mmx, mmy, u.ux, u.uy) == 1 && !otmp->cursed) {
 		    int dmg;
 		    struct obj *otmp2;
 
@@ -1579,7 +1607,7 @@
 		if(obj->otyp == WAN_MAKE_INVISIBLE && obj->spe > 0 &&
 		    !mtmp->minvis && !mtmp->invis_blkd &&
 		    (!mtmp->mpeaceful || See_invisible) &&
-		    (mdat != &mons[PM_MEDUSA] || mtmp->mcan)) {
+		    (!attacktype(mtmp->data, AT_GAZE) || mtmp->mcan)) {
 			m.misc = obj;
 			m.has_misc = MUSE_WAN_MAKE_INVISIBLE;
 		}
@@ -1587,7 +1615,7 @@
 		if(obj->otyp == POT_INVISIBILITY &&
 		    !mtmp->minvis && !mtmp->invis_blkd &&
 		    (!mtmp->mpeaceful || See_invisible) &&
-		    (mdat != &mons[PM_MEDUSA] || mtmp->mcan)) {
+		    (!attacktype(mtmp->data, AT_GAZE) || mtmp->mcan)) {
 			m.misc = obj;
 			m.has_misc = MUSE_POT_INVISIBILITY;
 		}
@@ -1620,6 +1648,23 @@
 #undef nomore
 }
 
+/* type of monster to polymorph into; defaults to one suitable for the
+   current level rather than the totally arbitrary choice of newcham() */
+static struct permonst *
+muse_newcham_mon(mon)
+struct monst *mon;
+{
+	struct obj *m_armr;
+
+	if ((m_armr = which_armor(mon, W_ARM)) != 0) {
+	    if (Is_dragon_scales(m_armr))
+		return Dragon_scales_to_pm(m_armr);
+	    else if (Is_dragon_mail(m_armr))
+		return Dragon_mail_to_pm(m_armr);
+	}
+	return rndmonst();
+}
+
 int
 use_misc(mtmp)
 struct monst *mtmp;
@@ -1701,7 +1746,7 @@
 	case MUSE_WAN_SPEED_MONSTER:
 		mzapmsg(mtmp, otmp, TRUE);
 		otmp->spe--;
-		mon_adjust_speed(mtmp, 1);
+		mon_adjust_speed(mtmp, 1, otmp);
 		return 2;
 	case MUSE_POT_SPEED:
 		mquaffmsg(mtmp, otmp);
@@ -1709,22 +1754,19 @@
 		   different methods of maintaining speed ratings:
 		   player's character becomes "very fast" temporarily;
 		   monster becomes "one stage faster" permanently */
-		if (vismon)
-		    pline("%s is suddenly moving faster.", Monnam(mtmp));
-		if (oseen) makeknown(POT_SPEED);
-		mon_adjust_speed(mtmp, 1);
+		mon_adjust_speed(mtmp, 1, otmp);
 		m_useup(mtmp, otmp);
 		return 2;
 	case MUSE_WAN_POLYMORPH:
 		mzapmsg(mtmp, otmp, TRUE);
 		otmp->spe--;
-		(void) newcham(mtmp, rndmonst());
+		(void) newcham(mtmp, muse_newcham_mon(mtmp), TRUE);
 		if (oseen) makeknown(WAN_POLYMORPH);
 		return 2;
 	case MUSE_POT_POLYMORPH:
 		mquaffmsg(mtmp, otmp);
 		if (vismon) pline("%s suddenly mutates!", Monnam(mtmp));
-		(void) newcham(mtmp, rndmonst());
+		(void) newcham(mtmp, muse_newcham_mon(mtmp), FALSE);
 		if (oseen) makeknown(POT_POLYMORPH);
 		m_useup(mtmp, otmp);
 		return 2;
@@ -1742,7 +1784,7 @@
 		if (mtmp->wormno) worm_move(mtmp);
 		newsym(trapx, trapy);
 
-		(void) newcham(mtmp, (struct permonst *)0);
+		(void) newcham(mtmp, (struct permonst *)0, FALSE);
 		return 2;
 	case MUSE_BULLWHIP:
 		/* attempt to disarm hero */
@@ -1767,16 +1809,21 @@
 		    }
 		    pline("%s wraps around %s you're wielding!",
 			  The_whip, the_weapon);
-		    if (obj->cursed) {
-			pline("%s is welded to your %s%c",
-			      (obj->quan == 1L) ? "It" : "They",
+		    if (welded(obj)) {
+			pline("%s welded to your %s%c",
+			      !is_plural(obj) ? "It is" : "They are",
 			      hand, !obj->bknown ? '!' : '.');
-			obj->bknown = 1;
+			/* obj->bknown = 1; */ /* welded() takes care of this */
 			where_to = 0;
 		    }
 		    if (!where_to) {
 			pline_The("whip slips free.");  /* not `The_whip' */
 			return 1;
+		    } else if (where_to == 3 && hates_silver(mtmp->data) &&
+			    objects[obj->otyp].oc_material == SILVER) {
+			/* this monster won't want to catch a silver
+			   weapon; drop it at hero's feet instead */
+			where_to = 2;
 		    }
 		    freeinv(obj);
 		    uwepgone();
@@ -1784,10 +1831,6 @@
 			case 1:		/* onto floor beneath mon */
 			    pline("%s yanks %s from your %s!", Monnam(mtmp),
 				  the_weapon, hand);
-			    if (obj->otyp == CRYSKNIFE && (!obj->oerodeproof || !rn2(10))) {
-			    	obj->otyp = WORM_TOOTH;
-			    	obj->oerodeproof = 0;
-			    }
 			    place_object(obj, mtmp->mx, mtmp->my);
 			    break;
 			case 2:		/* onto floor beneath you */
@@ -1886,12 +1929,14 @@
 	    return FALSE;
 
 	if (typ == WAN_MAKE_INVISIBLE || typ == POT_INVISIBILITY)
-	    return (boolean)(!mon->minvis && !mon->invis_blkd);
+	    return (boolean)(!mon->minvis && !mon->invis_blkd && !attacktype(mon->data, AT_GAZE));
 	if (typ == WAN_SPEED_MONSTER || typ == POT_SPEED)
 	    return (boolean)(mon->mspeed != MFAST);
 
 	switch (obj->oclass) {
 	case WAND_CLASS:
+	    if (obj->spe <= 0)
+		return FALSE;
 	    if (typ == WAN_DIGGING)
 		return (boolean)(!is_floater(mon->data));
 	    if (typ == WAN_POLYMORPH)
@@ -1911,9 +1956,10 @@
 		    typ == POT_PARALYSIS ||
 		    typ == POT_SLEEPING ||
 		    typ == POT_ACID ||
-		    typ == POT_BLINDNESS ||
 		    typ == POT_CONFUSION)
 		return TRUE;
+	    if (typ == POT_BLINDNESS && !attacktype(mon->data, AT_GAZE))
+		return TRUE;
 	    break;
 	case SCROLL_CLASS:
 	    if (typ == SCR_TELEPORTATION || typ == SCR_CREATE_MONSTER
@@ -1932,7 +1978,7 @@
 	    if (typ == UNICORN_HORN)
 		return (boolean)(!obj->cursed && !is_unicorn(mon->data));
 	    if (typ == FROST_HORN || typ == FIRE_HORN)
-		return TRUE;
+		return (obj->spe > 0);
 	    break;
 	case FOOD_CLASS:
 	    if (typ == CORPSE)
@@ -1940,7 +1986,8 @@
 				    touch_petrifies(&mons[obj->corpsenm])) ||
 				(!resists_ston(mon) &&
 				    (obj->corpsenm == PM_LIZARD ||
-					acidic(&mons[obj->corpsenm]))));
+					(acidic(&mons[obj->corpsenm]) &&
+					 obj->corpsenm != PM_GREEN_SLIME))));
 	    if (typ == EGG)
 		return (boolean)(touch_petrifies(&mons[obj->corpsenm]));
 	    break;
@@ -1976,7 +2023,8 @@
 	    if (str)
 		pline(str, s_suffix(mon_nam(mon)), "armor");
 	    return TRUE;
-	} else if (mon->data == &mons[PM_SILVER_DRAGON]) {
+	} else if (mon->data == &mons[PM_SILVER_DRAGON] ||
+		mon->data == &mons[PM_CHROMATIC_DRAGON]) {
 	    /* Silver dragons only reflect when mature; babies do not */
 	    if (str)
 		pline(str, s_suffix(mon_nam(mon)), "scales");
@@ -2034,51 +2082,67 @@
 	for(obj = mon->minvent; obj; obj = obj->nobj) {
 	    /* Monsters can also use potions of acid */
 	    if ((obj->otyp == POT_ACID) || (obj->otyp == CORPSE &&
-	    		(obj->corpsenm == PM_LIZARD || acidic(&mons[obj->corpsenm])))) {
-		int nutrit = dog_nutrition(mon, obj); /* also sets meating */
-
-		if (canseemon(mon)) {
-		    long save_quan = obj->quan;
-
-		    obj->quan = 1L;
-		    pline("%s %ss %s.", Monnam(mon),
-		    		(obj->otyp == POT_ACID) ? "quaff" : "eat",
-		    		distant_name(obj,doname));
-		    obj->quan = save_quan;
-		} else if (flags.soundok)
-		    You_hear("%s.", (obj->otyp == POT_ACID) ? "drinking" : "chewing");
-		m_useup(mon, obj);
-		if (((obj->otyp == POT_ACID) || acidic(&mons[obj->corpsenm])) &&
-				!resists_acid(mon)) {
-		    mon->mhp -= rnd(15);
-		    pline("%s has a very bad case of stomach acid.",
-			Monnam(mon));
-		}
-		if (mon->mhp <= 0) {
-		    pline("%s dies!", Monnam(mon));
-		    if (by_you) xkilled(mon, 0);
-		    else mondead(mon);
-		    return TRUE;
-		}
-		if (canseemon(mon)) {
-		    if (Hallucination)
-		pline("What a pity - %s just ruined a future piece of art!",
-			mon_nam(mon));
-		    else
-			pline("%s seems limber!", Monnam(mon));
-		}
-		if (mon->mtame && !mon->isminion) {
-		    struct edog *edog = EDOG(mon);
-
-		    if (edog->hungrytime < moves) edog->hungrytime = moves;
-		    edog->hungrytime += nutrit;
-		    mon->mconf = 0;
-		}
-		mon->mlstmv = monstermoves; /* it takes a turn */
+	    		(obj->corpsenm == PM_LIZARD || (acidic(&mons[obj->corpsenm]) && obj->corpsenm != PM_GREEN_SLIME)))) {
+		mon_consume_unstone(mon, obj, by_you, TRUE);
 		return TRUE;
 	    }
 	}
 	return FALSE;
 }
 
+STATIC_OVL void
+mon_consume_unstone(mon, obj, by_you, stoning)
+struct monst *mon;
+struct obj *obj;
+boolean by_you;
+boolean stoning;
+{
+    int nutrit = (obj->otyp == CORPSE) ? dog_nutrition(mon, obj) : 0;
+    /* also sets meating */
+
+    if (canseemon(mon)) {
+	long save_quan = obj->quan;
+
+	obj->quan = 1L;
+	pline("%s %ss %s.", Monnam(mon),
+		    (obj->otyp == POT_ACID) ? "quaff" : "eat",
+		    distant_name(obj,doname));
+	obj->quan = save_quan;
+    } else if (flags.soundok)
+	You_hear("%s.", (obj->otyp == POT_ACID) ? "drinking" : "chewing");
+    m_useup(mon, obj);
+    if (((obj->otyp == POT_ACID) || acidic(&mons[obj->corpsenm])) &&
+		    !resists_acid(mon)) {
+	mon->mhp -= rnd(15);
+	pline("%s has a very bad case of stomach acid.",
+	    Monnam(mon));
+    }
+    if (mon->mhp <= 0) {
+	pline("%s dies!", Monnam(mon));
+	if (by_you) xkilled(mon, 0);
+	else mondead(mon);
+	return;
+    }
+    if (stoning && canseemon(mon)) {
+	if (Hallucination)
+    pline("What a pity - %s just ruined a future piece of art!",
+	    mon_nam(mon));
+	else
+	    pline("%s seems limber!", Monnam(mon));
+    }
+    if (obj->otyp == CORPSE && obj->corpsenm == PM_LIZARD && mon->mconf) {
+	mon->mconf = 0;
+	if (canseemon(mon))
+	    pline("%s seems steadier now.", Monnam(mon));
+    }
+    if (mon->mtame && !mon->isminion && nutrit > 0) {
+	struct edog *edog = EDOG(mon);
+
+	if (edog->hungrytime < monstermoves) edog->hungrytime = monstermoves;
+	edog->hungrytime += nutrit;
+	mon->mconf = 0;
+    }
+    mon->mlstmv = monstermoves; /* it takes a turn */
+}
+
 /*muse.c*/
diff -Naurd ../nethack-3.3.1/src/music.c ./src/music.c
--- ../nethack-3.3.1/src/music.c Wed Aug 9 16:46:19 2000
+++ ./src/music.c Fri Mar 22 14:40:55 2002
@@ -73,8 +73,8 @@
 		    mtmp->mfrozen = 0;
 		    /* May scare some monsters */
 		    if (distm < distance/3 &&
-			    !resist(mtmp, SCROLL_CLASS, 0, NOTELL))
-			mtmp->mflee = 1;
+			    !resist(mtmp, TOOL_CLASS, 0, NOTELL))
+			monflee(mtmp, 0, FALSE, TRUE);
 		}
 	    }
 	    mtmp = mtmp->nmon;
@@ -93,7 +93,7 @@
 
 	while(mtmp) {
 		if (!DEADMONSTER(mtmp) && distu(mtmp->mx, mtmp->my) < distance &&
-			sleep_monst(mtmp, d(10,10), WAND_CLASS)) {
+			sleep_monst(mtmp, d(10,10), TOOL_CLASS)) {
 		    mtmp->msleeping = 1; /* 10d10 turns + wake_nearby to rouse */
 		    slept_monst(mtmp);
 		}
@@ -117,6 +117,7 @@
 		    distu(mtmp->mx, mtmp->my) < distance) {
 		was_peaceful = mtmp->mpeaceful;
 		mtmp->mpeaceful = 1;
+		mtmp->mavenge = 0;
 		could_see_mon = canseemon(mtmp);
 		mtmp->mundetected = 0;
 		newsym(mtmp->mx, mtmp->my);
@@ -149,6 +150,7 @@
 		    distu(mtmp->mx, mtmp->my) < distance) {
 		mtmp->msleeping = 0;
 		mtmp->mpeaceful = 1;
+		mtmp->mavenge = 0;
 		if (canseemon(mtmp))
 		    pline(
 		     "%s listens cheerfully to the music, then seems quieter.",
@@ -179,22 +181,29 @@
 	}
 }
 
-/* Charm monsters in range.  Note that they may resist the spell. */
+/* Charm monsters in range.  Note that they may resist the spell.
+ * If swallowed, range is reduced to 0.
+ */
 
 STATIC_OVL void
 charm_monsters(distance)
 int distance;
 {
-	register struct monst *mtmp = fmon, *mtmp2;
+	struct monst *mtmp, *mtmp2;
 
-	while(mtmp) {
+	if (u.uswallow) {
+	    if (!resist(u.ustuck, TOOL_CLASS, 0, NOTELL))
+		(void) tamedog(u.ustuck, (struct obj *) 0);
+	} else {
+	    for (mtmp = fmon; mtmp; mtmp = mtmp2) {
 		mtmp2 = mtmp->nmon;
-		if (!DEADMONSTER(mtmp)) {
-			if (distu(mtmp->mx, mtmp->my) <= distance)
-			    if(!resist(mtmp, SCROLL_CLASS, 0, NOTELL))
-				(void) tamedog(mtmp, (struct obj *) 0);
+		if (DEADMONSTER(mtmp)) continue;
+
+		if (distu(mtmp->mx, mtmp->my) <= distance) {
+		    if (!resist(mtmp, TOOL_CLASS, 0, NOTELL))
+			(void) tamedog(mtmp, (struct obj *) 0);
 		}
-		mtmp = mtmp2;
+	    }
 	}
 
 }
@@ -329,6 +338,7 @@
 		    if (*in_rooms(x, y, SHOPBASE))
 			add_damage(x, y, 0L);
 		    levl[x][y].doormask = D_NODOOR;
+		    unblock_point(x,y);
 		    newsym(x,y);
 		    break;
 	    }
@@ -383,8 +393,7 @@
 	    } /* else FALLTHRU */
 	case WOODEN_FLUTE:		/* May charm snakes */
 	    do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25);
-	    pline("%s %s.", The(xname(instr)),
-		  do_spec ? "trills" : "toots");
+	    pline("%s.", Tobjnam(instr, do_spec ? "trill" : "toot"));
 	    if (do_spec) charm_snakes(u.ulevel * 3);
 	    exercise(A_DEX, TRUE);
 	    break;
@@ -394,14 +403,14 @@
 		check_unpaid(instr);
 		instr->spe--;
 		if (!getdir((char *)0)) {
-		    pline("%s vibrates.", The(xname(instr)));
+		    pline("%s.", Tobjnam(instr, "vibrate"));
 		    break;
 		} else if (!u.dx && !u.dy && !u.dz) {
-		    if ((damage = zapyourself(instr, TRUE)) != 0)
-			losehp(damage,
-			       self_pronoun("using a magical horn on %sself",
-					    "him"),
-			       NO_KILLER_PREFIX);
+		    if ((damage = zapyourself(instr, TRUE)) != 0) {
+			char buf[BUFSZ];
+			Sprintf(buf, "using a magical horn on %sself", uhim());
+			losehp(damage, buf, NO_KILLER_PREFIX);
+		    }
 		} else {
 		    buzz((instr->otyp == FROST_HORN) ? AD_COLD-1 : AD_FIRE-1,
 			 rn1(6,6), u.ux, u.uy, u.dx, u.dy);
@@ -423,8 +432,7 @@
 	    if (do_spec && instr->spe > 0) {
 		check_unpaid(instr);
 		instr->spe--;
-		pline("%s produces very attractive music.",
-		      The(xname(instr)));
+		pline("%s very attractive music.", Tobjnam(instr, "produce"));
 		charm_monsters((u.ulevel - 1) / 3 + 1);
 		exercise(A_DEX, TRUE);
 		break;
diff -Naurd ../nethack-3.3.1/src/o_init.c ./src/o_init.c
--- ../nethack-3.3.1/src/o_init.c Fri Dec 10 14:16:39 1999
+++ ./src/o_init.c Fri Mar 22 14:40:55 2002
@@ -75,9 +75,7 @@
 {
 	int i, j, num_to_shuffle;
 	short sw;
-#ifdef TEXTCOLOR
 	int color;
-#endif /* TEXTCOLOR */
 
 	for (num_to_shuffle = 0, j=o_low; j <= o_high; j++)
 		if (!objects[j].oc_name_known) num_to_shuffle++;
@@ -94,11 +92,10 @@
 		sw = objects[j].oc_tough;
 		objects[j].oc_tough = objects[i].oc_tough;
 		objects[i].oc_tough = sw;
-#ifdef TEXTCOLOR
 		color = objects[j].oc_color;
 		objects[j].oc_color = objects[i].oc_color;
 		objects[i].oc_color = color;
-#endif /* TEXTCOLOR */
+
 		/* shuffle material */
 		if (domaterial) {
 			sw = objects[j].oc_material;
diff -Naurd ../nethack-3.3.1/src/objects.c ./src/objects.c
--- ../nethack-3.3.1/src/objects.c Mon Feb 21 23:36:26 2000
+++ ./src/objects.c Fri Mar 22 14:40:55 2002
@@ -13,12 +13,8 @@
 
 #else	/* !OBJECTS_PASS_2_ */
 /* second pass */
-# ifdef TEXTCOLOR
 #include "color.h"
 #  define COLOR_FIELD(X) X,
-# else
-#  define COLOR_FIELD(X) /*empty*/
-# endif
 #endif	/* !OBJECTS_PASS_2_ */
 
 
@@ -135,7 +131,7 @@
 WEAPON("athame", (char *)0,
 	1, 1, 0,  0, 10,  4,  4,  3, 2, S,   P_DAGGER, IRON, HI_METAL),
 WEAPON("scalpel", (char *)0,
-	1, 1, 0,  0,  5,  4,  3,  3, 2, S,   P_KNIFE, IRON, HI_METAL),
+	1, 1, 0,  0,  5,  6,  3,  3, 2, S,   P_KNIFE, METAL, HI_METAL),
 WEAPON("knife", (char *)0,
 	1, 1, 0, 20,  5,  4,  3,  2, 0, P|S, P_KNIFE, IRON, HI_METAL),
 WEAPON("stiletto", (char *)0,
@@ -263,7 +259,7 @@
 BOW("elven bow", "runed bow",	0, 12, 30, 60, 0, WOOD, P_BOW, HI_WOOD),
 BOW("orcish bow", "crude bow",	0, 12, 30, 60, 0, WOOD, P_BOW, CLR_BLACK),
 BOW("yumi", "long bow",		0,  0, 30, 60, 0, WOOD, P_BOW, HI_WOOD),
-BOW("sling", (char *)0,		1, 40,  3, 20, 0, WOOD, P_SLING, HI_WOOD),
+BOW("sling", (char *)0,		1, 40,  3, 20, 0, LEATHER, P_SLING, HI_LEATHER),
 BOW("crossbow", (char *)0,	1, 45, 50, 40, 0, WOOD, P_CROSSBOW, HI_WOOD),
 
 #undef P
@@ -410,32 +406,34 @@
 CLOAK("mummy wrapping", (char *)0,
 		1, 0,	0,	    0, 0,  3,  2, 10, 1, CLOTH, CLR_GRAY),
 CLOAK("elven cloak", "faded pall",
-		0, 1,	STEALTH,   10, 0, 10, 60,  9, 3, CLOTH, CLR_BLACK),
+		0, 1,	STEALTH,    8, 0, 10, 60,  9, 3, CLOTH, CLR_BLACK),
 CLOAK("orcish cloak", "coarse mantelet",
-		0, 0,	0,	   10, 0, 10, 40, 10, 2, CLOTH, CLR_BLACK),
+		0, 0,	0,	    8, 0, 10, 40, 10, 2, CLOTH, CLR_BLACK),
 CLOAK("dwarvish cloak", "hooded cloak",
-		0, 0,	0,	   10, 0, 10, 50, 10, 2, CLOTH, HI_CLOTH),
+		0, 0,	0,	    8, 0, 10, 50, 10, 2, CLOTH, HI_CLOTH),
 CLOAK("oilskin cloak", "slippery cloak",
-		0, 0,	0,	   10, 0, 10, 50,  9, 3, CLOTH, HI_CLOTH),
+		0, 0,	0,	    8, 0, 10, 50,  9, 3, CLOTH, HI_CLOTH),
 CLOAK("robe", (char *)0,
-		1, 1,	0,	    0, 0, 15, 50,  8, 3, CLOTH, CLR_RED),
+		1, 1,	0,	    3, 0, 15, 50,  8, 3, CLOTH, CLR_RED),
 CLOAK("alchemy smock", "apron",
 		0, 1,	POISON_RES, 9, 0, 10, 50,  9, 1, CLOTH, CLR_WHITE),
+CLOAK("leather cloak", (char *)0,
+		1, 0,	0,	    8, 0, 15, 40,  9, 1, LEATHER, CLR_BROWN),
 /* With shuffled appearances... */
 CLOAK("cloak of protection", "tattered cape",
-		0, 1,	PROTECTION,10, 0, 10, 50,  7, 3, CLOTH, HI_CLOTH),
+		0, 1,	PROTECTION, 9, 0, 10, 50,  7, 3, CLOTH, HI_CLOTH),
 CLOAK("cloak of invisibility", "opera cloak",
-		0, 1,	INVIS,	   11, 0, 10, 60,  9, 2, CLOTH, CLR_BRIGHT_MAGENTA),
+		0, 1,	INVIS,	   10, 0, 10, 60,  9, 2, CLOTH, CLR_BRIGHT_MAGENTA),
 CLOAK("cloak of magic resistance", "ornamental cope",
 		0, 1,	ANTIMAGIC,  2, 0, 10, 60,  9, 3, CLOTH, CLR_WHITE),
 CLOAK("cloak of displacement", "piece of cloth",
-		0, 1,	DISPLACED, 11, 0, 10, 50,  9, 2, CLOTH, HI_CLOTH),
+		0, 1,	DISPLACED, 10, 0, 10, 50,  9, 2, CLOTH, HI_CLOTH),
 
 /* shields */
 SHIELD("small shield", (char *)0,
 		1, 0, 0, 0,	     6, 0, 30,	3,  9, 0, WOOD, HI_WOOD),
 SHIELD("elven shield", "blue and green shield",
-		0, 0, 0, 0,	     2, 0, 50,	7,  8, 0, IRON, CLR_GREEN),
+		0, 0, 0, 0,	     2, 0, 40,	7,  8, 0, WOOD, CLR_GREEN),
 SHIELD("Uruk-hai shield", "white-handed shield",
 		0, 0, 0, 0,	     2, 0, 50,	7,  9, 0, IRON, HI_METAL),
 SHIELD("orcish shield", "red-eyed shield",
@@ -555,7 +553,7 @@
 	AMULET_CLASS, 0, 0, 20,    0, 0, 0, 0, 0,  1, HI_METAL),
 OBJECT(OBJ("Amulet of Yendor",	/* note: description == name */
 	"Amulet of Yendor"), BITS(0,0,1,0,1,0,1,1,0,0,0,0,MITHRIL), 0,
-	AMULET_CLASS, 0, 0, 20, 3500, 0, 0, 0, 0, 20, HI_METAL),
+	AMULET_CLASS, 0, 0, 20, 30000, 0, 0, 0, 0, 20, HI_METAL),
 #undef AMULET
 
 /* tools ... */
@@ -655,10 +653,10 @@
 /* two special unique artifact "tools" */
 OBJECT(OBJ("Candelabrum of Invocation", "candelabrum"),
 		BITS(0,0,1,0,1,0,1,1,0,0,0,P_NONE,GOLD), 0,
-		TOOL_CLASS, 0, 0,10, 3000, 0, 0, 0, 0, 200, HI_GOLD),
+		TOOL_CLASS, 0, 0,10, 5000, 0, 0, 0, 0, 200, HI_GOLD),
 OBJECT(OBJ("Bell of Opening", "silver bell"),
 		BITS(0,0,1,0,1,1,1,1,0,0,0,P_NONE,SILVER), 0,
-		TOOL_CLASS, 0, 0,10, 1000, 0, 0, 0, 0, 50, HI_SILVER),
+		TOOL_CLASS, 0, 0,10, 5000, 0, 0, 0, 0, 50, HI_SILVER),
 #undef TOOL
 #undef WEPTOOL
 
@@ -837,7 +835,7 @@
 SPELL("blank paper",     "plain",       P_NONE, 18,  0, 0, 0, 0,         HI_PAPER),
 /* a special, one of a kind, spellbook */
 OBJECT(OBJ("Book of the Dead", "papyrus"), BITS(0,0,1,0,1,0,1,1,0,0,0,P_NONE,PAPER), 0,
-	SPBOOK_CLASS, 0, 0,20, 3500, 0, 0, 0, 7, 20, HI_PAPER),
+	SPBOOK_CLASS, 0, 0,20, 10000, 0, 0, 0, 7, 20, HI_PAPER),
 #undef SPELL
 
 /* wands ... */
@@ -875,10 +873,10 @@
 #undef WAND
 
 /* coins ... - so far, gold is all there is */
-#define COIN(name,prob,metal) OBJECT( \
+#define COIN(name,prob,metal,worth) OBJECT( \
 		OBJ(name,(char *)0), BITS(0,1,0,0,0,0,0,0,0,0,0,P_NONE,metal), 0, \
-		GOLD_CLASS, prob, 0, 1, 0, 0, 0, 0, 0, 0, HI_GOLD )
-	COIN("gold piece",      1000, GOLD),
+		GOLD_CLASS, prob, 0, 1, worth, 0, 0, 0, 0, 0, HI_GOLD )
+	COIN("gold piece",      1000, GOLD,1),
 #undef COIN
 
 /* gems ... - includes stones and rocks but not boulders */
@@ -925,7 +923,8 @@
 
 ROCK("luckstone", "gray",	0, 10,  10, 60, 3, 3, 1, 10, 7, MINERAL, CLR_GRAY),
 ROCK("loadstone", "gray",	0, 10, 500,  1, 3, 3, 1, 10, 6, MINERAL, CLR_GRAY),
-ROCK("flint", "gray",		0, 18,  10,  1, 6, 6, 0, 10, 7, MINERAL, CLR_GRAY),
+ROCK("touchstone", "gray",	0,  8,  10, 45, 3, 3, 1, 10, 6, MINERAL, CLR_GRAY),
+ROCK("flint", "gray",		0, 10,  10,  1, 6, 6, 0, 10, 7, MINERAL, CLR_GRAY),
 ROCK("rock", (char *)0,		1,100,  10,  0, 3, 3, 0, 10, 7, MINERAL, CLR_GRAY),
 #undef GEM
 #undef ROCK
diff -Naurd ../nethack-3.3.1/src/objnam.c ./src/objnam.c
--- ../nethack-3.3.1/src/objnam.c Thu Aug 3 20:25:24 2000
+++ ./src/objnam.c Fri Mar 22 14:41:04 2002
@@ -7,14 +7,14 @@
 /* "an uncursed greased partly eaten guardian naga hatchling [corpse]" */
 #define PREFIX	80	/* (56) */
 #define SCHAR_LIM 127
+#define NUMOBUF 12
 
 STATIC_DCL char *FDECL(strprepend,(char *,const char *));
-#ifdef OVL0
-static boolean FDECL(the_unique_obj, (struct obj *obj));
-#endif
 #ifdef OVLB
 static boolean FDECL(wishymatch, (const char *,const char *,BOOLEAN_P));
 #endif
+static char *NDECL(nextobuf);
+static void FDECL(add_erosion_words, (struct obj *, char *));
 
 struct Jitem {
 	int item;
@@ -47,7 +47,6 @@
 	{ HELMET, "kabuto" },
 	{ LEATHER_GLOVES, "yugake" },
 	{ FOOD_RATION, "gunyoki" },
-	{ KELP_FROND, "nori" },
 	{ POT_BOOZE, "sake" },
 	{0, "" }
 };
@@ -77,22 +76,28 @@
 #endif /* OVL1 */
 #ifdef OVLB
 
+/* manage a pool of BUFSZ buffers, so callers don't have to */
+static char *
+nextobuf()
+{
+	static char NEARDATA bufs[NUMOBUF][BUFSZ];
+	static int bufidx = 0;
+
+	bufidx = (bufidx + 1) % NUMOBUF;
+	return bufs[bufidx];
+}
+
 char *
 obj_typename(otyp)
 register int otyp;
 {
-#ifdef LINT	/* static char buf[BUFSZ]; */
-	char buf[BUFSZ];
-#else
-	static char NEARDATA buf[BUFSZ];
-#endif
+	char *buf = nextobuf();
 	register struct objclass *ocl = &objects[otyp];
 	register const char *actualn = OBJ_NAME(*ocl);
 	register const char *dn = OBJ_DESCR(*ocl);
 	register const char *un = ocl->oc_uname;
 	register int nn = ocl->oc_name_known;
 
-
 	if (Role_if(PM_SAMURAI) && Japanese_item_name(otyp))
 		actualn = Japanese_item_name(otyp);
 	switch(ocl->oc_class) {
@@ -144,8 +149,12 @@
 		return(buf);
 	}
 	/* here for ring/scroll/potion/wand */
-	if(nn)
+	if(nn) {
+	    if (ocl->oc_unique)
+		Strcpy(buf, actualn); /* avoid spellbook of Book of the Dead */
+	    else
 		Sprintf(eos(buf), " of %s", actualn);
+	}
 	if(un)
 		Sprintf(eos(buf), " called %s", un);
 	if(dn)
@@ -207,12 +216,7 @@
 xname(obj)
 register struct obj *obj;
 {
-#ifdef LINT	/* lint may handle static decl poorly -- static char bufr[]; */
-	char bufr[BUFSZ];
-#else
-	static char bufr[BUFSZ];
-#endif
-	register char *buf = &(bufr[PREFIX]);	/* leave room for "17 -3 " */
+	register char *buf;
 	register int typ = obj->otyp;
 	register struct objclass *ocl = &objects[typ];
 	register int nn = ocl->oc_name_known;
@@ -220,10 +224,19 @@
 	register const char *dn = OBJ_DESCR(*ocl);
 	register const char *un = ocl->oc_uname;
 
+	buf = nextobuf() + PREFIX;	/* leave room for "17 -3 " */
 	if (Role_if(PM_SAMURAI) && Japanese_item_name(typ))
 		actualn = Japanese_item_name(typ);
 
 	buf[0] = '\0';
+	/*
+	 * clean up known when it's tied to oc_name_known, eg after AD_DRIN
+	 * This is only required for unique objects and the Fake AoY since the
+	 * article printed for the object is tied to the combination of the two
+	 * and printing the wrong article gives away information.
+	 */
+	if (!nn && ocl->oc_uses_known &&
+	    (ocl->oc_unique || typ == FAKE_AMULET_OF_YENDOR)) obj->known = 0;
 	if (!Blind) obj->dknown = TRUE;
 	if (Role_if(PM_PRIEST)) obj->bknown = TRUE;
 	if (obj_is_pname(obj))
@@ -451,11 +464,30 @@
 	return(buf);
 }
 
+/* xname() output augmented for multishot missile feedback */
+char *
+mshot_xname(obj)
+struct obj *obj;
+{
+    char tmpbuf[BUFSZ];
+    char *onm = xname(obj);
+
+    if (m_shot.n > 1 && m_shot.o == obj->otyp) {
+	/* copy xname's result so that we can reuse its return buffer */
+	Strcpy(tmpbuf, onm);
+	/* "the Nth arrow"; value will eventually be passed to an() or
+	   The(), both of which correctly handle this "the " prefix */
+	Sprintf(onm, "the %d%s %s", m_shot.i, ordin(m_shot.i), tmpbuf);
+    }
+
+    return onm;
+}
+
 #endif /* OVL1 */
 #ifdef OVL0
 
 /* used for naming "the unique_item" instead of "a unique_item" */
-static boolean
+boolean
 the_unique_obj(obj)
 register struct obj *obj;
 {
@@ -714,8 +746,19 @@
 			Strcat(bp, " (alternate weapon; not wielded)");
 	}
 	if(obj->owornmask & W_QUIVER) Strcat(bp, " (in quiver)");
-	if(obj->unpaid)
-		Strcat(bp, " (unpaid)");
+	if(obj->unpaid) {
+		xchar ox, oy; 
+		long quotedprice = unpaid_cost(obj);
+		struct monst *shkp = (struct monst *)0;
+
+		if (Has_contents(obj) &&
+		    get_obj_location(obj, &ox, &oy, BURIED_TOO|CONTAINED_TOO) &&
+		    costly_spot(ox, oy) &&
+		    (shkp = shop_keeper(*in_rooms(ox, oy, SHOPBASE))))
+			quotedprice += contained_cost(obj, shkp, 0L, FALSE, TRUE);
+		Sprintf(eos(bp), " (unpaid, %ld %s)",
+			quotedprice, currency(quotedprice));
+	}
 	if (!strncmp(prefix, "a ", 2) &&
 			index(vowels, *(prefix+2) ? *(prefix+2) : *bp)
 			&& (*(prefix+2) || (strncmp(bp, "uranium", 7)
@@ -765,17 +808,13 @@
 			 is_flammable(otmp));
 }
 
-/* The result is actually modifiable, but caller shouldn't rely on that
- * due to the small buffer size.
- */
-const char *
+char *
 corpse_xname(otmp, ignore_oquan)
 struct obj *otmp;
 boolean ignore_oquan;	/* to force singular */
 {
-	static char NEARDATA nambuf[40];
+	char *nambuf = nextobuf();
 
-     /* assert( strlen(mons[otmp->corpsenm].mname) <= 32 ); */
 	Sprintf(nambuf, "%s corpse", mons[otmp->corpsenm].mname);
 
 	if (ignore_oquan || otmp->quan < 2)
@@ -784,6 +823,16 @@
 	    return makeplural(nambuf);
 }
 
+/* xname, unless it's a corpse, then corpse_xname(obj, FALSE) */
+char *
+cxname(obj)
+struct obj *obj;
+{
+	if (obj->otyp == CORPSE)
+	    return corpse_xname(obj, FALSE);
+	return xname(obj);
+}
+
 /*
  * Used if only one of a collection of objects is named (e.g. in eat.c).
  */
@@ -810,7 +859,7 @@
 an(str)
 register const char *str;
 {
-	static char NEARDATA buf[BUFSZ];
+	char *buf = nextobuf();
 
 	buf[0] = '\0';
 
@@ -849,7 +898,7 @@
 the(str)
 const char *str;
 {
-	static char NEARDATA buf[BUFSZ];
+	char *buf = nextobuf();
 	boolean insert_the = FALSE;
 
 	if (!strncmpi(str, "the ", 4)) {
@@ -901,12 +950,13 @@
     return tmp;
 }
 
+/* returns "count cxname(otmp)" or just cxname(otmp) if count == 1 */
 char *
 aobjnam(otmp,verb)
 register struct obj *otmp;
 register const char *verb;
 {
-	register char *bp = xname(otmp);
+	register char *bp = cxname(otmp);
 	char prefix[PREFIX];
 
 	if(otmp->quan != 1L) {
@@ -915,20 +965,122 @@
 	}
 
 	if(verb) {
-		/* verb is given in plural (without trailing s) */
-		Strcat(bp, " ");
-		if(otmp->quan != 1L)
-			Strcat(bp, verb);
-		else if(!strcmp(verb, "are"))
-			Strcat(bp, "is");
-		else {
-			Strcat(bp, verb);
-			Strcat(bp, "s");
-		}
+	    Strcat(bp, " ");
+	    Strcat(bp, otense(otmp, verb));
+	}
+	return(bp);
+}
+
+/* like aobjnam, but prepend "The", not count, and use xname */
+char *
+Tobjnam(otmp, verb)
+register struct obj *otmp;
+register const char *verb;
+{
+	char *bp = The(xname(otmp));
+
+	if(verb) {
+	    Strcat(bp, " ");
+	    Strcat(bp, otense(otmp, verb));
 	}
 	return(bp);
 }
 
+/* return form of the verb (input plural) if xname(otmp) were the subject */
+char *
+otense(otmp, verb)
+register struct obj *otmp;
+register const char *verb;
+{
+	char *buf;
+
+	/*
+	 * verb is given in plural (without trailing s).  Return as input
+	 * if the result of xname(otmp) would be plural.  Don't bother
+	 * recomputing xname(otmp) at this time.
+	 */
+	if (!is_plural(otmp))
+	    return vtense((char *)0, verb);
+
+	buf = nextobuf();
+	Strcpy(buf, verb);
+	return buf;
+}
+
+/* return form of the verb (input plural) for present tense 3rd person subj */
+char *
+vtense(subj, verb)
+register const char *subj;
+register const char *verb;
+{
+	char *buf = nextobuf();
+	int len;
+	const char *spot;
+	const char *sp;
+
+	/*
+	 * verb is given in plural (without trailing s).  Return as input
+	 * if subj appears to be plural.  Add special cases as necessary.
+	 * Many hard cases can already be handled by using otense() instead.
+	 * If this gets much bigger, consider decomposing makeplural.
+	 * Note: monster names are not expected here (except before corpse).
+	 *
+	 * special case: allow null sobj to get the singular 3rd person
+	 * present tense form so we don't duplicate this code elsewhere.
+	 */
+	if (subj) {
+	    spot = (const char *)0;
+	    for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) {
+		if (!strncmp(sp, " of ", 4) ||
+		    !strncmp(sp, " called ", 8) ||
+		    !strncmp(sp, " named ", 7) ||
+		    !strncmp(sp, " labeled ", 9)) {
+		    if (sp != subj) spot = sp - 1;
+		    break;
+		}
+	    }
+	    len = strlen(subj);
+	    if (!spot) spot = subj + len - 1;
+
+	    /*
+	     * plural: anything that ends in 's', but not '*us'.
+	     * Guess at a few other special cases that makeplural creates.
+	     */
+	    if ((*spot == 's' && spot != subj && *(spot-1) != 'u') ||
+		((spot - subj) >= 4 && !strncmp(spot-3, "eeth", 4)) ||
+		((spot - subj) >= 3 && !strncmp(spot-3, "feet", 4)) ||
+		((spot - subj) >= 2 && !strncmp(spot-1, "ia", 2)) ||
+		((spot - subj) >= 2 && !strncmp(spot-1, "ae", 2))) {
+		Strcpy(buf, verb);
+		return buf;
+	    }
+	}
+
+	len = strlen(verb);
+	spot = verb + len - 1;
+
+	if (!strcmp(verb, "are"))
+	    Strcpy(buf, "is");
+	else if (!strcmp(verb, "have"))
+	    Strcpy(buf, "has");
+	else if (index("zxs", *spot) ||
+		 (len >= 2 && *spot=='h' && index("cs", *(spot-1))) ||
+		 (len == 2 && *spot == 'o')) {
+	    /* Ends in z, x, s, ch, sh; add an "es" */
+	    Strcpy(buf, verb);
+	    Strcat(buf, "es");
+	} else if (*spot == 'y' && (!index(vowels, *(spot-1)))) {
+	    /* like "y" case in makeplural */
+	    Strcpy(buf, verb);
+	    Strcpy(buf + len - 1, "ies");
+	} else {
+	    Strcpy(buf, verb);
+	    Strcat(buf, "s");
+	}
+
+	return buf;
+}
+
 /* capitalized variant of doname() */
 char *
 Doname2(obj)
@@ -945,11 +1097,11 @@
 yname(obj)
 struct obj *obj;
 {
-	static char outbuf[BUFSZ];
+	char *outbuf = nextobuf();
 	char *s = shk_your(outbuf, obj);	/* assert( s == outbuf ); */
-	int space_left = sizeof outbuf - strlen(s) - sizeof " ";
+	int space_left = BUFSZ - strlen(s) - sizeof " ";
 
-	return strncat(strcat(s, " "), xname(obj), space_left);
+	return strncat(strcat(s, " "), cxname(obj), space_left);
 }
 
 /* capitalized variant of yname() */
@@ -995,7 +1147,7 @@
 {
 	/* Note: cannot use strcmpi here -- it'd give MATZot, CAVEMeN,... */
 	register char *spot;
-	static char NEARDATA str[BUFSZ];
+	char *str = nextobuf();
 	const char *excess = (char *)0;
 	int len;
 
@@ -1008,14 +1160,11 @@
 	Strcpy(str, oldstr);
 
 	/*
-	Skip changing "pair of" to "pairs of".  According to Webster, usual
-	English usage is use pairs for humans, e.g. 3 pairs of dancers,
-	and pair for objects and non-humans, e.g. 3 pair of boots.  We don't
-	refer to pairs of humans in this game so just skip to the bottom.
-
-	Actually, none of the "pair" objects -- gloves, boots, and lenses --
-	currently merge, so this isn't used.
-	*/
+	 * Skip changing "pair of" to "pairs of".  According to Webster, usual
+	 * English usage is use pairs for humans, e.g. 3 pairs of dancers,
+	 * and pair for objects and non-humans, e.g. 3 pair of boots.  We don't
+	 * refer to pairs of humans in this game so just skip to the bottom.
+	 */
 	if (!strncmp(str, "pair of ", 8))
 		goto bottom;
 
@@ -1059,12 +1208,12 @@
 	    (len >= 3 && !strcmp(spot-2, " ya")) ||
 	    (len >= 4 &&
 	     (!strcmp(spot-3, "fish") || !strcmp(spot-3, "tuna") ||
-	      !strcmp(spot-3, "deer") || !strcmp(spot-3, "yaki") ||
-	      !strcmp(spot-3, "nori"))) ||
+	      !strcmp(spot-3, "deer") || !strcmp(spot-3, "yaki"))) ||
 	    (len >= 5 && (!strcmp(spot-4, "sheep") ||
 			!strcmp(spot-4, "ninja") ||
 			!strcmp(spot-4, "ronin") ||
 			!strcmp(spot-4, "shito") ||
+			!strcmp(spot-7, "shuriken") ||
 			!strcmp(spot-4, "tengu") ||
 			!strcmp(spot-4, "manes"))) ||
 	    (len >= 6 && !strcmp(spot-5, "ki-rin")) ||
@@ -1120,8 +1269,10 @@
 		goto bottom;
 	}
 
-	/* fungus/fungi, homunculus/homunculi, but wumpuses */
-	if (!strcmp(spot-1, "us") && (len < 6 || strcmp(spot-5, "wumpus"))) {
+	/* fungus/fungi, homunculus/homunculi, but buses, lotuses, wumpuses */
+	if (len > 3 && !strcmp(spot-1, "us") &&
+	    (len < 5 || (strcmp(spot-4, "lotus") &&
+			 (len < 6 || strcmp(spot-5, "wumpus"))))) {
 		*(spot--) = (char)0;
 		*spot = 'i';
 		goto bottom;
@@ -1258,13 +1409,12 @@
  * of readobjnam, and is also used in pager.c to singularize the string
  * for which help is sought.
  */
-
 char *
 makesingular(oldstr)
 const char *oldstr;
 {
 	register char *p, *bp;
-	static char NEARDATA str[BUFSZ];
+	char *str = nextobuf();
 
 	if (!oldstr || !*oldstr) {
 		impossible("singular of null?");
@@ -1442,13 +1592,19 @@
 	{ (const char *)0, 0 },
 };
 
-/* Return something wished for.  If not an object, return &zeroobj; if an error
- * (no matching object), return (struct obj *)0.  Giving readobjnam() a null
- * pointer skips the error return and creates a random object instead.
+/*
+ * Return something wished for.  Specifying a null pointer for
+ * the user request string results in a random object.  Otherwise,
+ * if asking explicitly for "nothing" (or "nil") return no_wish;
+ * if not an object return &zeroobj; if an error (no matching object),
+ * return null.
+ * If from_user is false, we're reading from the wizkit, nothing was typed in.
  */
 struct obj *
-readobjnam(bp)
+readobjnam(bp, no_wish, from_user)
 register char *bp;
+struct obj *no_wish;
+boolean from_user;
 {
 	register char *p;
 	register int i;
@@ -1501,6 +1657,10 @@
 	if (!bp) goto any;
 	/* first, remove extra whitespace they may have typed */
 	(void)mungspaces(bp);
+	/* allow wishing for "nothing" to preserve wishless conduct...
+	   [now requires "wand of nothing" if that's what was really wanted] */
+	if (!strcmpi(bp, "nothing") || !strcmpi(bp, "nil")) return no_wish;
+	/* save the [nearly] unmodified choice string */
 	Strcpy(fruitbuf, bp);
 
 	for(;;) {
@@ -1517,6 +1677,12 @@
 			while(digit(*bp)) bp++;
 			while(*bp == ' ') bp++;
 			l = 0;
+		} else if (*bp == '+' || *bp == '-') {
+			spesgn = (*bp++ == '+') ? 1 : -1;
+			spe = atoi(bp);
+			while(digit(*bp)) bp++;
+			while(*bp == ' ') bp++;
+			l = 0;
 		} else if (!strncmpi(bp, "blessed ", l=8) ||
 			   !strncmpi(bp, "holy ", l=5)) {
 			blessed = 1;
@@ -1576,21 +1742,14 @@
 			ishistoric = 1;
 		} else if (!strncmpi(bp, "diluted ", l=8)) {
 			isdiluted = 1;
+		} else if(!strncmpi(bp, "empty ", l=6)) {
+			contents = EMPTY;
 		} else break;
 		bp += l;
 	}
 	if(!cnt) cnt = 1;		/* %% what with "gems" etc. ? */
-	if(!strncmpi(bp, "empty ", 6)) {
-		contents = EMPTY;
-		bp += 6;
-	}
 	if (strlen(bp) > 1) {
-	    if (*bp == '+' || *bp == '-') {
-		spesgn = (*bp++ == '+') ? 1 : -1;
-		spe = atoi(bp);
-		while(digit(*bp)) bp++;
-		while(*bp == ' ') bp++;
-	    } else if ((p = rindex(bp, '(')) != 0) {
+	    if ((p = rindex(bp, '(')) != 0) {
 		if (p > bp && p[-1] == ' ') p[-1] = 0;
 		else *p = 0;
 		p++;
@@ -1712,7 +1871,7 @@
 		int mntmptoo, mntmplen;	/* double check for rank title */
 		char *obp = bp;
 		mntmptoo = title_to_mon(bp, (int *)0, &mntmplen);
-		bp += mntmp != mntmptoo ? strlen(mons[mntmp].mname) : mntmplen;
+		bp += mntmp != mntmptoo ? (int)strlen(mons[mntmp].mname) : mntmplen;
 		if (*bp == ' ') bp++;
 		else if (!strncmpi(bp, "s ", 2)) bp += 2;
 		else if (!strncmpi(bp, "es ", 3)) bp += 3;
@@ -1784,7 +1943,8 @@
 #endif
 						) cnt=5000;
 		if (cnt < 1) cnt=1;
-		pline("%d gold piece%s.", cnt, plur(cnt));
+		if (from_user)
+		    pline("%d gold piece%s.", cnt, plur(cnt));
 		u.ugold += cnt;
 		flags.botl=1;
 		return (&zeroobj);
@@ -1983,7 +2143,7 @@
 	/* must come after objects check so wizards can still wish for
 	 * trap objects like beartraps
 	 */
-	if (wizard) {
+	if (wizard && from_user) {
 		int trap;
 
 		for (trap = NO_TRAP+1; trap < TRAPNUM; trap++) {
@@ -2075,6 +2235,7 @@
 		    levl[u.ux][u.uy].typ = TREE;
 		    pline("A tree.");
 		    newsym(u.ux, u.uy);
+		    block_point(u.ux, u.uy);
 		    return &zeroobj;
 		}
 
@@ -2101,7 +2262,7 @@
 #endif
 	    switch (typ) {
 		case AMULET_OF_YENDOR:
-			typ = FAKE_AMULET_OF_YENDOR;
+		    typ = FAKE_AMULET_OF_YENDOR;
 		    break;
 		case CANDELABRUM_OF_INVOCATION:
 		    typ = rnd_class(TALLOW_CANDLE, WAX_CANDLE);
@@ -2215,7 +2376,10 @@
 	}
 
 	/* set otmp->corpsenm or dragon scale [mail] */
-	if (mntmp >= LOW_PM) switch(typ) {
+	if (mntmp >= LOW_PM) {
+		if (mntmp == PM_LONG_WORM_TAIL) mntmp = PM_LONG_WORM;
+
+		switch (typ) {
 		case TIN:
 			otmp->spe = 0; /* No spinach */
 			if (dead_species(mntmp, FALSE)) {
@@ -2268,6 +2432,7 @@
 			    otmp->otyp = GRAY_DRAGON_SCALE_MAIL +
 						    mntmp - PM_GRAY_DRAGON;
 			break;
+		}
 	}
 
 	/* set blessed/cursed -- setting the fields directly is safe
@@ -2371,23 +2536,21 @@
 	    artifact_exists(otmp, ONAME(otmp), FALSE);
 	    obfree(otmp, (struct obj *) 0);
 	    otmp = &zeroobj;
-	    pline(
-	     "For a moment, you feel %s in your %s, but it disappears!",
+	    pline("For a moment, you feel %s in your %s, but it disappears!",
 		  something,
 		  makeplural(body_part(HAND)));
 	}
 
-	otmp->owt = weight(otmp);
-	if (very && otmp->otyp == HEAVY_IRON_BALL) otmp->owt += 160;
 	if (halfeaten && otmp->oclass == FOOD_CLASS) {
 		if (otmp->otyp == CORPSE)
 			otmp->oeaten = mons[otmp->corpsenm].cnutrit;
 		else otmp->oeaten = objects[otmp->otyp].oc_nutrition;
-		otmp->owt /= 2;
-		otmp->oeaten /= 2;
-		if (!otmp->owt) otmp->owt = 1;
-		if (!otmp->oeaten) otmp->oeaten = 1;
+		/* (do this adjustment before setting up object's weight) */
+		consume_oeaten(otmp, 1);
 	}
+	otmp->owt = weight(otmp);
+	if (very && otmp->otyp == HEAVY_IRON_BALL) otmp->owt += 160;
+
 	return(otmp);
 }
 
@@ -2423,6 +2586,27 @@
 	}
 	return (const char *)0;
 }
+
+const char *
+cloak_simple_name(cloak)
+struct obj *cloak;
+{
+    if (cloak) {
+	switch (cloak->otyp) {
+	case ROBE:
+	    return "robe";
+	case MUMMY_WRAPPING:
+	    return "wrapping";
+	case ALCHEMY_SMOCK:
+	    return (objects[cloak->otyp].oc_name_known &&
+			cloak->dknown) ? "smock" : "apron";
+	default:
+	    break;
+	}
+    }
+    return "cloak";
+}
+
 #endif /* OVLB */
 
 /*objnam.c*/
diff -Naurd ../nethack-3.3.1/src/options.c ./src/options.c
--- ../nethack-3.3.1/src/options.c Wed Aug 9 19:33:01 2000
+++ ./src/options.c Fri Mar 22 14:41:05 2002
@@ -22,173 +22,172 @@
  *  options help (option_help()), the long options help (dat/opthelp),
  *  and the current options setting display function (doset()),
  *  and also the Guidebooks.
+ *
+ *  The order matters.  If an option is a an initial substring of another
+ *  option (e.g. time and timed_delay) the shorter one must come first.
  */
 
 static struct Bool_Opt
 {
 	const char *name;
 	boolean	*addr, initvalue;
+	int optflags;
 } boolopt[] = {
 #ifdef AMIGA
-	{"altmeta", &flags.altmeta, TRUE},
+	{"altmeta", &flags.altmeta, TRUE, DISP_IN_GAME},
 #else
-	{"altmeta", (boolean *)0, TRUE},
+	{"altmeta", (boolean *)0, TRUE, DISP_IN_GAME},
 #endif
+	{"ascii_map",     &iflags.wc_ascii_map, TRUE, SET_IN_GAME},	/*WC*/
 #ifdef MFLOPPY
-	{"asksavedisk", &flags.asksavedisk, FALSE},
+	{"asksavedisk", &flags.asksavedisk, FALSE, SET_IN_GAME},
 #else
-	{"asksavedisk", (boolean *)0, FALSE},
+	{"asksavedisk", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"autopickup", &flags.pickup, TRUE},
-	{"autoquiver", &flags.autoquiver, FALSE},
+	{"autodig", &flags.autodig, FALSE, SET_IN_GAME},
+	{"autopickup", &flags.pickup, TRUE, SET_IN_GAME},
+	{"autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME},
 #if defined(MICRO) && !defined(AMIGA)
-	{"BIOS", &iflags.BIOS, FALSE},
+	{"BIOS", &iflags.BIOS, FALSE, SET_IN_FILE},
 #else
-	{"BIOS", (boolean *)0, FALSE},
+	{"BIOS", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
 #ifdef INSURANCE
-	{"checkpoint", &flags.ins_chkpt, TRUE},
+	{"checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME},
 #else
-	{"checkpoint", (boolean *)0, FALSE},
+	{"checkpoint", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
 #ifdef MFLOPPY
-	{"checkspace", &iflags.checkspace, TRUE},
+	{"checkspace", &iflags.checkspace, TRUE, SET_IN_GAME},
 #else
-	{"checkspace", (boolean *)0, FALSE},
+	{"checkspace", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-#ifdef TEXTCOLOR
 # ifdef MICRO
-	{"color", &iflags.use_color, TRUE},
+	{"color",         &iflags.wc_color,TRUE, SET_IN_GAME},		/*WC*/
 # else	/* systems that support multiple terminals, many monochrome */
-	{"color", &iflags.use_color, FALSE},
+	{"color",         &iflags.wc_color, FALSE, SET_IN_GAME},	/*WC*/
 # endif
+	{"confirm",&flags.confirm, TRUE, SET_IN_GAME},
+#if defined(TERMLIB) && !defined(MAC_GRAPHICS_ENV)
+	{"DECgraphics", &iflags.DECgraphics, FALSE, SET_IN_GAME},
 #else
-	{"color", (boolean *)0, FALSE},
-#endif
-	{"confirm",&flags.confirm, TRUE},
-#ifdef TERMLIB
-	{"DECgraphics", &iflags.DECgraphics, FALSE},
-#else
-	{"DECgraphics", (boolean *)0, FALSE},
+	{"DECgraphics", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
+	{"eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME},	/*WC*/
 #ifdef TTY_GRAPHICS
-	{"eight_bit_tty", &iflags.eight_bit_tty, FALSE},
-	{"extmenu", &iflags.extmenu, FALSE},
+	{"extmenu", &iflags.extmenu, FALSE, SET_IN_GAME},
 #else
-	{"eight_bit_tty", (boolean *)0, FALSE},
-	{"extmenu", (boolean *)0, FALSE},
+	{"extmenu", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
 #ifdef OPT_DISPMAP
-	{"fast_map", &flags.fast_map, TRUE},
+	{"fast_map", &flags.fast_map, TRUE, SET_IN_GAME},
 #else
-	{"fast_map", (boolean *)0, TRUE},
+	{"fast_map", (boolean *)0, TRUE, SET_IN_FILE},
 #endif
-	{"female", &flags.female, FALSE},
-	{"fixinv", &flags.invlet_constant, TRUE},
+	{"female", &flags.female, FALSE, DISP_IN_GAME},
+	{"fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME},
 #ifdef AMIFLUSH
-	{"flush", &flags.amiflush, FALSE},
-#else
-	{"flush", (boolean *)0, FALSE},
-#endif
-	{"help", &flags.help, TRUE},
-#ifdef TEXTCOLOR
-	{"hilite_pet", &iflags.hilite_pet, FALSE},
+	{"flush", &flags.amiflush, FALSE, SET_IN_GAME},
 #else
-	{"hilite_pet", (boolean *)0, FALSE},
+	{"flush", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
+	{"help", &flags.help, TRUE, SET_IN_GAME},
+	{"hilite_pet",    &iflags.wc_hilite_pet, FALSE, SET_IN_GAME},	/*WC*/
 #ifdef ASCIIGRAPH
-	{"IBMgraphics", &iflags.IBMgraphics, FALSE},
+	{"IBMgraphics", &iflags.IBMgraphics, FALSE, SET_IN_GAME},
 #else
-	{"IBMgraphics", (boolean *)0, FALSE},
+	{"IBMgraphics", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"ignintr", &flags.ignintr, FALSE},
-#ifdef MAC_GRAPHICS_ENV
-	{"large_font", &iflags.large_font, FALSE},
+#ifndef MAC
+	{"ignintr", &flags.ignintr, FALSE, SET_IN_GAME},
 #else
-	{"large_font", (boolean *)0, FALSE},
+	{"ignintr", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"legacy", &flags.legacy, TRUE},
-	{"lit_corridor", &flags.lit_corridor, FALSE},
+	{"large_font", &iflags.wc_large_font, FALSE, SET_IN_FILE},	/*WC*/
+	{"legacy", &flags.legacy, TRUE, DISP_IN_GAME},
+	{"lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME},
 #ifdef MAC_GRAPHICS_ENV
-	{"Macgraphics", &iflags.MACgraphics, TRUE},
+	{"Macgraphics", &iflags.MACgraphics, TRUE, SET_IN_GAME},
 #else
-	{"Macgraphics", (boolean *)0, FALSE},
+	{"Macgraphics", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
 #ifdef MAIL
-	{"mail", &flags.biff, TRUE},
+	{"mail", &flags.biff, TRUE, SET_IN_GAME},
 #else
-	{"mail", (boolean *)0, TRUE},
+	{"mail", (boolean *)0, TRUE, SET_IN_FILE},
 #endif
-#ifdef NEWS
-	{"news", &iflags.news, TRUE},
+#ifdef WIZARD
+	/* for menu debugging only*/
+	{"menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_GAME},
 #else
-	{"news", (boolean *)0, FALSE},
+	{"menu_tab_sep", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"null", &flags.null, TRUE},
-	{"number_pad", &iflags.num_pad, FALSE},
-#ifdef MAC
-	{"page_wait", &flags.page_wait, TRUE},
+#ifdef TTY_GRAPHICS
+	{"msg_window", &iflags.prevmsg_window, FALSE, SET_IN_GAME},
 #else
-	{"page_wait", (boolean *)0, FALSE},
+	{"msg_window", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"perm_invent", &flags.perm_invent, FALSE},
-#ifdef MAC
-	{"popup_dialog", &iflags.popup_dialog, FALSE},
+#ifdef NEWS
+	{"news", &iflags.news, TRUE, DISP_IN_GAME},
 #else
-	{"popup_dialog", (boolean *)0, FALSE},
+	{"news", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"prayconfirm", &flags.prayconfirm, TRUE},
-#if defined(MSDOS) && defined(USE_TILES)
-	{"preload_tiles", &iflags.preload_tiles, TRUE},
+	{"null", &flags.null, TRUE, SET_IN_GAME},
+	{"number_pad", &iflags.num_pad, FALSE, SET_IN_GAME},
+#ifdef MAC
+	{"page_wait", &flags.page_wait, TRUE, SET_IN_GAME},
 #else
-	{"preload_tiles", (boolean *)0, FALSE},
+	{"page_wait", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"pushweapon", &flags.pushweapon, FALSE},
-#if defined(MICRO) && !defined(AMIGA)
-	{"rawio", &iflags.rawio, FALSE},
+	{"perm_invent", &flags.perm_invent, FALSE, SET_IN_GAME},
+	{"popup_dialog",  &iflags.wc_popup_dialog, FALSE, SET_IN_GAME},	/*WC*/
+	{"prayconfirm", &flags.prayconfirm, TRUE, SET_IN_GAME},
+	{"preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME},	/*WC*/
+	{"pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME},
+#if defined(MICRO) && !defined(AMIGA) && !defined(MSWIN_GRAPHICS)
+	{"rawio", &iflags.rawio, FALSE, DISP_IN_GAME},
 #else
-	{"rawio", (boolean *)0, FALSE},
+	{"rawio", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"rest_on_space", &flags.rest_on_space, FALSE},
-	{"safe_pet", &flags.safe_dog, TRUE},
+	{"rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME},
+	{"safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME},
 #ifdef WIZARD
-	{"sanity_check", &iflags.sanity_check, FALSE},
+	{"sanity_check", &iflags.sanity_check, FALSE, SET_IN_GAME},
 #else
-	{"sanity_check", (boolean *)0, FALSE},
+	{"sanity_check", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
 #ifdef EXP_ON_BOTL
-	{"showexp", &flags.showexp, FALSE},
+	{"showexp", &flags.showexp, FALSE, SET_IN_GAME},
 #else
-	{"showexp", (boolean *)0, FALSE},
+	{"showexp", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
 #ifdef SCORE_ON_BOTL
-	{"showscore", &flags.showscore, FALSE},
+	{"showscore", &flags.showscore, FALSE, SET_IN_GAME},
 #else
-	{"showscore", (boolean *)0, FALSE},
+	{"showscore", (boolean *)0, FALSE, SET_IN_FILE},
 #endif
-	{"silent", &flags.silent, TRUE},
-	{"sortpack", &flags.sortpack, TRUE},
-	{"sound", &flags.soundok, TRUE},
-	{"standout", &flags.standout, FALSE},
-	{"time", &flags.time, FALSE},
+	{"silent", &flags.silent, TRUE, SET_IN_GAME},
+	{"sortpack", &flags.sortpack, TRUE, SET_IN_GAME},
+	{"sound", &flags.soundok, TRUE, SET_IN_GAME},
+	{"sparkle", &flags.sparkle, TRUE, SET_IN_GAME},
+	{"standout", &flags.standout, FALSE, SET_IN_GAME},
+	{"splash_screen",     &iflags.wc_splash_screen, TRUE, DISP_IN_GAME},	/*WC*/
+	{"tiled_map",     &iflags.wc_tiled_map, FALSE, DISP_IN_GAME},	/*WC*/
+	{"time", &flags.time, FALSE, SET_IN_GAME},
 #ifdef TIMED_DELAY
-	{"timed_delay", &flags.nap, TRUE},
+	{"timed_delay", &flags.nap, TRUE, SET_IN_GAME},
 #else
-	{"timed_delay", (boolean *)0, FALSE},
+	{"timed_delay", (boolean *)0, FALSE, SET_IN_GAME},
 #endif
-	{"tombstone",&flags.tombstone, TRUE},
-	{"toptenwin",&flags.toptenwin, FALSE},
-	{"verbose", &flags.verbose, TRUE},
-	{(char *)0, (boolean *)0, FALSE}
+	{"tombstone",&flags.tombstone, TRUE, SET_IN_GAME},
+	{"toptenwin",&flags.toptenwin, FALSE, SET_IN_GAME},
+	{"use_inverse",   &iflags.wc_inverse, FALSE, SET_IN_GAME},		/*WC*/
+	{"verbose", &flags.verbose, TRUE, SET_IN_GAME},
+	{(char *)0, (boolean *)0, FALSE, 0}
 };
 
 /* compound options, for option_help() and external programs like Amiga
  * frontend */
-#define SET_IN_FILE	0 /* config file option only, not visible in game
-			   * or via program */
-#define SET_VIA_PROG	1 /* may be set via extern program, not seen in game */
-#define DISP_IN_GAME	2 /* may be set via extern program, displayed in game */
-#define SET_IN_GAME	3 /* may be set via extern program or set in the game */
 static struct Comp_Opt
 {
 	const char *name, *descr;
@@ -201,14 +200,18 @@
 } compopt[] = {
 	{ "align",    "your starting alignment (lawful, neutral, or chaotic)",
 						8, DISP_IN_GAME },
+	{ "align_message", "message window alignment", 20, DISP_IN_GAME }, 	/*WC*/
+	{ "align_status", "status window alignment", 20, DISP_IN_GAME }, 	/*WC*/
 #ifdef MAC
 	{ "background", "the color of the background (black or white)",
 						6, SET_IN_FILE },
 #endif
+	{ "boulder",  "the symbol to use for displaying boulders",
+						1, SET_IN_GAME },
 	{ "catname",  "the name of your (first) cat (e.g., catname:Tabby)",
 						PL_PSIZ, DISP_IN_GAME },
 	{ "disclose", "the kinds of information to disclose at end of game",
-						sizeof(flags.end_disclose),
+						sizeof(flags.end_disclose) * 2,
 						SET_IN_GAME },
 	{ "dogname",  "the name of your (first) dog (e.g., dogname:Fang)",
 						PL_PSIZ, DISP_IN_GAME },
@@ -216,18 +219,24 @@
 						MAXDCHARS+1, SET_IN_FILE },
 	{ "effects",  "the symbols to use in drawing special effects",
 						MAXECHARS+1, SET_IN_FILE },
-#ifdef MAC
-	{ "fontmap", "the font to use in the map window", 40, SET_IN_FILE },
-	{ "fontmessage", "the font to use in the message window",
-						40, SET_IN_FILE },
-	{ "fonttext", "the font to use in text windows", 40, SET_IN_FILE },
-#endif
+	{ "font_map", "the font to use in the map window", 40, DISP_IN_GAME },	/*WC*/
+	{ "font_menu", "the font to use in menus", 40, DISP_IN_GAME },		/*WC*/
+	{ "font_message", "the font to use in the message window",
+						40, DISP_IN_GAME },		/*WC*/
+	{ "font_size_map", "the size of the map font", 20, DISP_IN_GAME },	/*WC*/
+	{ "font_size_menu", "the size of the map font", 20, DISP_IN_GAME },	/*WC*/
+	{ "font_size_message", "the size of the map font", 20, DISP_IN_GAME },	/*WC*/
+	{ "font_size_status", "the size of the map font", 20, DISP_IN_GAME },	/*WC*/
+	{ "font_size_text", "the size of the map font", 20, DISP_IN_GAME },	/*WC*/
+	{ "font_status", "the font to use in status window", 40, DISP_IN_GAME }, /*WC*/
+	{ "font_text", "the font to use in text windows", 40, DISP_IN_GAME },	/*WC*/
 	{ "fruit",    "the name of a fruit you enjoy eating",
 						PL_FSIZ, SET_IN_GAME },
 	{ "gender",   "your starting gender (male or female)",
 						8, DISP_IN_GAME },
 	{ "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
 						PL_PSIZ, DISP_IN_GAME },
+	{ "map_mode", "map display mode under Windows", 20, DISP_IN_GAME },	/*WC*/
 	{ "menustyle", "user interface for object selection",
 						MENUTYPELEN, SET_IN_GAME },
 	{ "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
@@ -268,22 +277,29 @@
 						20, SET_IN_GAME },
 	{ "pickup_types", "types of objects to pick up automatically",
 						MAXOCLASSES, SET_IN_GAME },
+	{ "player_selection", "choose character via dialog or prompts",
+						12, DISP_IN_GAME },
 	{ "race",     "your starting race (e.g., Human, Elf)",
 						PL_CSIZ, DISP_IN_GAME },
 	{ "role",     "your starting role (e.g., Barbarian, Valkyrie)",
 						PL_CSIZ, DISP_IN_GAME },
 	{ "scores",   "the parts of the score list you wish to see",
 						32, SET_IN_GAME },
+	{ "scroll_margin", "scroll map when this far from the edge", 20, DISP_IN_GAME }, /*WC*/
 #ifdef MSDOS
 	{ "soundcard", "type of sound card to use", 20, SET_IN_FILE },
 #endif
 	{ "suppress_alert", "suppress alerts about version-specific features",
 						8, SET_IN_GAME },
+	{ "tile_width", "width of tiles", 20, DISP_IN_GAME},	/*WC*/
+	{ "tile_height", "height of tiles", 20, DISP_IN_GAME},	/*WC*/
+	{ "tile_file", "name of tile file", 70, DISP_IN_GAME},	/*WC*/
 	{ "traps",    "the symbols to use in drawing traps",
 						MAXTCHARS+1, SET_IN_FILE },
 #ifdef MAC
 	{"use_stone", "use stone background patterns", 8, SET_IN_FILE },
 #endif
+	{ "vary_msgcount", "show more old messages at a time", 20, DISP_IN_GAME }, /*WC*/
 #ifdef MSDOS
 	{ "video",    "method of video updating", 20, SET_IN_FILE },
 #endif
@@ -293,11 +309,10 @@
 	{ "videoshades", "gray shades to map to black/gray/white",
 						32, DISP_IN_GAME },
 #endif
-#if 0
-	{ "warnlevel", "minimum monster level to trigger warning", 4, SET_IN_GAME },
-#endif
+	{ "windowcolors",  "the foreground/background colors of windows",	/*WC*/
+						80, DISP_IN_GAME },
 	{ "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
-	{ (char *)0, (char *)0, 0 }
+	{ (char *)0, (char *)0, 0, 0 }
 };
 
 #ifdef OPTION_LISTS_ONLY
@@ -383,7 +398,6 @@
 STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int));
 STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
 STATIC_DCL void FDECL(escapes, (const char *, char *));
-STATIC_DCL int FDECL(boolopt_only_initial, (int));
 STATIC_DCL void FDECL(rejectoption, (const char *));
 STATIC_DCL void FDECL(badoption, (const char *));
 STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P));
@@ -396,9 +410,12 @@
 STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
 STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P));
 STATIC_DCL void FDECL(warning_opts, (char *,const char *));
-#if 0
-STATIC_DCL int FDECL(warnlevel_opts, (char *, const char *));
-#endif
+STATIC_DCL void FDECL(duplicate_opt_detection, (const char *, int));
+
+STATIC_OVL void FDECL(wc_set_font_name, (int, char *));
+STATIC_OVL int FDECL(wc_set_window_colors, (char *));
+STATIC_OVL boolean FDECL(is_wc_option, (const char *));
+STATIC_OVL boolean FDECL(wc_supported, (const char *));
 
 /* check whether a user-supplied option string is a proper leading
    substring of a particular option name; option string might have
@@ -451,6 +468,9 @@
 	/* initialize the random number generator */
 	setrandom();
 
+	/* for detection of configfile options specified multiple times */
+	iflags.opt_booldup = iflags.opt_compdup = (int *)0;
+	
 	for (i = 0; boolopt[i].name; i++) {
 		if (boolopt[i].addr)
 			*(boolopt[i].addr) = boolopt[i].initvalue;
@@ -474,6 +494,7 @@
 		monsyms[i] = (uchar) def_monsyms[i];
 	for (i = 0; i < WARNCOUNT; i++)
 		warnsyms[i] = def_warnsyms[i].sym;
+	iflags.bouldersym = 0;
 	flags.warnlevel = 1;
 	flags.warntype = 0L;
 
@@ -483,6 +504,8 @@
 	flags.pickup_types[0] = '\0';
 	flags.pickup_burden = MOD_ENCUMBER;
 
+	for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
+		flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
 	switch_graphics(ASCII_GRAPHICS);	/* set default characters */
 #if defined(UNIX) && defined(TTY_GRAPHICS)
 	/*
@@ -628,35 +649,12 @@
     *tp = '\0';
 }
 
-/* some boolean options can only be set on start-up */
-STATIC_OVL int
-boolopt_only_initial(i)
-int i;
-{
-	return (boolopt[i].addr == &flags.female
-	     || boolopt[i].addr == &flags.legacy
-#if defined(MICRO) && !defined(AMIGA)
-	     || boolopt[i].addr == &iflags.rawio
-	     || boolopt[i].addr == &iflags.BIOS
-#endif
-#if defined(MSDOS) && defined(USE_TILES)
-	     || boolopt[i].addr == &iflags.preload_tiles
-#endif
-	);
-}
-
 STATIC_OVL void
 rejectoption(optname)
 const char *optname;
 {
 #ifdef MICRO
-# ifdef AMIGA
-	if(FromWBench){
-		pline("\"%s\" settable only from %s or in icon.",
-			optname, configfile);
-	} else
-# endif
-		pline("\"%s\" settable only from %s.", optname, configfile);
+	pline("\"%s\" settable only from %s.", optname, configfile);
 #else
 	pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
 			configfile);
@@ -678,16 +676,11 @@
 	else return;
 #endif
 
-# ifdef AMIGA
-	if(ami_wbench_badopt(opts)) {
-# endif
 	if(from_file)
 	    raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
 	else
 	    raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
-# ifdef AMIGA
-	}
-# endif
+
 	wait_synch();
 }
 
@@ -826,48 +819,6 @@
 	    warnsyms[i] = graph_chars[i];
 }
 
-#if 0
-/* warnlevel is unnecessary with the new warning introduced in 3.3.1 */
-STATIC_OVL int
-warnlevel_opts(op, optn)
-char *op;
-const char *optn;
-{
-	char buf[BUFSZ];
-	int twarnlevel;
-	boolean rejectlevel = FALSE;
-
-	if (op) {
-		twarnlevel = atoi(op);
-		if (twarnlevel >= WARNCOUNT || twarnlevel < 1)
-			rejectlevel = TRUE;
-		else {
-			flags.warnlevel = twarnlevel;
-			see_monsters();
-		}
-	}
-	if (rejectlevel) {
-		if (!initial)
-			pline("warnlevel must be 1 to %d.", WARNCOUNT - 1);
-		else {
-			Sprintf(buf,
-			    "\n%s=%s Invalid warnlevel ignored (must be 1 to %d)",
-				optn, op, WARNCOUNT - 1);
-			badoption(buf);
-		}
-		return 0;
-	}
-	if (!initial) {
-		if (flags.warnlevel < WARNCOUNT -1)
-			Sprintf(buf, "s %d to %d", flags.warnlevel, WARNCOUNT - 1);
-		else
-			Sprintf(buf, " %d", flags.warnlevel);
-		pline("Warning level%s will be displayed.", buf);
-	}
-	return 1;
-}
-#endif
-
 STATIC_OVL int
 feature_alert_opts(op, optn)
 char *op;
@@ -902,6 +853,79 @@
 }
 
 void
+set_duplicate_opt_detection(on_or_off)
+int on_or_off;
+{
+	int k, *optptr;
+	if (on_or_off != 0) {
+		/*-- ON --*/
+		if (iflags.opt_booldup)
+			impossible("iflags.opt_booldup already on (memory leak)");
+		iflags.opt_booldup = (int *)alloc(SIZE(boolopt) * sizeof(int));
+		optptr = iflags.opt_booldup;
+		for (k = 0; k < SIZE(boolopt); ++k)
+			*optptr++ = 0;
+			
+		if (iflags.opt_compdup)
+			impossible("iflags.opt_compdup already on (memory leak)");
+		iflags.opt_compdup = (int *)alloc(SIZE(compopt) * sizeof(int));
+		optptr = iflags.opt_compdup;
+		for (k = 0; k < SIZE(compopt); ++k)
+			*optptr++ = 0;
+	} else {
+		/*-- OFF --*/
+		if (iflags.opt_booldup) free((genericptr_t) iflags.opt_booldup);
+		iflags.opt_booldup = (int *)0;
+		if (iflags.opt_compdup) free((genericptr_t) iflags.opt_compdup);
+		iflags.opt_compdup = (int *)0;
+	} 
+}
+
+STATIC_OVL void
+duplicate_opt_detection(opts, bool_or_comp)
+const char *opts;
+int bool_or_comp;	/* 0 == boolean option, 1 == compound */
+{
+	int i, *optptr;
+#if defined(MAC)
+	/* the Mac has trouble dealing with the output of messages while
+	 * processing the config file.  That should get fixed one day.
+	 * For now just return.
+	 */
+	return;
+#endif
+	if ((bool_or_comp == 0) && iflags.opt_booldup && initial && from_file) {
+	    for (i = 0; boolopt[i].name; i++) {
+		if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
+			optptr = iflags.opt_booldup + i;
+			if (*optptr == 1) {
+			    raw_printf(
+				"\nWarning - Boolean option specified multiple times: %s.\n",
+					opts);
+			        wait_synch();
+			}
+			*optptr += 1;
+			break; /* don't match multiple options */
+		}
+	    }
+	} else if ((bool_or_comp == 1) && iflags.opt_compdup && initial && from_file) {
+	    for (i = 0; compopt[i].name; i++) {
+		if (match_optname(opts, compopt[i].name, strlen(compopt[i].name), TRUE)) {
+			optptr = iflags.opt_compdup + i;
+			if (*optptr == 1) {
+			    raw_printf(
+				"\nWarning - compound option specified multiple times: %s.\n",
+					compopt[i].name);
+			        wait_synch();
+			}
+			*optptr += 1;
+			break; /* don't match multiple options */
+		}
+	    }
+	}
+}
+
+void
 parseoptions(opts, tinitial, tfrom_file)
 register char *opts;
 boolean tinitial, tfrom_file;
@@ -940,6 +964,8 @@
 	if (match_optname(opts, "colour", 5, FALSE))
 		Strcpy(opts, "color");	/* fortunately this isn't longer */
 
+	duplicate_opt_detection(opts, 1);	/* 1 means compound opts */
+
 	/* special boolean options */
 
 	if (match_optname(opts, "female", 3, FALSE)) {
@@ -983,11 +1009,15 @@
 			case 'F':
 			    preferred_pet = 'c';
 			    break;
+			case 'n':	/* no pet */
+			case 'N':
+			    preferred_pet = 'n';
+			    break;
 			default:
 			    pline("Unrecognized pet type '%s'", op);
 			    break;
 		    }
-		} else if (negated) preferred_pet = 0;
+		} else if (negated) preferred_pet = 'n';
 		return;
 	}
 
@@ -1024,6 +1054,78 @@
 		return;
 	}
 
+	/* WINCAP
+	 * setting font options  */
+	fullname = "font";
+	if (!strncmpi(opts, fullname, 4))
+	{
+		int wintype = -1;
+		char *fontopts = opts + 4;
+
+		if (!strncmpi(fontopts, "map", 3) ||
+		    !strncmpi(fontopts, "_map", 4))
+			wintype = NHW_MAP;
+		else if (!strncmpi(fontopts, "message", 7) ||
+			 !strncmpi(fontopts, "_message", 8))
+			wintype = NHW_MESSAGE;
+		else if (!strncmpi(fontopts, "text", 4) ||
+			 !strncmpi(fontopts, "_text", 5))
+			wintype = NHW_TEXT;			
+		else if (!strncmpi(fontopts, "menu", 4) ||
+			 !strncmpi(fontopts, "_menu", 5))
+			wintype = NHW_MENU;
+		else if (!strncmpi(fontopts, "status", 6) ||
+			 !strncmpi(fontopts, "_status", 7))
+			wintype = NHW_STATUS;
+		else if (!strncmpi(fontopts, "_size", 5)) {
+			if (!strncmpi(fontopts, "_size_map", 8))
+				wintype = NHW_MAP;
+			else if (!strncmpi(fontopts, "_size_message", 12))
+				wintype = NHW_MESSAGE;
+			else if (!strncmpi(fontopts, "_size_text", 9))
+				wintype = NHW_TEXT;
+			else if (!strncmpi(fontopts, "_size_menu", 9))
+				wintype = NHW_MENU;
+			else if (!strncmpi(fontopts, "_size_status", 11))
+				wintype = NHW_STATUS;
+			else {
+				badoption(opts);
+				return;
+			}
+			if (wintype > 0 && !negated &&
+			    (op = string_for_opt(opts, FALSE)) != 0) {
+			    switch(wintype)  {
+			    	case NHW_MAP:
+					iflags.wc_fontsiz_map = atoi(op);
+					break;
+			    	case NHW_MESSAGE:
+					iflags.wc_fontsiz_message = atoi(op);
+					break;
+			    	case NHW_TEXT:
+					iflags.wc_fontsiz_text = atoi(op);
+					break;
+			    	case NHW_MENU:
+					iflags.wc_fontsiz_menu = atoi(op);
+					break;
+			    	case NHW_STATUS:
+					iflags.wc_fontsiz_status = atoi(op);
+					break;
+			    }
+			}
+			return;
+		} else {
+			badoption(opts);
+		}
+		if (wintype > 0 &&
+		    (op = string_for_opt(opts, FALSE)) != 0) {
+			wc_set_font_name(wintype, op);
+#ifdef MAC
+			set_font_name (wintype, op);
+#endif
+			return;
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
 #ifdef CHANGE_COLOR
 #ifdef MAC
 	fullname = "use_stone";
@@ -1047,30 +1149,13 @@
 		}
 		return;
 	}
-	
-	fullname = "font";
-	if (!strncmpi(opts, fullname, 4))
-	{	int wintype = -1;
-	
-		opts += 4;
-		if (!strncmpi (opts, "map", 3))
-			wintype = NHW_MAP;
-		else if (!strncmpi (opts, "message", 7))
-			wintype = NHW_MESSAGE;
-		else if (!strncmpi (opts, "text", 4))
-			wintype = NHW_TEXT;
-				
-		if (wintype > 0 && (op = string_for_env_opt(fullname, opts, FALSE)) != 0)
-		{	set_font_name (wintype, op);
-		}
-		return;
-	}
-#endif
+#endif	
+
 	if (match_optname(opts, "palette", 3, TRUE)
 # ifdef MAC
-					|| match_optname(opts, "hicolor", 3, TRUE)
+	    || match_optname(opts, "hicolor", 3, TRUE)
 # endif
-									) {
+							) {
 	    int color_number, color_incr;
 
 # ifdef MAC
@@ -1138,7 +1223,7 @@
 	    }
 	    return;
 	}
-#endif
+#endif /* CHANGE_COLOR */
 
 	if (match_optname(opts, "fruit", 2, TRUE)) {
 		char empty_str = '\0';
@@ -1266,15 +1351,26 @@
 		else warning_opts(opts, fullname);
 		return;
 	}
-#if 0
-	fullname = "warnlevel";
-	if (match_optname(opts, fullname, 5, TRUE)) {
-	    op = string_for_opt(opts, negated);
-	    if (negated) bad_negation(fullname, FALSE);
-	    else if (op) (void) warnlevel_opts(op,fullname);
-	    return;
+	/* boulder:symbol */
+	fullname = "boulder";
+	if (match_optname(opts, fullname, 7, TRUE)) {
+		if (negated) {
+		    bad_negation(fullname, FALSE);
+		    return;
+		}
+/*		if (!(opts = string_for_env_opt(fullname, opts, FALSE))) */
+		if (!(opts = string_for_opt(opts, FALSE)))
+			return;
+		escapes(opts, opts);
+
+		/*
+		 * Override the default boulder symbol.
+		 */
+		iflags.bouldersym = (uchar) opts[0];
+		if (!initial) need_redraw = TRUE;
+		return;
 	}
-#endif
+
 	/* name:string */
 	fullname = "name";
 	if (match_optname(opts, fullname, 4, TRUE)) {
@@ -1324,9 +1420,47 @@
 		return;
 	}
 
+	/* WINCAP
+	 * align_status:[left|top|right|bottom] */
+	fullname = "align_status";
+	if (match_optname(opts, fullname, sizeof("align_status")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if (op && !negated) {
+		    if (!strncmpi (op, "left", sizeof("left")-1))
+			iflags.wc_align_status = ALIGN_LEFT;
+		    else if (!strncmpi (op, "top", sizeof("top")-1))
+			iflags.wc_align_status = ALIGN_TOP;
+		    else if (!strncmpi (op, "right", sizeof("right")-1))
+			iflags.wc_align_status = ALIGN_RIGHT;
+		    else if (!strncmpi (op, "bottom", sizeof("bottom")-1))
+			iflags.wc_align_status = ALIGN_BOTTOM;
+		    else
+			badoption(opts);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
+	/* WINCAP
+	 * align_message:[left|top|right|bottom] */
+	fullname = "align_message";
+	if (match_optname(opts, fullname, sizeof("align_message")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if (op && !negated) {
+		    if (!strncmpi (op, "left", sizeof("left")-1))
+			iflags.wc_align_message = ALIGN_LEFT;
+		    else if (!strncmpi (op, "top", sizeof("top")-1))
+			iflags.wc_align_message = ALIGN_TOP;
+		    else if (!strncmpi (op, "right", sizeof("right")-1))
+			iflags.wc_align_message = ALIGN_RIGHT;
+		    else if (!strncmpi (op, "bottom", sizeof("bottom")-1))
+			iflags.wc_align_message = ALIGN_BOTTOM;
+		    else
+			badoption(opts);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
 	/* align:string */
 	fullname = "align";
-	if (match_optname(opts, fullname, 4, TRUE)) {
+	if (match_optname(opts, fullname, sizeof("align")-1, TRUE)) {
 		if (negated) bad_negation(fullname, FALSE);
 		else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
 			if ((flags.initalign = str2align(op)) == ROLE_NONE)
@@ -1359,19 +1493,19 @@
 				case 'u':
 					flags.pickup_burden = UNENCUMBERED;
 					break;
-				/* Burdened (slight encumberance) */
+				/* Burdened (slight encumbrance) */
 				case 'b':
 					flags.pickup_burden = SLT_ENCUMBER;
 					break;
-				/* streSsed (moderate encumberance) */
+				/* streSsed (moderate encumbrance) */
 				case 's':
 					flags.pickup_burden = MOD_ENCUMBER;
 					break;
-				/* straiNed (heavy encumberance) */
+				/* straiNed (heavy encumbrance) */
 				case 'n':
 					flags.pickup_burden = HVY_ENCUMBER;
 					break;
-				/* OverTaxed (extreme encumberance) */
+				/* OverTaxed (extreme encumbrance) */
 				case 'o':
 				case 't':
 					flags.pickup_burden = EXT_ENCUMBER;
@@ -1447,10 +1581,46 @@
 		}
 		return;
 	}
+	/* WINCAP
+	 * player_selection: dialog | prompts */
+	fullname = "player_selection";
+	if (match_optname(opts, fullname, sizeof("player_selection")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if (op && !negated) {
+		    if (!strncmpi (op, "dialog", sizeof("dialog")-1))
+			iflags.wc_player_selection = VIA_DIALOG;
+		    else if (!strncmpi (op, "prompt", sizeof("prompt")-1))
+			iflags.wc_player_selection = VIA_PROMPTS;
+		    else
+		    	badoption(opts);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
 
 	/* things to disclose at end of game */
-	if (match_optname(opts, "disclose", 4, TRUE)) {
-		flags.end_disclose[0] = '\0';	/* all */
+	if (match_optname(opts, "disclose", 7, TRUE)) {
+		/*
+		 * The order that the end_disclore options are stored:
+		 * inventory, attribs, vanquished, genocided, conduct
+		 * There is an array in flags:
+		 *	end_disclose[NUM_DISCLOSURE_OPT];
+		 * with option settings for the each of the following:
+		 * iagvc [see disclosure_options in decl.c]:
+		 * Legal setting values in that array are:
+		 *	DISCLOSE_PROMPT_DEFAULT_YES  ask with default answer yes
+		 *	DISCLOSE_PROMPT_DEFAULT_NO   ask with default answer no
+		 *	DISCLOSE_YES_WITHOUT_PROMPT  always disclose and don't ask
+		 *	DISCLOSE_NO_WITHOUT_PROMPT   never disclose and don't ask
+		 *
+		 * Those setting values can be used in the option
+		 * string as a prefix to get the desired behaviour.
+		 *
+		 * For backward compatibility, no prefix is required,
+		 * and the presence of a i,a,g,v, or c without a
+		 * prefix sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT;
+		 */
+		boolean badopt = FALSE;
+		int idx, prefix_val;
 		if (!(op = string_for_opt(opts, TRUE))) {
 			/* for backwards compatibility, "disclose" without a
 			 * value means all (was inventory and attributes,
@@ -1458,7 +1628,11 @@
 			 * it means "none"
 			 * (note "none" contains none of "iavkgc")
 			 */
-			if (negated) Strcpy(flags.end_disclose, "none");
+			for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++) {
+				if (negated)
+				    flags.end_disclose[num] = DISCLOSE_NO_WITHOUT_PROMPT;
+			 	else flags.end_disclose[num] = DISCLOSE_PROMPT_DEFAULT_YES;
+			}
 			return;
 		}
 		if (negated) {
@@ -1466,16 +1640,40 @@
 			return;
 		}
 		num = 0;
+		prefix_val = -1;
 		while (*op && num < sizeof flags.end_disclose - 1) {
-			register char c;
+			register char c, *dop;
+			static char valid_settings[] = {
+				DISCLOSE_PROMPT_DEFAULT_YES,
+				DISCLOSE_PROMPT_DEFAULT_NO,
+				DISCLOSE_YES_WITHOUT_PROMPT,
+				DISCLOSE_NO_WITHOUT_PROMPT,
+				'\0'
+			};
 			c = lowc(*op);
 			if (c == 'k') c = 'v';	/* killed -> vanquished */
-			if (!index(flags.end_disclose, c)) {
-				flags.end_disclose[num++] = c;
-				flags.end_disclose[num] = '\0';	/* for index */
-			}
+			dop = index(disclosure_options, c);
+			if (dop) {
+				idx = dop - disclosure_options;
+				if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
+				    impossible("bad disclosure index %d %c",
+							idx, c);
+				    continue;
+				}
+				if (prefix_val != -1) {
+				    flags.end_disclose[idx] = prefix_val;
+				    prefix_val = -1;
+				} else
+				    flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
+			} else if (index(valid_settings, c)) {
+				prefix_val = c;
+			} else if (c == ' ') {
+				/* do nothing */
+			} else
+				badopt = TRUE;				
 			op++;
 		}
+		if (badopt) badoption(opts);
 		return;
 	}
 
@@ -1590,6 +1788,91 @@
 	}
 #endif /* MSDOS */
 
+	/* WINCAP
+	 * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12|ascii8x12|
+			ascii16x12|ascii12x16|ascii10x18|fit_to_screen] */
+	fullname = "map_mode";
+	if (match_optname(opts, fullname, sizeof("map_mode")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if (op && !negated) {
+		    if (!strncmpi (op, "tiles", sizeof("tiles")-1))
+			iflags.wc_map_mode = MAP_MODE_TILES;
+		    else if (!strncmpi (op, "ascii4x6", sizeof("ascii4x6")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII4x6;
+		    else if (!strncmpi (op, "ascii6x8", sizeof("ascii6x8")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII6x8;
+		    else if (!strncmpi (op, "ascii8x8", sizeof("ascii8x8")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII8x8;
+		    else if (!strncmpi (op, "ascii16x8", sizeof("ascii16x8")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII16x8;
+		    else if (!strncmpi (op, "ascii7x12", sizeof("ascii7x12")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII7x12;
+		    else if (!strncmpi (op, "ascii8x12", sizeof("ascii8x12")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII8x12;
+		    else if (!strncmpi (op, "ascii16x12", sizeof("ascii16x12")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII16x12;
+		    else if (!strncmpi (op, "ascii12x16", sizeof("ascii12x16")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII12x16;
+		    else if (!strncmpi (op, "ascii10x18", sizeof("ascii10x18")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII10x18;
+		    else if (!strncmpi (op, "fit_to_screen", sizeof("fit_to_screen")-1))
+			iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
+		    else
+		    	badoption(opts);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
+	/* WINCAP
+	 * scroll_margin:nn */
+	fullname = "scroll_margin";
+	if (match_optname(opts, fullname, sizeof("scroll_margin")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if ((negated && !op) || (!negated && op)) {
+			iflags.wc_scroll_margin = negated ? 5 : atoi(op);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
+	/* WINCAP
+	 * tile_width:nn */
+	fullname = "tile_width";
+	if (match_optname(opts, fullname, sizeof("tile_width")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if ((negated && !op) || (!negated && op)) {
+			iflags.wc_tile_width = negated ? 0 : atoi(op);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
+	/* WINCAP
+	 * tile_file:name */
+	fullname = "tile_file";
+	if (match_optname(opts, fullname, sizeof("tile_file")-1, TRUE)) {
+		if ((op = string_for_opt(opts, FALSE)) != 0) {
+			if (iflags.wc_tile_file) free(iflags.wc_tile_file);
+			iflags.wc_tile_file = (char *)alloc(strlen(op) + 1);
+			Strcpy(iflags.wc_tile_file, op);
+		}
+		return;
+	}
+	/* WINCAP
+	 * tile_height:nn */
+	fullname = "tile_height";
+	if (match_optname(opts, fullname, sizeof("tile_height")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if ((negated && !op) || (!negated && op)) {
+			iflags.wc_tile_height = negated ? 0 : atoi(op);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
+	/* WINCAP
+	 * vary_msgcount:nn */
+	fullname = "vary_msgcount";
+	if (match_optname(opts, fullname, sizeof("vary_msgcount")-1, TRUE)) {
+		op = string_for_opt(opts, negated);
+		if ((negated && !op) || (!negated && op)) {
+			iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
 
 	fullname = "windowtype";
 	if (match_optname(opts, fullname, 3, TRUE)) {
@@ -1604,6 +1887,19 @@
 	    return;
 	}
 
+	/* WINCAP
+	 * setting window colors
+         * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
+         */
+	fullname = "windowcolors";
+	if (match_optname(opts, fullname, 7, TRUE)) {
+		if ((op = string_for_opt(opts, FALSE)) != 0) {
+			if (!wc_set_window_colors(op))
+				badoption(opts);
+		} else if (negated) bad_negation(fullname, TRUE);
+		return;
+	}
+
 	/* menustyle:traditional or combo or full or partial */
 	if (match_optname(opts, "menustyle", 4, TRUE)) {
 		int tmp;
@@ -1681,13 +1977,15 @@
 			    return;
 			}
 			/* options that must come from config file */
-			if (!initial && boolopt_only_initial(i)) {
+			if (!initial && (boolopt[i].optflags == SET_IN_FILE)) {
 			    rejectoption(boolopt[i].name);
 			    return;
 			}
 
 			*(boolopt[i].addr) = !negated;
 
+			duplicate_opt_detection(boolopt[i].name, 0);
+
 #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV)
 			if (FALSE
 # ifdef TERMLIB
@@ -1763,10 +2061,14 @@
 			    vision_recalc(2);		/* shut down vision */
 			    vision_full_recalc = 1;	/* delayed recalc */
 			}
-
+			else if ((boolopt[i].addr) == &iflags.use_inverse) {
+			    need_redraw = TRUE;
+			}
+			else if ((boolopt[i].addr) == &iflags.hilite_pet) {
+			    need_redraw = TRUE;
+			}
 #ifdef TEXTCOLOR
-			else if ((boolopt[i].addr) == &iflags.use_color
-			      || (boolopt[i].addr) == &iflags.hilite_pet) {
+			else if ((boolopt[i].addr) == &iflags.use_color) {
 			    need_redraw = TRUE;
 # ifdef TOS
 			    if ((boolopt[i].addr) == &iflags.use_color
@@ -1861,6 +2163,7 @@
 #endif
 
 static char fmtstr_doset_add_menu[] = "%s%-15s [%s]   "; 
+static char fmtstr_doset_add_menu_tab[] = "%s\t[%s]";
 
 STATIC_OVL void
 doset_add_menu(win, option, indexoffset)
@@ -1893,7 +2196,10 @@
 	}
     }
     /* "    " replaces "a - " -- assumes menus follow that style */
-    Sprintf(buf, fmtstr_doset_add_menu, (any.a_int ? "" : "    "), option, value);
+    if (!iflags.menu_tab_sep)
+	Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ", option, value);
+    else
+	Sprintf(buf, fmtstr_doset_add_menu_tab, option, value);
     add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
 }
 
@@ -1922,15 +2228,23 @@
 	for (pass = 0; pass <= 1; pass++)
 	    for (i = 0; boolopt[i].name; i++)
 		if ((bool_p = boolopt[i].addr) != 0 &&
-			(boolopt_only_initial(i) ^ pass)) {
+			((boolopt[i].optflags == DISP_IN_GAME && pass == 0) ||
+			 (boolopt[i].optflags == SET_IN_GAME && pass == 1))) {
 		    if (bool_p == &flags.female) continue;  /* obsolete */
 #ifdef WIZARD
 		    if (bool_p == &iflags.sanity_check && !wizard) continue;
+		    if (bool_p == &iflags.menu_tab_sep && !wizard) continue;
 #endif
+		    if (is_wc_option(boolopt[i].name) &&
+			!wc_supported(boolopt[i].name)) continue;
 		    any.a_int = (pass == 0) ? 0 : i + 1;
-		    Sprintf(buf, "%s%-13s [%s]",
+		    if (!iflags.menu_tab_sep)
+			Sprintf(buf, "%s%-13s [%s]",
 			    pass == 0 ? "    " : "",
 			    boolopt[i].name, *bool_p ? "true" : "false");
+ 		    else
+			Sprintf(buf, "%s\t[%s]",
+			    boolopt[i].name, *bool_p ? "true" : "false");
 		    add_menu(tmpwin, NO_GLYPH, &any, 0, 0,
 			     ATR_NONE, buf, MENU_UNSELECTED);
 		}
@@ -1954,8 +2268,9 @@
 		    strlen(compopt[i].name) > (unsigned) biggest_name)
 			biggest_name = (int) strlen(compopt[i].name);
 	if (biggest_name > 30) biggest_name = 30;
-	Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name);
-
+	if (!iflags.menu_tab_sep)
+		Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name);
+	
 	/* deliberately put `name', `role', `race', `gender' first */
 	doset_add_menu(tmpwin, "name", 0);
 	doset_add_menu(tmpwin, "role", 0);
@@ -1970,6 +2285,9 @@
 		    	    !strcmp(compopt[i].name, "race") ||
 		    	    !strcmp(compopt[i].name, "gender"))
 		    	    	continue;
+		    	else if (is_wc_option(compopt[i].name) &&
+					!wc_supported(compopt[i].name))
+		    		continue;
 		    	else
 				doset_add_menu(tmpwin, compopt[i].name,
 					(pass == DISP_IN_GAME) ? 0 : indexoffset);
@@ -1997,6 +2315,8 @@
 		    Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
 			    boolopt[opt_indx].name);
 		    parseoptions(buf, setinitial, fromfile);
+		    if (wc_supported(boolopt[opt_indx].name))
+			preference_update(boolopt[opt_indx].name);
 		} else {
 		    /* compound option */
 		    opt_indx -= boolcount;
@@ -2009,6 +2329,8 @@
 			/* pass the buck */
 			parseoptions(buf, setinitial, fromfile);
 		    }
+		    if (wc_supported(compopt[opt_indx].name))
+			preference_update(compopt[opt_indx].name);
 		}
 	    }
 	    free((genericptr_t)pick_list);
@@ -2032,7 +2354,7 @@
     char buf[BUFSZ];
     boolean retval = FALSE;
     
-    /* Special handling of menustyle, pickup_burden, and pickup_types. */
+    /* Special handling of menustyle, pickup_burden, and pickup_types, disclose options. */
     if (!strcmp("menustyle", optname)) {
 	const char *style_name;
 	menu_item *style_pick = (menu_item *)0;
@@ -2064,7 +2386,7 @@
 		add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0,
 			 ATR_NONE, burden_name, MENU_UNSELECTED);
         }
-	end_menu(tmpwin, "Select encumberence level:");
+	end_menu(tmpwin, "Select encumbrance level:");
 	if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
 		flags.pickup_burden = burden_pick->item.a_int - 1;
 		free((genericptr_t)burden_pick);
@@ -2075,6 +2397,70 @@
 	/* parseoptions will prompt for the list of types */
 	parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
 	retval = TRUE;
+    } else if (!strcmp("disclose", optname)) {
+	int pick_cnt, pick_idx, opt_idx;
+	menu_item *disclosure_category_pick = (menu_item *)0;
+	/*
+	 * The order of disclose_names[]
+         * must correspond to disclosure_options in decl.h
+         */
+	static const char *disclosure_names[] = {
+		"inventory", "attributes", "vanquished", "genocides", "conduct"
+	};
+	int disc_cat[NUM_DISCLOSURE_OPTIONS];
+	const char *disclosure_name;
+
+        tmpwin = create_nhwindow(NHW_MENU);
+	start_menu(tmpwin);
+	for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
+		disclosure_name = disclosure_names[i];
+		any.a_int = i + 1;
+		add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
+			 ATR_NONE, disclosure_name, MENU_UNSELECTED);
+		disc_cat[i] = 0;
+        }
+	end_menu(tmpwin, "Change which disclosure options categories:");
+	if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_category_pick)) > 0) {
+	    for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
+		opt_idx = disclosure_category_pick[pick_idx].item.a_int - 1;
+		disc_cat[opt_idx] = 1;
+	    }
+	    free((genericptr_t)disclosure_category_pick);
+	    disclosure_category_pick = (menu_item *)0;
+	}
+	destroy_nhwindow(tmpwin);
+
+	for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
+	    if (disc_cat[i]) {
+	    	char dbuf[BUFSZ];
+		menu_item *disclosure_option_pick = (menu_item *)0;
+		Sprintf(dbuf, "Disclosure options for %s:", disclosure_names[i]);
+	        tmpwin = create_nhwindow(NHW_MENU);
+		start_menu(tmpwin);
+		any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
+		add_menu(tmpwin, NO_GLYPH, &any, 'a', 0,
+			ATR_NONE,"Never disclose and don't prompt", MENU_UNSELECTED);
+		any.a_void = 0;
+		any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
+		add_menu(tmpwin, NO_GLYPH, &any, 'b', 0,
+			ATR_NONE,"Always disclose and don't prompt", MENU_UNSELECTED);
+		any.a_void = 0;
+		any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
+		add_menu(tmpwin, NO_GLYPH, &any, 'c', 0,
+			ATR_NONE,"Prompt and default answer to \"No\"", MENU_UNSELECTED);
+		any.a_void = 0;
+		any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
+		add_menu(tmpwin, NO_GLYPH, &any, 'd', 0,
+			ATR_NONE,"Prompt and default answer to \"Yes\"", MENU_UNSELECTED);
+		end_menu(tmpwin, dbuf);
+		if (select_menu(tmpwin, PICK_ONE, &disclosure_option_pick) > 0) {
+			flags.end_disclose[i] = disclosure_option_pick->item.a_char;
+			free((genericptr_t)disclosure_option_pick);
+		}
+		destroy_nhwindow(tmpwin);
+	    }
+	}
+	retval = TRUE;
     }
     return retval;
 }
@@ -2091,31 +2477,99 @@
 {
 	char ocl[MAXOCLASSES+1];
 	static const char none[] = "(none)", randomrole[] = "random",
-		     to_be_done[] = "(to be done)";
-#ifdef PREFIXES_IN_USE
+		     to_be_done[] = "(to be done)",
+		     defopt[] = "default",
+		     defbrief[] = "def";
 	int i;
-#endif
 
 	buf[0] = '\0';
-	if (!strcmp(optname,"align"))
+	if (!strcmp(optname,"align_message"))
+		Sprintf(buf, "%s", iflags.wc_align_message == ALIGN_TOP     ? "top" :
+				   iflags.wc_align_message == ALIGN_LEFT    ? "left" :
+				   iflags.wc_align_message == ALIGN_BOTTOM  ? "bottom" :
+				   iflags.wc_align_message == ALIGN_RIGHT   ? "right" :
+				   defopt);
+	else if (!strcmp(optname,"align_status"))
+		Sprintf(buf, "%s", iflags.wc_align_status == ALIGN_TOP     ? "top" :
+				   iflags.wc_align_status == ALIGN_LEFT    ? "left" :
+				   iflags.wc_align_status == ALIGN_BOTTOM  ? "bottom" :
+				   iflags.wc_align_status == ALIGN_RIGHT   ? "right" :
+				   defopt);
+	else if (!strcmp(optname,"align"))
 		Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
+	else if (!strcmp(optname, "boulder"))
+		Sprintf(buf, "%c", iflags.bouldersym ?
+			iflags.bouldersym : oc_syms[(int)objects[BOULDER].oc_class]);
 	else if (!strcmp(optname, "catname")) 
 		Sprintf(buf, "%s", catname[0] ? catname : none );
-	else if (!strcmp(optname, "disclose")) 
-		Sprintf(buf, "%s",
-			flags.end_disclose[0] ? flags.end_disclose : "all" );
+	else if (!strcmp(optname, "disclose")) {
+		for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
+			char topt[2];
+			if (i) Strcat(buf," ");
+			topt[1] = '\0';
+			topt[0] = flags.end_disclose[i];
+			Strcat(buf, topt);
+			topt[0] = disclosure_options[i];
+			Strcat(buf, topt);
+		}
+	}
 	else if (!strcmp(optname, "dogname")) 
 		Sprintf(buf, "%s", dogname[0] ? dogname : none );
 	else if (!strcmp(optname, "dungeon"))
 		Sprintf(buf, "%s", to_be_done);
 	else if (!strcmp(optname, "effects"))
 		Sprintf(buf, "%s", to_be_done);
+	else if (!strcmp(optname, "font_map"))
+		Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
+	else if (!strcmp(optname, "font_message"))
+		Sprintf(buf, "%s", iflags.wc_font_message ? iflags.wc_font_message : defopt);
+	else if (!strcmp(optname, "font_status"))
+		Sprintf(buf, "%s", iflags.wc_font_status ? iflags.wc_font_status : defopt);
+	else if (!strcmp(optname, "font_menu"))
+		Sprintf(buf, "%s", iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
+	else if (!strcmp(optname, "font_text"))
+		Sprintf(buf, "%s", iflags.wc_font_text ? iflags.wc_font_text : defopt);
+	else if (!strcmp(optname, "font_size_map")) {
+		if (iflags.wc_fontsiz_map) Sprintf(buf, "%d", iflags.wc_fontsiz_map);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "font_size_message")) {
+		if (iflags.wc_fontsiz_message) Sprintf(buf, "%d",
+							iflags.wc_fontsiz_message);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "font_size_status")) {
+		if (iflags.wc_fontsiz_status) Sprintf(buf, "%d", iflags.wc_fontsiz_status);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "font_size_menu")) {
+		if (iflags.wc_fontsiz_menu) Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "font_size_text")) {
+		if (iflags.wc_fontsiz_text) Sprintf(buf, "%d",iflags.wc_fontsiz_text);
+		else Strcpy(buf, defopt);
+	}
 	else if (!strcmp(optname, "fruit")) 
 		Sprintf(buf, "%s", pl_fruit);
 	else if (!strcmp(optname, "gender"))
 		Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
 	else if (!strcmp(optname, "horsename")) 
 		Sprintf(buf, "%s", horsename[0] ? horsename : none);
+	else if (!strcmp(optname, "map_mode"))
+		Sprintf(buf, "%s",
+			iflags.wc_map_mode == MAP_MODE_TILES      ? "tiles" :
+			iflags.wc_map_mode == MAP_MODE_ASCII4x6   ? "ascii4x6" :
+			iflags.wc_map_mode == MAP_MODE_ASCII6x8   ? "ascii6x8" :
+			iflags.wc_map_mode == MAP_MODE_ASCII8x8   ? "ascii8x8" :
+			iflags.wc_map_mode == MAP_MODE_ASCII16x8  ? "ascii16x8" :
+			iflags.wc_map_mode == MAP_MODE_ASCII7x12  ? "ascii7x12" :
+			iflags.wc_map_mode == MAP_MODE_ASCII8x12  ? "ascii8x12" :
+			iflags.wc_map_mode == MAP_MODE_ASCII16x12 ? "ascii16x12" :
+			iflags.wc_map_mode == MAP_MODE_ASCII12x16 ? "ascii12x16" :
+			iflags.wc_map_mode == MAP_MODE_ASCII10x18 ? "ascii10x18" :
+			iflags.wc_map_mode == MAP_MODE_ASCII_FIT_TO_SCREEN ?
+			"fit_to_screen" : defopt);
 	else if (!strcmp(optname, "menustyle")) 
 		Sprintf(buf, "%s", menutype[(int)flags.menu_style] );
 	else if (!strcmp(optname, "menu_deselect_all"))
@@ -2158,7 +2612,8 @@
 #endif
 	else if (!strcmp(optname, "pettype")) 
 		Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat" :
-				(preferred_pet == 'd') ? "dog" : "random" );
+				(preferred_pet == 'd') ? "dog" :
+				(preferred_pet == 'n') ? "none" : "random");
 	else if (!strcmp(optname, "pickup_burden"))
 		Sprintf(buf, "%s", burdentype[flags.pickup_burden] );
 	else if (!strcmp(optname, "pickup_types")) {
@@ -2172,7 +2627,13 @@
 	else if (!strcmp(optname, "scores")) {
 		Sprintf(buf, "%d top/%d around%s", flags.end_top,
 				flags.end_around, flags.end_own ? "/own" : "");
-	     }
+	}
+	else if (!strcmp(optname, "scroll_margin")) {
+		if (iflags.wc_scroll_margin) Sprintf(buf, "%d",iflags.wc_scroll_margin);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "player_selection"))
+		Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
 #ifdef MSDOS
 	else if (!strcmp(optname, "soundcard"))
 		Sprintf(buf, "%s", to_be_done);
@@ -2185,8 +2646,23 @@
 			FEATURE_NOTICE_VER_MAJ,
 			FEATURE_NOTICE_VER_MIN,
 			FEATURE_NOTICE_VER_PATCH);
-	} else if (!strcmp(optname, "traps"))
+	}
+	else if (!strcmp(optname, "tile_file"))
+		Sprintf(buf, "%s", iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
+	else if (!strcmp(optname, "tile_height")) {
+		if (iflags.wc_tile_height) Sprintf(buf, "%d",iflags.wc_tile_height);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "tile_width")) {
+		if (iflags.wc_tile_width) Sprintf(buf, "%d",iflags.wc_tile_width);
+		else Strcpy(buf, defopt);
+	}
+	else if (!strcmp(optname, "traps"))
 		Sprintf(buf, "%s", to_be_done);
+	else if (!strcmp(optname, "vary_msgcount")) {
+		if (iflags.wc_vary_msgcount) Sprintf(buf, "%d",iflags.wc_vary_msgcount);
+		else Strcpy(buf, defopt);
+	}
 #ifdef MSDOS
 	else if (!strcmp(optname, "video"))
 		Sprintf(buf, "%s", to_be_done);
@@ -2204,12 +2680,18 @@
 			ttycolors[CLR_BRIGHT_MAGENTA],
 			ttycolors[CLR_BRIGHT_CYAN]);
 #endif /* VIDEOSHADES */
-#if 0
-	else if (!strcmp(optname, "warnlevel"))
-		Sprintf(buf, "%d", flags.warnlevel);
-#endif
 	else if (!strcmp(optname, "windowtype"))
 		Sprintf(buf, "%s", windowprocs.name);
+	else if (!strcmp(optname, "windowcolors"))
+		Sprintf(buf, "%s/%s %s/%s %s/%s %s/%s",
+			iflags.wc_foregrnd_menu    ? iflags.wc_foregrnd_menu : defbrief,
+			iflags.wc_backgrnd_menu    ? iflags.wc_backgrnd_menu : defbrief,
+			iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message : defbrief,
+			iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message : defbrief,
+			iflags.wc_foregrnd_status  ? iflags.wc_foregrnd_status : defbrief,
+			iflags.wc_backgrnd_status  ? iflags.wc_backgrnd_status : defbrief,
+			iflags.wc_foregrnd_text    ? iflags.wc_foregrnd_text : defbrief,
+			iflags.wc_backgrnd_text    ? iflags.wc_backgrnd_text : defbrief);
 #ifdef PREFIXES_IN_USE
 	else {
 	    for (i = 0; i < PREFIX_COUNT; ++i)
@@ -2288,6 +2765,7 @@
 	if (boolopt[i].addr) {
 #ifdef WIZARD
 	    if (boolopt[i].addr == &iflags.sanity_check && !wizard) continue;
+	    if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard) continue;
 #endif
 	    next_opt(datawin, boolopt[i].name);
 	}
@@ -2531,6 +3009,242 @@
     return ret;
 }
 
+struct wc_Opt wc_options[] = {
+	{"ascii_map", WC_ASCII_MAP},
+	{"color", WC_COLOR},
+	{"eight_bit_tty", WC_EIGHT_BIT_IN},
+	{"hilite_pet", WC_HILITE_PET},
+	{"large_font", WC_LARGE_FONT},	/* now obsolete */
+	{"popup_dialog", WC_POPUP_DIALOG},
+	{"preload_tiles", WC_PRELOAD_TILES},
+	{"tiled_map", WC_TILED_MAP},
+	{"tile_file", WC_TILE_FILE},
+	{"tile_width", WC_TILE_WIDTH},
+	{"tile_height", WC_TILE_HEIGHT},
+	{"use_inverse", WC_INVERSE},
+	{"align_message", WC_ALIGN_MESSAGE},
+	{"align_status", WC_ALIGN_STATUS},
+	{"font_map", WC_FONT_MAP},
+	{"font_menu", WC_FONT_MENU},
+	{"font_message",WC_FONT_MESSAGE},
+#if 0
+	{"perm_invent",WC_PERM_INVENT},
+#endif
+	{"font_size_map", WC_FONTSIZ_MAP},
+	{"font_size_menu", WC_FONTSIZ_MENU},
+	{"font_size_message", WC_FONTSIZ_MESSAGE},
+	{"font_size_status", WC_FONTSIZ_STATUS},
+	{"font_size_text", WC_FONTSIZ_TEXT},
+	{"font_status", WC_FONT_STATUS},
+	{"font_text", WC_FONT_TEXT},
+	{"map_mode", WC_MAP_MODE},
+	{"scroll_margin", WC_SCROLL_MARGIN},
+	{"vary_msgcount",WC_VARY_MSGCOUNT},
+	{(char *)0, 0L}
+};
+
+
+/*
+ * If a port wants to change or ensure that the
+ * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
+ * correct (for controlling its display in the option menu) call
+ * set_option_mod_status()
+ * with the second argument of 0,2, or 3 respectively.
+ */
+void
+set_option_mod_status(optnam, status)
+char *optnam;
+int status;
+{
+	int k;
+	if (status < SET_IN_FILE || status > SET_IN_GAME) {
+		impossible("set_option_mod_status: status out of range %d.", status);
+		return;
+	}
+	for (k = 0; boolopt[k].name; k++) {
+		if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
+			boolopt[k].optflags = status;
+			return;
+		}
+	}
+	for (k = 0; compopt[k].name; k++) {
+		if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
+			compopt[k].optflags = status;
+			return;
+		}
+	}
+}
+
+/*
+ * You can set several wc_options in one call to
+ * set_wc_option_mod_status() by setting
+ * the appropriate bits for each option that you
+ * are setting in the optmask argument
+ * prior to calling.
+ *    example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN, SET_IN_GAME);
+ */
+void
+set_wc_option_mod_status(optmask, status)
+unsigned long optmask;
+int status;
+{
+	int k = 0;
+	if (status < SET_IN_FILE || status > SET_IN_GAME) {
+		impossible("set_option_mod_status: status out of range %d.", status);
+		return;
+	}
+	while (wc_options[k].wc_name) {
+		if (optmask & wc_options[k].wc_bit) {
+			set_option_mod_status(wc_options[k].wc_name, status);
+		}
+		k++;
+	}
+}
+
+STATIC_OVL boolean
+is_wc_option(optnam)
+const char *optnam;
+{
+	int k = 0;
+	while (wc_options[k].wc_name) {
+		if (strcmp(wc_options[k].wc_name, optnam) == 0)
+			return TRUE;
+		k++;
+	}
+	return FALSE;
+}
+
+STATIC_OVL boolean
+wc_supported(optnam)
+const char *optnam;
+{
+	int k = 0;
+	while (wc_options[k].wc_name) {
+		if (!strcmp(wc_options[k].wc_name, optnam) &&
+		    (windowprocs.wincap & wc_options[k].wc_bit))
+			return TRUE;
+		k++;
+	}
+	return FALSE;
+}
+
+STATIC_OVL void
+wc_set_font_name(wtype, fontname)
+int wtype;
+char *fontname;
+{
+	char **fn = (char **)0;
+	if (!fontname) return;
+	switch(wtype) {
+	    case NHW_MAP:
+	    		fn = &iflags.wc_font_map;
+			break;
+	    case NHW_MESSAGE:
+	    		fn = &iflags.wc_font_message;
+			break;
+	    case NHW_TEXT:
+	    		fn = &iflags.wc_font_text;
+			break;
+	    case NHW_MENU:
+	    		fn = &iflags.wc_font_menu;
+			break;
+	    case NHW_STATUS:
+	    		fn = &iflags.wc_font_status;
+			break;
+	    default:
+	    		return;
+	}
+	if (fn) {
+		if (*fn) free(*fn);
+		*fn = (char *)alloc(strlen(fontname) + 1);
+		Strcpy(*fn, fontname);
+	}
+	return;
+}
+
+STATIC_OVL int
+wc_set_window_colors(op)
+char *op;
+{
+	/* syntax:
+	 *  menu white/black message green/yellow status white/blue text white/black
+	 */
+
+	int j;
+	char buf[BUFSZ];
+	char *wn, *tfg, *tbg, *newop;
+	static char *wnames[] = {"menu", "message", "status", "text"};
+	static char *shortnames[] = {"mnu", "msg", "sts", "txt"};
+	static char **fgp[] = {
+		&iflags.wc_foregrnd_menu,
+		&iflags.wc_foregrnd_message,
+		&iflags.wc_foregrnd_status,
+		&iflags.wc_foregrnd_text
+	};
+	static char **bgp[] = {
+		&iflags.wc_backgrnd_menu,
+		&iflags.wc_backgrnd_message,
+		&iflags.wc_backgrnd_status,
+		&iflags.wc_backgrnd_text
+	};
+
+	Strcpy(buf, op);
+	newop = mungspaces(buf);
+	while (newop && *newop) {
+
+		wn = tfg = tbg = (char *)0;
+
+		/* until first non-space in case there's leading spaces - before colorname*/
+		while(*newop && isspace(*newop)) newop++;
+		if (*newop) wn = newop;
+		else return 0;
+
+		/* until first space - colorname*/
+		while(*newop && !isspace(*newop)) newop++;
+		if (*newop) *newop = '\0';
+		else return 0;
+		newop++;
+
+		/* until first non-space - before foreground*/
+		while(*newop && isspace(*newop)) newop++;
+		if (*newop) tfg = newop;
+		else return 0;
+
+		/* until slash - foreground */
+		while(*newop && *newop != '/') newop++;
+		if (*newop) *newop = '\0';
+		else return 0;
+		newop++;
+
+		/* until first non-space (in case there's leading space after slash) - before background */
+		while(*newop && isspace(*newop)) newop++;
+		if (*newop) tbg = newop;
+		else return 0;
+
+		/* until first space - background */
+		while(*newop && !isspace(*newop)) newop++;
+		if (*newop) *newop++ = '\0';
+
+		for (j = 0; j < 4; ++j) {
+			if (!strcmpi(wn, wnames[j]) ||
+			    !strcmpi(wn, shortnames[j])) {
+				if (tfg && !strstri(tfg, " ")) {
+					if (*fgp[j]) free(*fgp[j]);
+					*fgp[j] = (char *)alloc(strlen(tfg) + 1);
+					Strcpy(*fgp[j], tfg);
+				}
+				if (tbg && !strstri(tbg, " ")) {
+					if (*bgp[j]) free(*bgp[j]);
+					*bgp[j] = (char *)alloc(strlen(tbg) + 1);
+					Strcpy(*bgp[j], tbg);
+				}
+ 				break;
+			}
+		}
+	}
+	return 1;
+}
+
 #endif	/* OPTION_LISTS_ONLY */
 
 /*options.c*/
diff -Naurd ../nethack-3.3.1/src/pager.c ./src/pager.c
--- ../nethack-3.3.1/src/pager.c Sat Jul 22 01:59:17 2000
+++ ./src/pager.c Fri Mar 22 14:40:55 2002
@@ -98,18 +98,23 @@
 	bhitpos.x = x;
 	bhitpos.y = y;
 	mtmp = m_at(x,y);
-	if(mtmp != (struct monst *) 0) {
-	    register boolean hp = (mtmp->data == &mons[PM_HIGH_PRIEST]);
+	if (mtmp != (struct monst *) 0) {
+	    char *name, monnambuf[BUFSZ];
+	    boolean accurate = !Hallucination;
+
+	    if (mtmp->data == &mons[PM_COYOTE] && accurate)
+		name = coyotename(mtmp, monnambuf);
+	    else
+		name = distant_monnam(mtmp, ARTICLE_NONE, monnambuf);
 
 	    pm = mtmp->data;
 	    Sprintf(buf, "%s%s%s",
 		    (mtmp->mx != x || mtmp->my != y) ?
-			((mtmp->isshk && !Hallucination)
+			((mtmp->isshk && accurate)
 				? "tail of " : "tail of a ") : "",
-		    (!hp && mtmp->mtame && !Hallucination) ? "tame " :
-		    (!hp && mtmp->mpeaceful && !Hallucination) ?
-		                                          "peaceful " : "",
-		    (hp ? "high priest" : x_monnam(mtmp, ARTICLE_NONE, (char *)0, 0, TRUE)));
+		    (mtmp->mtame && accurate) ? "tame " :
+		    (mtmp->mpeaceful && accurate) ? "peaceful " : "",
+		    name);
 	    if (u.ustuck == mtmp)
 		Strcat(buf, (Upolyd && sticks(youmonst.data)) ?
 			", being held" : ", holding you");
@@ -176,8 +181,13 @@
 		    }
 		    if (MATCH_WARN_OF_MON(mtmp)) {
 		    	char wbuf[BUFSZ];
-		    	Sprintf(wbuf, "warned of %s", makeplural(mtmp->data->mname));
-		    	Strcat(monbuf, wbuf);
+			if (Hallucination)
+				Strcat(monbuf, "paranoid delusion");
+			else {
+				Sprintf(wbuf, "warned of %s",
+					makeplural(mtmp->data->mname));
+		    		Strcat(monbuf, wbuf);
+		    	}
 		    	if (ways_seen-- > 1) Strcat(monbuf, ", ");
 		    }
 		}
@@ -485,6 +495,8 @@
 		sym = showsyms[trap_to_defsym(glyph_to_trap(glyph))];
 	    } else if (glyph_is_object(glyph)) {
 		sym = oc_syms[(int)objects[glyph_to_obj(glyph)].oc_class];
+		if (sym == '`' && iflags.bouldersym && (int)glyph_to_obj(glyph) == BOULDER)
+			sym = iflags.bouldersym;
 	    } else if (glyph_is_monster(glyph)) {
 		/* takes care of pets, detected, ridden, and regular mons */
 		sym = monsyms[(int)mons[glyph_to_mon(glyph)].mlet];
@@ -606,7 +618,7 @@
 	}
 
 	/* Now check for warning symbols */
-	for (i = 0; i < WARNCOUNT; i++) {
+	for (i = 1; i < WARNCOUNT; i++) {
 	    x_str = def_warnsyms[i].explanation;
 	    if (sym == (from_screen ? warnsyms[i] : def_warnsyms[i].sym)) {
 		if (!found) {
@@ -633,6 +645,10 @@
 	    }
 	}
 
+	/* handle optional boulder symbol as a special case */ 
+	if (iflags.bouldersym && sym == iflags.bouldersym)
+		found += append_str(out_str, "boulder");
+	
 	/*
 	 * If we are looking at the screen, follow multiple possibilities or
 	 * an ambiguous explanation by something more detailed.
@@ -640,14 +656,12 @@
 	if (from_screen) {
 	    if (found > 1 || need_to_look) {
 		char monbuf[BUFSZ];
-		char temp_buf[BUFSZ], coybuf[QBUFSZ];
+		char temp_buf[BUFSZ];
 
 		pm = lookat(cc.x, cc.y, look_buf, monbuf);
 		firstmatch = look_buf;
 		if (*firstmatch) {
-		    Sprintf(temp_buf, " (%s)",
-				(pm == &mons[PM_COYOTE]) ?
-				coyotename(coybuf) : firstmatch);
+		    Sprintf(temp_buf, " (%s)", firstmatch);
 		    (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1);
 		    found = 1;	/* we have something to look up */
 		}
diff -Naurd ../nethack-3.3.1/src/pickup.c ./src/pickup.c
--- ../nethack-3.3.1/src/pickup.c Sat Jul 22 19:41:29 2000
+++ ./src/pickup.c Fri Mar 22 14:41:05 2002
@@ -133,7 +133,8 @@
 
 	oclasses[oclassct = 0] = '\0';
 	*one_at_a_time = *everything = m_seen = FALSE;
-	iletct = collect_obj_classes(ilets, objs, here, incl_gold,
+	iletct = collect_obj_classes(ilets, objs, here,
+				     incl_gold,
 				     (boolean FDECL((*),(OBJ_P))) 0);
 	if (iletct == 0) {
 		return FALSE;
@@ -284,6 +285,18 @@
     if (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) ||
 	(index(valid_menu_classes, obj->oclass) != (char *)0))
 	return TRUE;
+    else if (((index(valid_menu_classes,'U') != (char *)0) &&
+	(obj->oclass != GOLD_CLASS && obj->bknown && !obj->blessed && !obj->cursed)))
+	return TRUE;
+    else if (((index(valid_menu_classes,'B') != (char *)0) &&
+	(obj->oclass != GOLD_CLASS && obj->bknown && obj->blessed)))
+	return TRUE;
+    else if (((index(valid_menu_classes,'C') != (char *)0) &&
+	(obj->oclass != GOLD_CLASS && obj->bknown && obj->cursed)))
+	return TRUE;
+    else if (((index(valid_menu_classes,'X') != (char *)0) &&
+	(obj->oclass != GOLD_CLASS && !obj->bknown)))
+	return TRUE;
     else
 	return FALSE;
 }
@@ -314,7 +327,8 @@
 }
 
 /*
- * Have the hero pick things from the ground.
+ * Have the hero pick things from the ground
+ * or a monster's inventory if swallowed.
  *
  * Arg what:
  *	>0  autopickup
@@ -331,47 +345,57 @@
 	int i, n, res, count, n_tried = 0, n_picked = 0;
 	menu_item *pick_list = (menu_item *) 0;
 	boolean autopickup = what > 0;
+	struct obj *objchain;
+	int traverse_how;
 
 	if (what < 0)		/* pick N of something */
 	    count = -what;
 	else			/* pick anything */
 	    count = 0;
 
-	/* no auto-pick if no-pick move, nothing there, or in a pool */
-	if (autopickup && (flags.nopick || !OBJ_AT(u.ux, u.uy) ||
+	if (!u.uswallow) {
+		/* no auto-pick if no-pick move, nothing there, or in a pool */
+		if (autopickup && (flags.nopick || !OBJ_AT(u.ux, u.uy) ||
 			(is_pool(u.ux, u.uy) && !Underwater) || is_lava(u.ux, u.uy))) {
-	    read_engr_at(u.ux, u.uy);
-	    return (0);
-	}
+			read_engr_at(u.ux, u.uy);
+			return (0);
+		}
 
-	/* no pickup if levitating & not on air or water level */
-	if (!can_reach_floor()) {
-	    if ((multi && !flags.run) || (autopickup && !flags.pickup))
-		read_engr_at(u.ux, u.uy);
-	    return (0);
-	}
+		/* no pickup if levitating & not on air or water level */
+		if (!can_reach_floor()) {
+		    if ((multi && !flags.run) || (autopickup && !flags.pickup))
+			read_engr_at(u.ux, u.uy);
+		    return (0);
+		}
 
-	/* multi && !flags.run means they are in the middle of some other
-	 * action, or possibly paralyzed, sleeping, etc.... and they just
-	 * teleported onto the object.  They shouldn't pick it up.
-	 */
-	if ((multi && !flags.run) || (autopickup && !flags.pickup)) {
-	    check_here(FALSE);
-	    return (0);
-	}
+		/* multi && !flags.run means they are in the middle of some other
+		 * action, or possibly paralyzed, sleeping, etc.... and they just
+		 * teleported onto the object.  They shouldn't pick it up.
+		 */
+		if ((multi && !flags.run) || (autopickup && !flags.pickup)) {
+		    check_here(FALSE);
+		    return (0);
+		}
+		if (notake(youmonst.data)) {
+		    if (!autopickup)
+			You("are physically incapable of picking anything up.");
+		    else
+			check_here(FALSE);
+		    return (0);
+		}
 
-	if (notake(youmonst.data)) {
-	    if (!autopickup)
-		You("are physically incapable of picking anything up.");
-	    else
-		check_here(FALSE);
-	    return (0);
+		/* if there's anything here, stop running */
+		if (OBJ_AT(u.ux,u.uy) && flags.run && flags.run != 8 && !flags.nopick) nomul(0);
 	}
 
-	/* if there's anything here, stop running */
-	if (OBJ_AT(u.ux,u.uy) && flags.run && !flags.nopick) nomul(0);
-
 	add_valid_menu_class(0);	/* reset */
+	if (!u.uswallow) {
+		objchain = level.objects[u.ux][u.uy];
+		traverse_how = BY_NEXTHERE;
+	} else {
+		objchain = u.ustuck->minvent;
+		traverse_how = 0;	/* nobj */
+	}
 	/*
 	 * Start the actual pickup process.  This is split into two main
 	 * sections, the newer menu and the older "traditional" methods.
@@ -379,7 +403,7 @@
 	 * to make things less confusing.
 	 */
 	if (autopickup) {
-	    n = autopick(level.objects[u.ux][u.uy], BY_NEXTHERE, &pick_list);
+	    n = autopick(objchain, traverse_how, &pick_list);
 	    goto menu_pickup;
 	}
 
@@ -390,15 +414,15 @@
 		char buf[QBUFSZ];
 		Sprintf(buf, "Pick %d of what?", count);
 		val_for_n_or_more = count;	/* set up callback selector */
-		n = query_objlist(buf, level.objects[u.ux][u.uy],
-			    BY_NEXTHERE|AUTOSELECT_SINGLE|INVORDER_SORT,
+		n = query_objlist(buf, objchain,
+			    traverse_how|AUTOSELECT_SINGLE|INVORDER_SORT,
 			    &pick_list, PICK_ONE, n_or_more);
 		/* correct counts, if any given */
 		for (i = 0; i < n; i++)
 		    pick_list[i].count = count;
 	    } else {
-		n = query_objlist("Pick up what?", level.objects[u.ux][u.uy],
-			    BY_NEXTHERE|AUTOSELECT_SINGLE|INVORDER_SORT,
+		n = query_objlist("Pick up what?", objchain,
+			    traverse_how|AUTOSELECT_SINGLE|INVORDER_SORT,
 			    &pick_list, PICK_ANY, all_but_uchain);
 	    }
 menu_pickup:
@@ -424,12 +448,13 @@
 	    selective = FALSE;		/* ask for each item */
 
 	    /* check for more than one object */
-	    for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere)
+	    for (obj = objchain;
+		  obj; obj = (traverse_how == BY_NEXTHERE) ? obj->nexthere : obj->nobj)
 		ct++;
 
 	    if (ct == 1 && count) {
 		/* if only one thing, then pick it */
-		obj = level.objects[u.ux][u.uy];
+		obj = objchain;
 		lcount = min(obj->quan, (long)count);
 		n_tried++;
 		if (pickup_object(obj, lcount, FALSE) > 0)
@@ -442,20 +467,25 @@
 		There("are %s objects here.",
 		      (ct <= 10) ? "several" : "many");
 		if (!query_classes(oclasses, &selective, &all_of_a_type,
-				   "pick up", level.objects[u.ux][u.uy],
-				   TRUE, FALSE, &via_menu)) {
+				   "pick up", objchain,
+				   traverse_how == BY_NEXTHERE,
+				   FALSE,
+				   &via_menu)) {
 		    if (!via_menu) return (0);
 		    n = query_objlist("Pick up what?",
-				  level.objects[u.ux][u.uy],
-				  BY_NEXTHERE|(selective ? 0 : INVORDER_SORT),
+				  objchain,
+				  traverse_how|(selective ? 0 : INVORDER_SORT),
 				  &pick_list, PICK_ANY,
 				  via_menu == -2 ? allow_all : allow_category);
 		    goto menu_pickup;
 		}
 	    }
 
-	    for (obj = level.objects[u.ux][u.uy]; obj; obj = obj2) {
-		obj2 = obj->nexthere;	/* perhaps obj will be picked up */
+	    for (obj = objchain; obj; obj = obj2) {
+		if (traverse_how == BY_NEXTHERE)
+			obj2 = obj->nexthere;	/* perhaps obj will be picked up */
+		else
+			obj2 = obj->nobj;
 		lcount = -1L;
 
 		if (!selective && oclasses[0] && !index(oclasses,obj->oclass))
@@ -494,11 +524,15 @@
 	    ;	/* semicolon needed by brain-damaged compilers */
 	}
 
-	/* position may need updating (invisible hero) */
-	if (n_picked) newsym(u.ux,u.uy);
+	if (!u.uswallow) {
+		if (!OBJ_AT(u.ux,u.uy)) u.uundetected = 0;
 
-	/* see whether there's anything else here, after auto-pickup is done */
-	if (autopickup) check_here(n_picked > 0);
+		/* position may need updating (invisible hero) */
+		if (n_picked) newsym(u.ux,u.uy);
+
+		/* see whether there's anything else here, after auto-pickup is done */
+		if (autopickup) check_here(n_picked > 0);
+	}
 	return (n_tried > 0);
 }
 
@@ -662,14 +696,33 @@
 	char invlet;
 	int ccount;
 	boolean do_unpaid = FALSE;
+	boolean do_blessed = FALSE, do_cursed = FALSE, do_uncursed = FALSE,
+	    do_buc_unknown = FALSE;
+	int num_buc_types = 0;
 
 	*pick_list = (menu_item *) 0;
 	if (!olist) return 0;
 	if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) do_unpaid = TRUE;
+	if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED)) {
+	    do_blessed = TRUE;
+	    num_buc_types++;
+	}
+	if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED)) {
+	    do_cursed = TRUE;
+	    num_buc_types++;
+	}
+	if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED)) {
+	    do_uncursed = TRUE;
+	    num_buc_types++;
+	}
+	if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN)) {
+	    do_buc_unknown = TRUE;
+	    num_buc_types++;
+	}
 
 	ccount = count_categories(olist, qflags);
 	/* no point in actually showing a menu for a single category */
-	if (ccount == 1 && !do_unpaid && !(qflags & BILLED_TYPES)) {
+	if (ccount == 1 && !do_unpaid && num_buc_types <= 1 && !(qflags & BILLED_TYPES)) {
 	    for (curr = olist; curr; curr = FOLLOW(curr, qflags)) {
 		if ((qflags & WORN_TYPES) &&
 		    !(curr->owornmask & (W_ARMOR|W_RING|W_AMUL|W_TOOL|W_WEP|W_SWAPWEP|W_QUIVER)))
@@ -751,6 +804,36 @@
 			"Auto-select every item being worn" :
 			"Auto-select every item", MENU_UNSELECTED);
 	}
+	/* items with b/u/c/unknown if there are any */
+	if (do_blessed) {
+		invlet = 'B';
+		any.a_void = 0;
+		any.a_int = 'B';
+		add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
+			"Items known to be Blessed", MENU_UNSELECTED);
+	}
+	if (do_cursed) {
+		invlet = 'C';
+		any.a_void = 0;
+		any.a_int = 'C';
+		add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
+			"Items known to be Cursed", MENU_UNSELECTED);
+	}
+	if (do_uncursed) {
+		invlet = 'U';
+		any.a_void = 0;
+		any.a_int = 'U';
+		add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
+			"Items known to be Uncursed", MENU_UNSELECTED);
+	}
+	if (do_buc_unknown) {
+		invlet = 'X';
+		any.a_void = 0;
+		any.a_int = 'X';
+		add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE,
+			"Items of unknown B/C/U status",
+			MENU_UNSELECTED);
+	}
 	end_menu(win, qstr);
 	n = select_menu(win, how, pick_list);
 	destroy_nhwindow(win);
@@ -807,7 +890,9 @@
 
     savequan = obj->quan;
     saveowt = obj->owt;
+
     iw = max_capacity();
+
     if (count != savequan) {
 	obj->quan = count;
 	obj->owt = (unsigned)weight(obj);
@@ -824,6 +909,7 @@
     }
     *wt_before = iw;
     *wt_after  = wt;
+
     if (wt < 0)
 	return count;
 
@@ -912,7 +998,7 @@
 	suffx  = "";
     }
     There("%s %s %s, but %s%s%s%s.",
-	  (obj->quan == 1L) ? "is" : "are", obj_nambuf, where,
+	  otense(obj, "are"), obj_nambuf, where,
 	  prefx1, prefx2, verb, suffx);
 
  /* *wt_after = iw; */
@@ -921,14 +1007,14 @@
 
 /* determine whether character is able and player is willing to carry `obj' */
 STATIC_OVL
-int lift_object(obj, container, cnt_p, telekinesis)
+int 
+lift_object(obj, container, cnt_p, telekinesis)
 struct obj *obj, *container;	/* object to pick up, bag it's coming out of */
 long *cnt_p;
 boolean telekinesis;
 {
     int result, old_wt, new_wt, prev_encumbr, next_encumbr;
 
-
     if (obj->otyp == BOULDER && In_sokoban(&u.uz)) {
 	You("cannot get your %s around this %s.",
 			body_part(HAND), xname(obj));
@@ -1019,7 +1105,7 @@
 	    if (gold_capacity <= 0L) {
 		pline(
 	       "There %s %ld gold piece%s %s, but you cannot carry any more.",
-		      (obj->quan == 1L) ? "is" : "are",
+		      otense(obj, "are"),
 		      obj->quan, plur(obj->quan), where);
 		return 0;
 	    } else if (gold_capacity < count) {
@@ -1050,16 +1136,15 @@
 	    if (flags.run) nomul(0);
 	    return 1;
 	} else if (obj->otyp == CORPSE) {
-		if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg
+	    if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg
 				&& !Stone_resistance && !telekinesis) {
 		if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
 		    display_nhwindow(WIN_MESSAGE, FALSE);
 		else {
 			char kbuf[BUFSZ];
 
-			pline("Touching %s corpse is a fatal mistake.",
-					an(mons[obj->corpsenm].mname));
-			Sprintf(kbuf, "%s corpse", an(mons[obj->corpsenm].mname));
+			Strcpy(kbuf, an(corpse_xname(obj, TRUE)));
+			pline("Touching %s is a fatal mistake.", kbuf);
 			instapetrify(kbuf);
 		    return -1;
 		}
@@ -1074,8 +1159,8 @@
 	    if (obj->blessed) obj->blessed = 0;
 	    else if (!obj->spe && !obj->cursed) obj->spe = 1;
 	    else {
-		pline_The("scroll%s turn%s to dust as you %s %s up.",
-			plur(obj->quan), (obj->quan == 1L) ? "s" : "",
+		pline_The("scroll%s %s to dust as you %s %s up.",
+			plur(obj->quan), otense(obj, "turn"),
 			telekinesis ? "raise" : "pick",
 			(obj->quan == 1L) ? "it" : "them");
 		if (!(objects[SCR_SCARE_MONSTER].oc_name_known) &&
@@ -1091,13 +1176,14 @@
 	    return res;
 
 	if (obj->quan != count && obj->otyp != LOADSTONE)
-	    (void) splitobj(obj, count);
+	    obj = splitobj(obj, count);
 
 	obj = pick_obj(obj);
 
 	if (uwep && uwep == obj) mrg_to_wielded = TRUE;
 	nearload = near_capacity();
-	prinv(nearload == SLT_ENCUMBER ? moderateloadmsg : (char *) 0, obj, count);
+	prinv(nearload == SLT_ENCUMBER ? moderateloadmsg : (char *) 0,
+	      obj, count);
 	mrg_to_wielded = FALSE;
 	return 1;
 }
@@ -1108,19 +1194,35 @@
  * pointer to the object where otmp ends up.  This may be different
  * from otmp because of merging.
  *
- * Gold never reaches this routine.
+ * Gold never reaches this routine unless GOLDOBJ is defined.
  */
 struct obj *
 pick_obj(otmp)
-register struct obj *otmp;
+struct obj *otmp;
 {
 	obj_extract_self(otmp);
-	if (*u.ushops && costly_spot(u.ux, u.uy) &&
-	    otmp != uball)     /* don't charge for this - kd, 1/17/90 */
-	   /* sets obj->unpaid if necessary */
+	if (otmp->no_charge) {
+	    /* this attribute only applies to objects outside invent */
+	    otmp->no_charge = 0;
+	} else if (otmp != uball && costly_spot(otmp->ox, otmp->oy)) {
+	    char saveushops[5], fakeshop[2];
+
+	    /* addtobill cares about your location rather than the object's;
+	       usually they'll be the same, but not when using telekinesis
+	       (if ever implemented) or a grappling hook */
+	    Strcpy(saveushops, u.ushops);
+	    fakeshop[0] = *in_rooms(otmp->ox, otmp->oy, SHOPBASE);
+	    fakeshop[1] = '\0';
+	    Strcpy(u.ushops, fakeshop);
+	    /* sets obj->unpaid if necessary */
 	    addtobill(otmp, TRUE, FALSE, FALSE);
-	if(Invisible) newsym(u.ux,u.uy);
-	return(addinv(otmp));    /* might merge it with other objects */
+	    Strcpy(u.ushops, saveushops);
+	    /* if you're outside the shop, make shk notice */
+	    if (!index(u.ushops, *fakeshop))
+		remote_burglary(otmp->ox, otmp->oy);
+	}
+	if (Invisible) newsym(otmp->ox, otmp->oy);
+	return addinv(otmp);	/* might merge it with other objects */
 }
 
 /*
@@ -1139,7 +1241,8 @@
 		break;
 	case 2: You("rebalance your load.  Movement is difficult.");
 		break;
-	case 3: You("stagger under your heavy load.  Movement is very hard.");
+	case 3: You("%s under your heavy load.  Movement is very hard.",
+		    stagger(youmonst.data, "stagger"));
 		break;
 	default: You("%s move a handspan with this load!",
 		     newcap == 4 ? "can barely" : "can't even");
@@ -1154,7 +1257,8 @@
 		break;
 	case 2: You("rebalance your load.  Movement is still difficult.");
 		break;
-	case 3: You("stagger under your load.  Movement is still very hard.");
+	case 3: You("%s under your load.  Movement is still very hard.",
+		    stagger(youmonst.data, "stagger"));
 		break;
 	}
 	flags.botl = 1;
@@ -1234,16 +1338,17 @@
     const char *dont_find_anything = "don't find anything";
     struct monst *mtmp;
     char qbuf[QBUFSZ];
-#ifdef STEED
-    struct obj *otmp;
-    boolean saddled_there = FALSE;
-    boolean got_saddle = FALSE;
-#endif
+    int prev_inquiry = 0;
+    boolean prev_loot = FALSE;
 
     if (check_capacity((char *)0)) {
 	/* "Can't do that while carrying so much stuff." */
 	return 0;
     }
+    if (nohands(youmonst.data)) {
+	You("have no hands!");	/* not `body_part(HAND)' */
+	return 0;
+    }
     x = u.ux; y = u.uy;
 
 lootcont:
@@ -1264,11 +1369,12 @@
 		    continue;
 		}
 		if (cobj->otyp == BAG_OF_TRICKS) {
+		    int tmp;
 		    You("carefully open the bag...");
 		    pline("It develops a huge set of teeth and bites you!");
-		    c = rnd(10);
-		    if (Half_physical_damage) c = (c+1) / 2;
-		    losehp(c, "carnivorous bag", KILLED_BY_AN);
+		    tmp = rnd(10);
+		    if (Half_physical_damage) tmp = (tmp+1) / 2;
+		    losehp(tmp, "carnivorous bag", KILLED_BY_AN);
 		    makeknown(BAG_OF_TRICKS);
 		    timepassed = 1;
 		    continue;
@@ -1292,18 +1398,10 @@
 			if (coffers->otyp == CHEST && coffers->spe == pass)
 			    goto gotit;	/* two level break */
 gotit:
-		if (coffers){
-		    struct obj *tmp;
+		if (coffers) {
 	    verbalize("Thank you for your contribution to reduce the debt.");
-		    for (tmp = coffers->cobj; tmp; tmp = tmp->nobj)
-			if (tmp->otyp == goldob->otyp) break;
-
-		    if (tmp) {
-			tmp->quan += goldob->quan;
-			delobj(goldob);
-		    } else {
-			add_to_container(coffers, goldob);
-		    }
+		    (void) add_to_container(coffers, goldob);
+		    coffers->owt = weight(coffers);
 		} else {
 		    struct monst *mon = makemon(courtmon(),
 					    u.ux, u.uy, NO_MM_FLAGS);
@@ -1346,14 +1444,57 @@
 	    return timepassed;
 	}
 	mtmp = m_at(x, y);
+	if (mtmp) timepassed = loot_mon(mtmp, &prev_inquiry, &prev_loot);
+
+	/* Preserve pre-3.3.1 behaviour for containers.
+	 * Adjust this if-block to allow container looting
+	 * from one square away to change that in the future.
+	 */
+	if (!underfoot) {
+	    if (container_at(x, y, FALSE)) {
+		if (mtmp) {
+		    You_cant("loot anything %sthere with %s in the way.",
+			    prev_inquiry ? "else " : "", mon_nam(mtmp));
+		    return timepassed;
+		} else {
+		    You("have to be at a container to loot it.");
+		}
+	    } else {
+		You("%s %sthere to loot.", dont_find_anything,
+			(prev_inquiry || prev_loot) ? "else " : "");
+		return timepassed;
+	    }
+	}
+    } else if (c != 'y' && c != 'n') {
+	You("%s %s to loot.", dont_find_anything,
+		    underfoot ? "here" : "there");
+    }
+    return (timepassed);
+}
+
+/* loot_mon() returns amount of time passed.
+ */
+int
+loot_mon(mtmp, passed_info, prev_loot)
+struct monst *mtmp;
+int *passed_info;
+boolean *prev_loot;
+{
+    int c = -1;
+    int timepassed = 0;
 #ifdef STEED
-	/* 3.3.1 introduced the ability to remove saddle from a steed */
-	if (mtmp && mtmp != u.usteed && (otmp = which_armor(mtmp, W_SADDLE))) {
-	    long unwornmask;
-	    saddled_there = TRUE;
-	    Sprintf(qbuf, "Do you want to remove the saddle from %s?",
+    struct obj *otmp;
+    char qbuf[QBUFSZ];
+
+    /* 3.3.1 introduced the ability to remove saddle from a steed             */
+    /* 	*passed_info is set to TRUE if a loot query was given.               */
+    /*	*prev_loot is set to TRUE if something was actually acquired in here. */
+    if (mtmp && mtmp != u.usteed && (otmp = which_armor(mtmp, W_SADDLE))) {
+	long unwornmask;
+	if (passed_info) *passed_info = 1;
+	Sprintf(qbuf, "Do you want to remove the saddle from %s?",
 		x_monnam(mtmp, ARTICLE_THE, (char *)0, SUPPRESS_SADDLE, FALSE));
-	    if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
+	if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
 		if (nolimbs(youmonst.data)) {
 		    You_cant("do that without limbs."); /* not body_part(HAND) */
 		    return (0);
@@ -1375,69 +1516,18 @@
 		otmp = hold_another_object(otmp, "You drop %s!", doname(otmp),
 					(const char *)0);
 		timepassed = rnd(3);
-		got_saddle = TRUE;
-	    } else if (c == 'q') {
+		if (prev_loot) *prev_loot = TRUE;
+	} else if (c == 'q') {
 		return (0);
-	    }
-	}
-# if 0
-	/* Loot your steed, even if you can't reach the floor */
-	if (u.usteed) {
-	    Sprintf(qbuf, "Do you want to loot %s inventory?",
-			s_suffix(x_monnam(u.usteed, ARTICLE_YOUR,
-				(char *)0, SUPPRESS_SADDLE, FALSE)));
-	    switch (c = ynq(qbuf)) {
-		case 'y':
-		    if (!u.usteed->minvent) {
-			impossible("no saddle?");
-			break;
-		    }
-		    /* TO DO: get and put things into the inventory */
-		    You("peek at %s inventory...",
-			s_suffix(x_monnam(u.usteed, ARTICLE_YOUR,
-				(char *)0, SUPPRESS_SADDLE, FALSE)));
-		    (void) display_minventory(u.usteed, MINV_ALL);
-		    timepassed = 1;
-		    break;
-		case 'n':
-		    break;
-		case 'q':
-		    return (0);
-	    }
 	}
-# endif
+    }
 #endif	/* STEED */
-
-	/* Preserve pre-3.3.1 behaviour for containers.
-	 * Adjust this if-block to allow container looting
-	 * from one square away to change that in the future.
-	 */
-	if (!underfoot) {
-	    if (container_at(x, y, FALSE)) {
-		if (mtmp) {
-		    You("can't loot anything %sthere with %s in the way.",
-#ifdef STEED
-			    saddled_there ? "else " :
-#endif
-			    "", mon_nam(mtmp));
-		    return timepassed;
-		} else {
-		    You("have to be at a container to loot it.");
-		}
-	    } else {
-		You("%s %sthere to loot.", dont_find_anything,
-#ifdef STEED
-			(saddled_there || got_saddle) ? "else " :
-#endif
-			"");
-		return timepassed;
-	    }
-	}
-    } else if (c != 'y' && c != 'n') {
-	You("%s %s to loot.", dont_find_anything,
-		    underfoot ? "here" : "there");
+    /* 3.4.0 introduced the ability to pick things up from within swallower's stomach */
+    if (u.uswallow) {
+	int count = passed_info ? *passed_info : 0;
+	timepassed = pickup(count);
     }
-    return (timepassed);
+    return timepassed;
 }
 
 /*
@@ -1477,7 +1567,6 @@
 in_container(obj)
 register struct obj *obj;
 {
-	register struct obj *gold;
 	boolean is_gold = (obj->oclass == GOLD_CLASS);
 	boolean floor_container = !carried(current_container);
 	char buf[BUFSZ];
@@ -1510,7 +1599,7 @@
 	    pline("%s cannot be confined in such trappings.", The(xname(obj)));
 	    return 0;
 	} else if (obj->otyp == LEASH && obj->leashmon != 0) {
-		pline("%s is attached to your pet.", The(xname(obj)));
+		pline("%s attached to your pet.", Tobjnam(obj, "are"));
 		return 0;
 	} else if (obj == uwep) {
 		if (welded(obj)) {
@@ -1527,6 +1616,22 @@
 		if (uquiver) return 0;     /* unwielded, died, rewielded */
 	}
 
+	if (obj->otyp == CORPSE) {
+	    if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg
+		 && !Stone_resistance) {
+		if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
+		    display_nhwindow(WIN_MESSAGE, FALSE);
+		else {
+		    char kbuf[BUFSZ];
+
+		    Strcpy(kbuf, an(corpse_xname(obj, TRUE)));
+		    pline("Touching %s is a fatal mistake.", kbuf);
+		    instapetrify(kbuf);
+		    return -1;
+		}
+	    }
+	}
+
 	/* boxes, boulders, and big statues can't fit into any container */
 	if (obj->otyp == ICE_BOX || Is_box(obj) || obj->otyp == BOULDER ||
 		(obj->otyp == STATUE && bigmonst(&mons[obj->corpsenm]))) {
@@ -1544,39 +1649,24 @@
 
 	freeinv(obj);
 
-	if (is_gold) {	/* look for other money to merge within the container */
-		for (gold = current_container->cobj; gold; gold = gold->nobj)
-			if (gold->otyp == obj->otyp) break;
-	} else
-		gold = 0;
-
-	if (gold) {
-		gold->quan += obj->quan;
-	} else {
-		add_to_container(current_container, obj);
-	}
-
-	current_container->owt = weight(current_container);
-
-	Strcpy(buf, the(xname(current_container)));
-	You("put %s into %s.", doname(obj), buf);
-
 	if (obj_is_burning(obj))	/* this used to be part of freeinv() */
 		(void) snuff_lit(obj);
 
 	if (floor_container && costly_spot(u.ux, u.uy)) {
-		sellobj_state(TRUE);
+		sellobj_state(SELL_DELIBERATE);
 		sellobj(obj, u.ux, u.uy);
-		sellobj_state(FALSE);
+		sellobj_state(SELL_NORMAL);
 	}
 	if (Icebox && obj->otyp != OIL_LAMP && obj->otyp != BRASS_LANTERN
 			&& !Is_candle(obj)) {
 		obj->age = monstermoves - obj->age; /* actual age */
 		/* stop any corpse timeouts when frozen */
 		if (obj->otyp == CORPSE && obj->timed) {
-			(void) stop_timer(ROT_CORPSE, (genericptr_t)obj);
+			long rot_alarm = stop_timer(ROT_CORPSE, (genericptr_t)obj);
 			(void) stop_timer(REVIVE_MON, (genericptr_t)obj);
-		}
+			/* mark a non-reviving corpse as such */
+			if (rot_alarm) obj->norevive = 1;
+  		}
 	}
 
 	else if (Is_mbag(current_container) && mbag_explodes(obj, 0)) {
@@ -1590,6 +1680,8 @@
 			(void)stolen_value(current_container, u.ux, u.uy,
 					   (boolean)shkp->mpeaceful, FALSE);
 		}
+		/* did not actually insert obj yet */
+		obfree(obj, (struct obj *)0);
 		delete_contents(current_container);
 		if (!floor_container)
 			useup(current_container);
@@ -1602,10 +1694,14 @@
 		current_container = 0;	/* baggone = TRUE; */
 	}
 
-	if (is_gold) {
-		if (gold) dealloc_obj(obj);
-		bot();	/* update character's gold piece count immediately */
+	if (current_container) {
+	    Strcpy(buf, the(xname(current_container)));
+	    You("put %s into %s.", doname(obj), buf);
+
+	    (void) add_to_container(current_container, obj);
+	    current_container->owt = weight(current_container);
 	}
+	if (is_gold) bot(); /* update gold piece count immediately */
 
 	return(current_container ? 1 : -1);
 }
@@ -1636,12 +1732,28 @@
 
 	if(obj->oartifact && !touch_artifact(obj,&youmonst)) return 0;
 
+	if (obj->otyp == CORPSE) {
+	    if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg
+		 && !Stone_resistance) {
+		if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
+		    display_nhwindow(WIN_MESSAGE, FALSE);
+		else {
+		    char kbuf[BUFSZ];
+
+		    Strcpy(kbuf, an(corpse_xname(obj, TRUE)));
+		    pline("Touching %s is a fatal mistake.", kbuf);
+		    instapetrify(kbuf);
+		    return -1;
+		}
+	    }
+	}
+
 	count = obj->quan;
 	if ((res = lift_object(obj, current_container, &count, FALSE)) <= 0)
 	    return res;
 
 	if (obj->quan != count && obj->otyp != LOADSTONE)
-	    (void) splitobj(obj, count);
+	    obj = splitobj(obj, count);
 
 	/* Remove the object from the list. */
 	obj_extract_self(obj);
@@ -1687,7 +1799,8 @@
 register struct obj *obj;
 register int held;
 {
-	struct obj *curr, *otmp, *u_gold = (struct obj *)0;
+	struct obj *curr, *otmp;
+	struct obj *u_gold = (struct obj *)0;
 	struct monst *shkp;
 	boolean one_by_one, allflag, loot_out = FALSE, loot_in = FALSE;
 	char select[MAXOCLASSES+1];
@@ -1697,7 +1810,7 @@
 	    menu_on_request;
 
 	if (obj->olocked) {
-	    pline("%s seems to be locked.", The(xname(obj)));
+	    pline("%s to be locked.", Tobjnam(obj, "seem"));
 	    if (held) You("must put it down to unlock.");
 	    return 0;
 	} else if (obj->otrapped) {
@@ -1738,7 +1851,8 @@
 				       obj->ox, obj->oy, sc);
 		if (ocat) {
 		    obj_extract_self(ocat);
-		    add_to_container(obj, ocat);  /* weight handled below */
+		    (void) add_to_container(obj, ocat);
+		    /* weight handled below */
 		}
 		pline_The("%s inside the box is dead!",
 		    Hallucination ? rndmonnam() : "housecat");
@@ -1777,13 +1891,13 @@
 	}
 
 	if (cnt && loss)
-	    You("owe %ld zorkmids for lost item%s.",
-		loss, lcnt > 1 ? "s" : "");
+	    You("owe %ld %s for lost item%s.",
+		loss, currency(loss), lcnt > 1 ? "s" : "");
 
 	obj->owt = weight(obj);
 
 	if (!cnt) {
-	    pline("%s is empty.", Yname2(obj));
+	    pline("%s %s empty.", Yname2(obj), otense(obj, "are"));
 	} else {
 	    Sprintf(qbuf, "Do you want to take %s out of %s?",
 		    something, yname(obj));
@@ -1812,7 +1926,9 @@
 		case 'y':
 		    if (query_classes(select, &one_by_one, &allflag,
 				      "take out", current_container->cobj,
-				      FALSE, FALSE, &menu_on_request)) {
+				      FALSE,
+				      FALSE,
+				      &menu_on_request)) {
 			if (askchain((struct obj **)&current_container->cobj,
 				     (one_by_one ? (char *)0 : select),
 				     allflag, out_container,
@@ -1865,7 +1981,8 @@
 		/* traditional code */
 		menu_on_request = 0;
 		if (query_classes(select, &one_by_one, &allflag, "put in",
-				   invent, FALSE, (u.ugold != 0L),
+				   invent, FALSE,
+				   (u.ugold != 0L),
 				   &menu_on_request)) {
 		    (void) askchain((struct obj **)&invent,
 				    (one_by_one ? (char *)0 : select), allflag,
@@ -1884,7 +2001,6 @@
 	    invent = u_gold->nobj;
 	    dealloc_obj(u_gold);
 	}
-
 	return used;
 }
 
@@ -1909,7 +2025,8 @@
     } else if (flags.menu_style == MENU_FULL) {
 	all_categories = FALSE;
 	Sprintf(buf,"%s what type of objects?", put_in ? putin : takeout);
-	mflags = put_in ? ALL_TYPES : ALL_TYPES|CHOOSE_ALL;
+	mflags = put_in ? ALL_TYPES | BUC_ALLBKNOWN | BUC_UNKNOWN :
+		          ALL_TYPES | CHOOSE_ALL | BUC_ALLBKNOWN | BUC_UNKNOWN;
 	n = query_category(buf, put_in ? invent : container->cobj,
 			   mflags, &pick_list, PICK_ANY);
 	if (!n) return 0;
@@ -1943,11 +2060,8 @@
 		    otmp = pick_list[i].item.a_obj;
 		    count = pick_list[i].count;
 		    if (count > 0 && count < otmp->quan) {
-			otmp2 = splitobj(otmp, count);
+			otmp = splitobj(otmp, count);
 			/* special split case also handled by askchain() */
-			if (otmp == uwep) setuwep(otmp2);
-			if (otmp == uquiver) setuqwep(otmp2);
-			if (otmp == uswapwep) setuswapwep(otmp2);
 		    }
 		    res = put_in ? in_container(otmp) : out_container(otmp);
 		    if (res < 0)
@@ -1975,12 +2089,12 @@
     start_menu(win);
     any.a_int = 1;
     Sprintf(buf,"Take %s out of %s", something, the(xname(obj)));
-    add_menu(win, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf, MENU_UNSELECTED);
+    add_menu(win, NO_GLYPH, &any, 'o', 0, ATR_NONE, buf, MENU_UNSELECTED);
     any.a_int = 2;
     Sprintf(buf,"Put %s into %s", something, the(xname(obj)));
-    add_menu(win, NO_GLYPH, &any, 'b', 0, ATR_NONE, buf, MENU_UNSELECTED);
+    add_menu(win, NO_GLYPH, &any, 'i', 0, ATR_NONE, buf, MENU_UNSELECTED);
     any.a_int = 3;
-    add_menu(win, NO_GLYPH, &any, 'c', 0, ATR_NONE,
+    add_menu(win, NO_GLYPH, &any, 'b', 0, ATR_NONE,
 		"Both of the above", MENU_UNSELECTED);
     end_menu(win, prompt);
     n = select_menu(win, PICK_ONE, &pick_list);
diff -Naurd ../nethack-3.3.1/src/pline.c ./src/pline.c
--- ../nethack-3.3.1/src/pline.c Fri May 19 22:21:27 2000
+++ ./src/pline.c Fri Mar 22 14:40:55 2002
@@ -1,10 +1,13 @@
-/*	SCCS Id: @(#)pline.c	3.3	1999/11/28	*/
+/*	SCCS Id: @(#)pline.c	3.4	1999/11/28	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #define NEED_VARARGS /* Uses ... */	/* comment line for pre-compiled headers */
 #include "hack.h"
 #include "epri.h"
+#ifdef WIZARD
+#include "edog.h"
+#endif
 
 #ifdef OVLB
 
@@ -282,7 +285,13 @@
 	info[0] = 0;
 	if (mtmp->mtame) {	  Strcat(info, ", tame");
 #ifdef WIZARD
-	    if (wizard)		  Sprintf(eos(info), " (%d)", mtmp->mtame);
+	    if (wizard) {
+		Sprintf(eos(info), " (%d", mtmp->mtame);
+		if (!mtmp->isminion)
+		    Sprintf(eos(info), "; hungry %ld; apport %d",
+			EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport);
+		Strcat(info, ")");
+	    }
 #endif
 	}
 	else if (mtmp->mpeaceful) Strcat(info, ", peaceful");
diff -Naurd ../nethack-3.3.1/src/polyself.c ./src/polyself.c
--- ../nethack-3.3.1/src/polyself.c Sun Jul 16 16:11:05 2000
+++ ./src/polyself.c Fri Mar 22 14:40:55 2002
@@ -16,7 +16,6 @@
 STATIC_DCL void FDECL(polyman, (const char *,const char *));
 STATIC_DCL void NDECL(break_armor);
 STATIC_DCL void FDECL(drop_weapon,(int));
-STATIC_DCL void NDECL(skinback);
 STATIC_DCL void NDECL(uunstick);
 STATIC_DCL int FDECL(armor_to_dragon,(int));
 STATIC_DCL void NDECL(newman);
@@ -48,7 +47,7 @@
 
 	u.mh = u.mhmax = 0;
 	u.mtimedone = 0;
-	skinback();
+	skinback(FALSE);
 	u.uundetected = 0;
 
 	if (sticky) uunstick();
@@ -104,13 +103,15 @@
 	boolean already_polyd = (boolean) Upolyd;
 
 	/* Some monsters are always of one sex and their sex can't be changed */
-	/* succubi/incubi are handled below */
-	if (u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS && !is_male(youmonst.data) && !is_female(youmonst.data) && !is_neuter(youmonst.data))
+	/* succubi/incubi can change, but are handled below */
+	/* !already_polyd check necessary because is_male() and is_female()
+           are true if the player is a priest/priestess */
+	if (!already_polyd || (!is_male(youmonst.data) && !is_female(youmonst.data) && !is_neuter(youmonst.data)))
 	    flags.female = !flags.female;
 	if (already_polyd)	/* poly'd: also change saved sex */
 	    u.mfemale = !u.mfemale;
 	max_rank_sz();		/* [this appears to be superfluous] */
-	if (flags.female && urole.name.f)
+	if ((already_polyd ? u.mfemale : flags.female) && urole.name.f)
 	    Strcpy(pl_character, urole.name.f);
 	else
 	    Strcpy(pl_character, urole.name.m);
@@ -119,6 +120,7 @@
 	if (!already_polyd) {
 	    u.umonnum = u.umonster;
 	} else if (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS) {
+	    flags.female = !flags.female;
 	    /* change monster type to match new sex */
 	    u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS;
 	    set_uasmon();
@@ -128,24 +130,34 @@
 STATIC_OVL void
 newman()
 {
-	int tmp, tmp2;
-
-	if (!rn2(10)) change_sex();
+	int tmp, oldlvl;
 
 	tmp = u.uhpmax;
-	tmp2 = u.ulevel;
+	oldlvl = u.ulevel;
 	u.ulevel = u.ulevel + rn1(5, -2);
-	if (u.ulevel > 127 || u.ulevel < 1) u.ulevel = 1;
+	if (u.ulevel > 127 || u.ulevel < 1) { /* level went below 0? */
+	    u.ulevel = oldlvl; /* restore old level in case they lifesave */
+	    goto dead;
+	}
 	if (u.ulevel > MAXULEV) u.ulevel = MAXULEV;
+	/* If your level goes down, your peak level goes down by
+	   the same amount so that you can't simply use blessed
+	   full healing to undo the decrease.  But if your level
+	   goes up, your peak level does *not* undergo the same
+	   adjustment; you might end up losing out on the chance
+	   to regain some levels previously lost to other causes. */
+	if (u.ulevel < oldlvl) u.ulevelmax -= (oldlvl - u.ulevel);
 	if (u.ulevelmax < u.ulevel) u.ulevelmax = u.ulevel;
 
-	adjabil(tmp2, (int)u.ulevel);
+	if (!rn2(10)) change_sex();
+
+	adjabil(oldlvl, (int)u.ulevel);
 	reset_rndmonst(NON_PM);	/* new monster generation criteria */
 
 	/* random experience points for the new experience level */
 	u.uexp = rndexp();
 
-	/* u.uhpmax * u.ulevel / tmp2: proportionate hit points to new level
+	/* u.uhpmax * u.ulevel / oldlvl: proportionate hit points to new level
 	 * -10 and +10: don't apply proportionate HP to 10 of a starting
 	 *   character's hit points (since a starting character's hit points
 	 *   are not on the same scale with hit points obtained through level
@@ -153,7 +165,7 @@
 	 * 9 - rn2(19): random change of -9 to +9 hit points
 	 */
 #ifndef LINT
-	u.uhpmax = ((u.uhpmax - 10) * (long)u.ulevel / tmp2 + 10) +
+	u.uhpmax = ((u.uhpmax - 10) * (long)u.ulevel / oldlvl + 10) +
 		(9 - rn2(19));
 #endif
 
@@ -165,7 +177,7 @@
 
 	tmp = u.uenmax;
 #ifndef LINT
-	u.uenmax = u.uenmax * (long)u.ulevel / tmp2 + 9 - rn2(19);
+	u.uenmax = u.uenmax * (long)u.ulevel / oldlvl + 9 - rn2(19);
 #endif
 	if (u.uenmax < 0) u.uenmax = 0;
 #ifndef LINT
@@ -174,7 +186,6 @@
 
 	redist_attr();
 	u.uhunger = rn1(500,500);
-	newuhs(FALSE);
 	if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL);
 	Stoned = 0;
 	delayed_killer = 0;
@@ -183,12 +194,16 @@
 		    if (u.uhp <= 0) u.uhp = 1;
 		    if (u.uhpmax <= 0) u.uhpmax = 1;
 		} else {
+dead: /* we come directly here if their experience level went to 0 or less */
 		    Your("new form doesn't seem healthy enough to survive.");
 		    killer_format = KILLED_BY_AN;
 		    killer="unsuccessful polymorph";
 		    done(DIED);
+		    newuhs(FALSE);
+		    return; /* lifesaved */
 		}
 	}
+	newuhs(FALSE);
 	polyman("feel like a new %s!",
 		(flags.female && urace.individual.f) ? urace.individual.f :
 		(urace.individual.m) ? urace.individual.m : urace.noun);
@@ -202,7 +217,8 @@
 }
 
 void
-polyself()
+polyself(forcecontrol)
+boolean forcecontrol;     
 {
 	char buf[BUFSZ];
 	int old_light, new_light;
@@ -214,7 +230,7 @@
 	boolean iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data));
 	boolean isvamp = (youmonst.data->mlet == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT);
 
-	if(!Polymorph_control && !draconian && !iswere && !isvamp) {
+        if(!Polymorph_control && !forcecontrol && !draconian && !iswere && !isvamp) {
 	    if (rn2(20) > ACURR(A_CON)) {
 		You(shudder_for_moment);
 		losehp(rnd(30), "system shock", KILLED_BY_AN);
@@ -224,7 +240,7 @@
 	}
 	old_light = Upolyd ? emits_light(youmonst.data) : 0;
 
-	if (Polymorph_control) {
+	if (Polymorph_control || forcecontrol) {
 		do {
 			getlin("Become what kind of monster? [type the name]",
 				buf);
@@ -388,12 +404,14 @@
 		You("no longer feel sick.");
 	}
 	if (Slimed) {
-	    if (mntmp == PM_FIRE_VORTEX || mntmp == PM_FIRE_ELEMENTAL) {
+	    if (mntmp == PM_FIRE_VORTEX || mntmp == PM_FIRE_ELEMENTAL || mntmp == PM_SALAMANDER) {
 		pline_The("slime burns away!");
-		Slimed = 0;
+		Slimed = 0L;
+		flags.botl = 1;
 	    } else if (mntmp == PM_GREEN_SLIME) {
 		/* do it silently */
-		Slimed = 0;
+		Slimed = 0L;
+		flags.botl = 1;
 	    }
 	}
 	if (nohands(youmonst.data)) Glib = 0;
@@ -428,7 +446,7 @@
 	}
 
 	if (uskin && mntmp != armor_to_dragon(uskin->otyp))
-		skinback();
+		skinback(FALSE);
 	break_armor();
 	drop_weapon(1);
 	if (hides_under(youmonst.data))
@@ -470,8 +488,8 @@
 		pline(use_thec,monsterc,"spit venom");
 	    if (youmonst.data->mlet == S_NYMPH)
 		pline(use_thec,monsterc,"remove an iron ball");
-	    if (youmonst.data->mlet == S_UMBER)
-		pline(use_thec,monsterc,"confuse monsters");
+	    if (attacktype(youmonst.data, AT_GAZE))
+		pline(use_thec,monsterc,"gaze at monsters");
 	    if (is_hider(youmonst.data))
 		pline(use_thec,monsterc,"hide");
 	    if (is_were(youmonst.data))
@@ -549,11 +567,11 @@
 	}
 	if ((otmp = uarmc) != 0) {
 	    if(otmp->oartifact) {
-		Your("cloak falls off!");
+		Your("%s falls off!", cloak_simple_name(otmp));
 		(void) Cloak_off();
 		dropx(otmp);
 	    } else {
-		Your("cloak tears apart!");
+		Your("%s tears apart!", cloak_simple_name(otmp));
 		(void) Cloak_off();
 		useup(otmp);
 	    }
@@ -573,8 +591,8 @@
 	}
 	if ((otmp = uarmc) != 0) {
 		if (is_whirly(youmonst.data))
-			Your("cloak falls, unsupported!");
-		else You("shrink out of your cloak!");
+			Your("%s falls, unsupported!", cloak_simple_name(otmp));
+		else You("shrink out of your %s!", cloak_simple_name(otmp));
 		(void) Cloak_off();
 		dropx(otmp);
 	}
@@ -628,6 +646,8 @@
 int alone;
 {
     struct obj *otmp;
+    struct obj *otmp2;
+
     if ((otmp = uwep) != 0) {
 	/* !alone check below is currently superfluous but in the
 	 * future it might not be so if there are monsters which cannot
@@ -638,10 +658,16 @@
 
 	    if (alone) You("find you must drop your weapon%s!",
 			   	u.twoweap ? "s" : "");
+	    otmp2 = u.twoweap ? uswapwep : 0;
 	    uwepgone();
 	    if (!wep->cursed || wep->otyp != LOADSTONE)
 		dropx(otmp);
-		untwoweapon();
+	    if (otmp2 != 0) {
+		uswapwepgone();
+		if (!otmp2->cursed || otmp2->otyp != LOADSTONE)
+		    dropx(otmp2);
+	    }
+	    untwoweapon();
 	}
     }
 }
@@ -714,7 +740,7 @@
 	otmp = mksobj(u.umonnum==PM_COBRA ? BLINDING_VENOM : ACID_VENOM,
 			TRUE, FALSE);
 	otmp->spe = 1; /* to indicate it's yours */
-	throwit(otmp, 0L);
+	throwit(otmp, 0L, FALSE);
 	return(1);
 }
 
@@ -806,9 +832,16 @@
 			deltrap(ttmp);
 			if (Invisible) newsym(u.ux, u.uy);
 			return 1;
+		case ROLLING_BOULDER_TRAP:
+			You("spin a web, jamming the trigger.");
+			deltrap(ttmp);
+			if (Invisible) newsym(u.ux, u.uy);
+			return(1);
 		case ARROW_TRAP:
 		case DART_TRAP:
 		case BEAR_TRAP:
+		case ROCKTRAP:
+		case FIRE_TRAP:
 		case LANDMINE:
 		case SLP_GAS_TRAP:
 		case RUST_TRAP:
@@ -816,7 +849,7 @@
 		case ANTI_MAGIC:
 		case POLY_TRAP:
 			You("have triggered a trap!");
-			dotrap(ttmp);
+			dotrap(ttmp, 0);
 			return(1);
 		default:
 			impossible("Webbing over trap type %d?", ttmp->ttyp);
@@ -849,15 +882,29 @@
 }
 
 int
-doconfuse()
+dogaze()
 {
 	register struct monst *mtmp;
 	int looked = 0;
 	char qbuf[QBUFSZ];
+	int i;
+	uchar adtyp = 0;
+
+	for (i = 0; i < NATTK; i++) {
+	    if(youmonst.data->mattk[i].aatyp == AT_GAZE) {
+		adtyp = youmonst.data->mattk[i].adtyp;
+		break;
+	    }
+	}
+	if (adtyp != AD_CONF && adtyp != AD_FIRE) {
+	    impossible("gaze attack %d?", adtyp);
+	    return 0;
+	}
+
 
 	if (Blind) {
-		You_cant("see anything to gaze at.");
-		return 0;
+	    You_cant("see anything to gaze at.");
+	    return 0;
 	}
 	if (u.uen < 15) {
 	    You("lack the energy to use your special gaze!");
@@ -868,7 +915,7 @@
 
 	for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
 	    if (DEADMONSTER(mtmp)) continue;
-	    if (canseemon(mtmp)) {
+	    if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
 		looked++;
 		if (Invis && !perceives(mtmp->data))
 		    pline("%s seems not to notice your gaze.", Monnam(mtmp));
@@ -884,7 +931,9 @@
 		} else {
 		    if (flags.confirm && mtmp->mpeaceful && !Confusion
 							&& !Hallucination) {
-			Sprintf(qbuf, "Really confuse %s?", mon_nam(mtmp));
+			Sprintf(qbuf, "Really %s %s?",
+			    (adtyp == AD_CONF) ? "confuse" : "attack",
+			    mon_nam(mtmp));
 			if (yn(qbuf) != 'y') continue;
 			setmangry(mtmp);
 		    }
@@ -893,15 +942,37 @@
 			looked--;
 			continue;
 		    }
-		    if (!mon_reflects(mtmp,"Your gaze is reflected by %s %s.")){
+		    /* No reflection check for consistency with when a monster
+		     * gazes at *you*--only medusa gaze gets reflected then.
+		     */
+		    if (adtyp == AD_CONF) {
 			if (!mtmp->mconf)
 			    Your("gaze confuses %s!", mon_nam(mtmp));
 			else
 			    pline("%s is getting more and more confused.",
-							    Monnam(mtmp));
+							Monnam(mtmp));
 			mtmp->mconf = 1;
+		    } else if (adtyp == AD_FIRE) {
+			int dmg = d(2,6);
+			You("attack %s with a fiery gaze!", mon_nam(mtmp));
+			if (resists_fire(mtmp)) {
+			    pline_The("fire doesn't burn %s!", mon_nam(mtmp));
+			    dmg = 0;
+			}
+			if((int) u.ulevel > rn2(20))
+			    (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE);
+			if((int) u.ulevel > rn2(20))
+			    (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
+			if((int) u.ulevel > rn2(25))
+			    (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
+			if (dmg && !DEADMONSTER(mtmp)) mtmp->mhp -= dmg;
+			if (mtmp->mhp <= 0) killed(mtmp);
 		    }
-		    if ((mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) {
+		    /* For consistency with passive() in uhitm.c, this only
+		     * affects you if the monster is still alive.
+		     */
+		    if (!DEADMONSTER(mtmp) &&
+			  (mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) {
 			if (!Free_action) {
 			    You("are frozen by %s gaze!",
 					     s_suffix(mon_nam(mtmp)));
@@ -914,7 +985,13 @@
 			    You("stiffen momentarily under %s gaze.",
 				    s_suffix(mon_nam(mtmp)));
 		    }
-		    if ((mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) {
+		    /* Technically this one shouldn't affect you at all because
+		     * the Medusa gaze is an active monster attack that only
+		     * works on the monster's turn, but for it to *not* have an
+		     * effect would be too weird.
+		     */
+		    if (!DEADMONSTER(mtmp) &&
+			    (mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) {
 			pline(
 			 "Gazing at the awake %s is not a very good idea.",
 			    l_monnam(mtmp));
@@ -996,11 +1073,12 @@
 	u.ustuck = 0;
 }
 
-STATIC_OVL void
-skinback()
+void
+skinback(silently)
+boolean silently;
 {
 	if (uskin) {
-		Your("skin returns to its original form.");
+		if (!silently) Your("skin returns to its original form.");
 		uarm = uskin;
 		uskin = (struct obj *)0;
 		/* undo save/restore hack */
@@ -1019,40 +1097,44 @@
 	static NEARDATA const char
 	*humanoid_parts[] = { "arm", "eye", "face", "finger",
 		"fingertip", "foot", "hand", "handed", "head", "leg",
-		"light headed", "neck", "spine", "toe", "hair", "blood", "lung"},
+		"light headed", "neck", "spine", "toe", "hair",
+		"blood", "lung", "nose", "stomach"},
 	*jelly_parts[] = { "pseudopod", "dark spot", "front",
 		"pseudopod extension", "pseudopod extremity",
 		"pseudopod root", "grasp", "grasped", "cerebral area",
 		"lower pseudopod", "viscous", "middle", "surface",
-		"pseudopod extremity", "ripples", "juices", "surface" },
+		"pseudopod extremity", "ripples", "juices",
+		"surface", "sensor", "stomach" },
 	*animal_parts[] = { "forelimb", "eye", "face", "foreclaw", "claw tip",
 		"rear claw", "foreclaw", "clawed", "head", "rear limb",
 		"light headed", "neck", "spine", "rear claw tip",
-		"fur", "blood", "lung" },
+		"fur", "blood", "lung", "nose", "stomach" },
 	*horse_parts[] = { "foreleg", "eye", "face", "forehoof", "hoof tip",
 		"rear hoof", "foreclaw", "hooved", "head", "rear leg",
 		"light headed", "neck", "backbone", "rear hoof tip",
-		"mane", "blood", "lung" },
+		"mane", "blood", "lung", "nose", "stomach"},
 	*sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
 		"tentacle tip", "lower appendage", "tentacle", "tentacled",
 		"body", "lower tentacle", "rotational", "equator", "body",
-		"lower tentacle tip", "cilia", "life force", "retina" },
+		"lower tentacle tip", "cilia", "life force", "retina",
+		"olfactory nerve", "interior" },
 	*fungus_parts[] = { "mycelium", "visual area", "front", "hypha",
 		"hypha", "root", "strand", "stranded", "cap area",
 		"rhizome", "sporulated", "stalk", "root", "rhizome tip",
-		"spores", "juices", "gill" },
+		"spores", "juices", "gill", "gill", "interior" },
 	*vortex_parts[] = { "region", "eye", "front", "minor current",
 		"minor current", "lower current", "swirl", "swirled",
 		"central core", "lower current", "addled", "center",
-		"currents", "edge", "currents", "life force", "center" },
+		"currents", "edge", "currents", "life force",
+		"center", "leading edge", "interior" },
 	*snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
 		"large scale tip", "rear region", "scale gap", "scale gapped",
 		"head", "rear region", "light headed", "neck", "length",
-		"rear scale", "scales", "blood", "lung" },
+		"rear scale", "scales", "blood", "lung", "forked tongue", "stomach" },
 	*fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary",
 		"pelvic fin", "anal fin", "pectoral fin", "finned", "head", "peduncle",
 		"played out", "gills", "dorsal fin", "caudal fin",
-		"scales", "blood", "gill" };
+		"scales", "blood", "gill", "nostril", "stomach" };
 	/* claw attacks are overloaded in mons[]; most humanoids with
 	   such attacks should still reference hands rather than claws */
 	static const char not_claws[] = {
@@ -1073,8 +1155,16 @@
 		    mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS])
 		return part == HAND ? "claw" : "clawed";
 	}
+	if ((mptr == &mons[PM_MUMAK] || mptr == &mons[PM_MASTODON]) &&
+		part == NOSE)
+	    return "trunk";
 	if (mptr == &mons[PM_SHARK] && part == HAIR)
 	    return "skin";	/* sharks don't have scales */
+	if (mptr == &mons[PM_JELLYFISH] && (part == ARM || part == FINGER ||
+	    part == HAND || part == FOOT || part == TOE))
+	    return "tentacle";
+	if (mptr == &mons[PM_FLOATING_EYE] && part == EYE)
+	    return "cornea";
 	if (humanoid(mptr) &&
 		(part == ARM || part == FINGER || part == FINGERTIP ||
 		    part == HAND || part == HANDED))
@@ -1084,7 +1174,7 @@
 	    return horse_parts[part];
 	if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH])
 	    return fish_parts[part];
-	if (slithy(mptr))
+	if (slithy(mptr) || (mptr->mlet == S_DRAGON && part == HAIR))
 	    return snake_parts[part];
 	if (mptr->mlet == S_EYE)
 	    return sphere_parts[part];
diff -Naurd ../nethack-3.3.1/src/potion.c ./src/potion.c
--- ../nethack-3.3.1/src/potion.c Sat Aug 5 00:42:35 2000
+++ ./src/potion.c Fri Mar 22 14:40:55 2002
@@ -83,7 +83,14 @@
 			Hallucination ? "less wobbly" : "a bit steadier");
 	}
 	if (xtime && !old) {
-		if (talk) You("stagger...");
+		if (talk) {
+#ifdef STEED
+			if (u.usteed)
+				You("wobble in the saddle.");
+			else
+#endif
+			You("%s...", stagger(youmonst.data, "stagger"));
+		}
 	}
 	if ((!xtime && old) || (xtime && !old)) flags.botl = TRUE;
 
@@ -157,34 +164,77 @@
 boolean talk;
 {
 	long old = Blinded;
-	boolean changed = FALSE;
+	boolean u_could_see, can_see_now;
+	int eyecnt;
+	char buf[BUFSZ];
+	static const char
+		vismsg[] = "vision seems to %s for a moment but is %s now.",
+		eyemsg[] = "%s momentarily %s.";
+
+	/* we need to probe ahead in case the Eyes of the Overworld
+	   are or will be overriding blindness */
+	u_could_see = !Blind;
+	Blinded = xtime ? 1L : 0L;
+	can_see_now = !Blind;
+	Blinded = old;		/* restore */
 
 	if (u.usleep) talk = FALSE;
 
-	if (!xtime && old && !Blindfolded && haseyes(youmonst.data)) {
+	if (can_see_now && !u_could_see) {	/* regaining sight */
 	    if (talk) {
 		if (Hallucination)
 		    pline("Far out!  Everything is all cosmic again!");
-		else		   You("can see again.");
+		else
+		    You("can see again.");
+	    }
+	} else if (old && !xtime) {
+	    /* clearing temporary blindness without toggling blindness */
+	    if (talk) {
+		if (!haseyes(youmonst.data)) {
+		    strange_feeling((struct obj *)0, (char *)0);
+		} else if (Blindfolded) {
+		    Strcpy(buf, body_part(EYE));
+		    eyecnt = eyecount(youmonst.data);
+		    Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf),
+			 (eyecnt == 1) ? "itches" : "itch");
+		} else {	/* Eyes of the Overworld */
+		    Your(vismsg, "brighten",
+			 Hallucination ? "sadder" : "normal");
+		}
 	    }
-	    changed = TRUE;
 	}
-	if (xtime && !old && !Blindfolded && haseyes(youmonst.data)) {
+
+	if (u_could_see && !can_see_now) {	/* losing sight */
 	    if (talk) {
 		if (Hallucination)
-			pline("Oh, bummer!  Everything is dark!  Help!");
+		    pline("Oh, bummer!  Everything is dark!  Help!");
 		else
-			pline("A cloud of darkness falls upon you.");
+		    pline("A cloud of darkness falls upon you.");
 	    }
-	    changed = TRUE;
-
 	    /* Before the hero goes blind, set the ball&chain variables. */
 	    if (Punished) set_bc(0);
+	} else if (!old && xtime) {
+	    /* setting temporary blindness without toggling blindness */
+	    if (talk) {
+		if (!haseyes(youmonst.data)) {
+		    strange_feeling((struct obj *)0, (char *)0);
+		} else if (Blindfolded) {
+		    Strcpy(buf, body_part(EYE));
+		    eyecnt = eyecount(youmonst.data);
+		    Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf),
+			 (eyecnt == 1) ? "twitches" : "twitch");
+		} else {	/* Eyes of the Overworld */
+		    Your(vismsg, "dim",
+			 Hallucination ? "happier" : "normal");
+		}
+	    }
 	}
+
 	set_itimeout(&Blinded, xtime);
-	if (changed) {
+
+	if (u_could_see ^ can_see_now) {  /* one or the other but not both */
 	    flags.botl = 1;
-	    vision_full_recalc = 1;
+	    vision_full_recalc = 1;	/* blindness just got toggled */
 	    if (Blind_telepat || Infravision) see_monsters();
 	}
 }
@@ -352,7 +402,9 @@
 		    break;
 		} else {
 		    pline("Wow!  This makes you feel %s!",
-			  (otmp->blessed) ? "great" : "good");
+			  (otmp->blessed) ?
+				(unfixable_trouble_count(FALSE) ? "better" : "great")
+			  : "good");
 		    i = rn2(A_MAX);		/* start at a random point */
 		    for (ii = 0; ii < A_MAX; ii++) {
 			lim = AMAX(i);
@@ -517,7 +569,7 @@
 	    }
 	case POT_PARALYSIS:
 		if (Free_action)
-		    You("stiffen momentarily.");             
+		    You("stiffen momentarily.");
 		else {
 		    if (Levitation||Is_airlevel(&u.uz)||Is_waterlevel(&u.uz))
 			You("are motionlessly suspended.");
@@ -533,7 +585,7 @@
 		    exercise(A_DEX, FALSE);
 		}
 		break;
-	case POT_SLEEPING:        
+	case POT_SLEEPING:
 		if(Sleep_resistance || Free_action)
 		    You("yawn.");
 		else {
@@ -548,7 +600,12 @@
 
 		    if (Detect_monsters) nothing++;
 		    unkn++;
-		    incr_itimeout(&HDetect_monsters, 20+rnd(40));
+		    /* after a while, repeated uses become less effective */
+		    if (HDetect_monsters >= 300L)
+			i = 1;
+		    else
+			i = rn1(40,21);
+		    incr_itimeout(&HDetect_monsters, i);
 		    for (x = 1; x < COLNO; x++) {
 			for (y = 0; y < ROWNO; y++) {
 			    if (levl[x][y].glyph == GLYPH_INVISIBLE) {
@@ -577,8 +634,14 @@
 		if (otmp->blessed) {
 		    pline("(But in fact it was mildly stale %s juice.)",
 			  pl_fruit);
-		    if (!Role_if(PM_HEALER))
-			losehp(1, "mildly contaminated potion", KILLED_BY_AN);
+		    if (!Role_if(PM_HEALER)) {
+			if (otmp->corpsenm)
+			    losehp(1,
+				   "mildly contaminated tap water", KILLED_BY);
+			else
+			    losehp(1,
+				   "mildly contaminated potion", KILLED_BY_AN);
+		    }
 		} else {
 		    if(Poison_resistance)
 			pline(
@@ -595,9 +658,14 @@
 			    		Poison_resistance ? -1 : -rn1(4,3),
 			    		TRUE);
 			}
-			if(!Poison_resistance)
+			if(!Poison_resistance) {
+			    if (otmp->corpsenm)
+				losehp(rnd(10)+5*!!(otmp->cursed),
+				       "contaminated tap water", KILLED_BY);
+			    else
 				losehp(rnd(10)+5*!!(otmp->cursed),
 				       "contaminated potion", KILLED_BY_AN);
+			}
 			exercise(A_CON, FALSE);
 		    }
 		}
@@ -622,6 +690,8 @@
 		if(otmp->cursed) {
 		    pline("Ulch!  That potion tasted foul!");
 		    unkn++;
+		} else if (Fixed_abil) {
+		    nothing++;
 		} else {      /* If blessed, increase all; if not, try up to */
 		    int itmp; /* 6 times to find one which can be increased. */
 		    i = -1;		/* increment to 0 */
@@ -636,11 +706,15 @@
 		}
 		break;
 	case POT_SPEED:
-		if(Wounded_legs && !otmp->cursed) {
+		if(Wounded_legs && !otmp->cursed
+#ifdef STEED
+		   && !u.usteed	/* heal_legs() would heal steeds legs */
+#endif
+						) {
 			heal_legs();
 			unkn++;
 			break;
-		}		/* and fall through */
+		} /* and fall through */
 	case SPE_HASTE_SELF:
 		if(!Very_fast) /* wwf@doe.carleton.ca */
 			You("are suddenly moving %sfaster.",
@@ -657,7 +731,7 @@
 		if(Blind) nothing++;
 		make_blinded(itimeout_incr(Blinded,
 					   rn1(200, 250 - 125 * bcsign(otmp))),
-			     TRUE);
+			     (boolean)!Blind);
 		break;
 	case POT_GAIN_LEVEL:
 		if (otmp->cursed) {
@@ -706,12 +780,16 @@
 		exercise(A_CON, TRUE);
 		exercise(A_STR, TRUE);
 		break;
-	case POT_FULL_HEALING:        
+	case POT_FULL_HEALING:
 		You_feel("completely healed.");
 		healup(400, 4+4*bcsign(otmp), !otmp->cursed, TRUE);
 		/* Restore one lost level if blessed */
-		if (otmp->blessed && (u.ulevel < u.ulevelmax))
-			pluslvl(FALSE);
+		if (otmp->blessed && u.ulevel < u.ulevelmax) {
+		    /* when multiple levels have been lost, drinking
+		       multiple potions will only get half of them back */
+		    u.ulevelmax -= 1;
+		    pluslvl(FALSE);
+		}
 		make_hallucinated(0L,TRUE,0L);
 		exercise(A_STR, TRUE);
 		exercise(A_CON, TRUE);
@@ -744,6 +822,7 @@
 		    incr_itimeout(&HLevitation, rn1(50,250));
 		    HLevitation |= I_SPECIAL;
 		} else incr_itimeout(&HLevitation, rn1(140,10));
+		spoteffects(FALSE);	/* for sinks */
 		break;
 	case POT_GAIN_ENERGY:			/* M. Stephenson */
 		{	register int num;
@@ -786,16 +865,17 @@
 			pline("This tastes %s.", Hallucination ? "tangy" : "sour");
 		else {
 			pline("This burns%s!", otmp->blessed ? " a little" :
-					otmp->cursed ? " a lot" : "");
+					otmp->cursed ? " a lot" : " like acid");
 			losehp(d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8),
 					"potion of acid", KILLED_BY_AN);
 			exercise(A_CON, FALSE);
 		}
 		if (Stoned) fix_petrification();
+		unkn++; /* holy/unholy water can burn like acid too */
 		break;
 	case POT_POLYMORPH:
 		You_feel("a little %s.", Hallucination ? "normal" : "strange");
-		if (!Unchanging) polyself();
+		if (!Unchanging) polyself(FALSE);
 		break;
 	default:
 		impossible("What a funny potion! (%u)", otmp->otyp);
@@ -829,7 +909,7 @@
 register struct obj *obj;
 register const char *txt;
 {
-	if(flags.beginner)
+	if (flags.beginner || !txt)
 		You("have a %s feeling for a moment, then it passes.",
 		Hallucination ? "normal" : "strange");
 	else
@@ -848,13 +928,20 @@
 	"bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
 };
 
+
+const char *
+bottlename()
+{
+	return bottlenames[rn2(SIZE(bottlenames))];
+}
+
 void
 potionhit(mon, obj, your_fault)
 register struct monst *mon;
 register struct obj *obj;
 boolean your_fault;
 {
-	register const char *botlnam = bottlenames[rn2(SIZE(bottlenames))];
+	register const char *botlnam = bottlename();
 	boolean isyou = (mon == &youmonst);
 	int distance;
 
@@ -886,7 +973,7 @@
 
 	/* oil doesn't instantly evaporate */
 	if (obj->otyp != POT_OIL && cansee(mon->mx,mon->my))
-		pline("%s evaporates.", The(xname(obj)));
+		pline("%s.", Tobjnam(obj, "evaporate"));
 
     if (isyou) {
 	switch (obj->otyp) {
@@ -896,7 +983,7 @@
 		break;
 	case POT_POLYMORPH:
 		You_feel("a little %s.", Hallucination ? "normal" : "strange");
-		if (!Unchanging && !Antimagic) polyself();
+		if (!Unchanging && !Antimagic) polyself(FALSE);
 		break;
 	case POT_ACID:
 		if (!Acid_resistance) {
@@ -930,7 +1017,7 @@
 	case POT_SICKNESS:
 		if (mon->data == &mons[PM_PESTILENCE]) goto do_healing;
 		if (dmgtype(mon->data, AD_DISE) ||
-			   dmgtype(mon->data, AD_PEST) ||
+			   dmgtype(mon->data, AD_PEST) || /* won't happen, see prior goto */
 			   resists_poison(mon)) {
 		    if (canseemon(mon))
 			pline("%s looks unharmed.", Monnam(mon));
@@ -971,7 +1058,7 @@
 		break;
 	case POT_SPEED:
 		angermon = FALSE;
-		mon_adjust_speed(mon, 1);
+		mon_adjust_speed(mon, 1, obj);
 		break;
 	case POT_BLINDNESS:
 		if(haseyes(mon->data)) {
@@ -986,7 +1073,8 @@
 		if (is_undead(mon->data) || is_demon(mon->data) ||
 			is_were(mon->data)) {
 		    if (obj->blessed) {
-			pline("%s shrieks in pain!", Monnam(mon));
+			pline("%s %s in pain!", Monnam(mon),
+			      is_silent(mon->data) ? "writhes" : "shrieks");
 			mon->mhp -= d(2,6);
 			/* should only be by you */
 			if (mon->mhp < 1) killed(mon);
@@ -1019,7 +1107,8 @@
 		break;
 	case POT_ACID:
 		if (!resists_acid(mon) && !resist(mon, POTION_CLASS, 0, NOTELL)) {
-		    pline("%s shrieks in pain!", Monnam(mon));
+		    pline("%s %s in pain!", Monnam(mon),
+			  is_silent(mon->data) ? "writhes" : "shrieks");
 		    mon->mhp -= d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8);
 		    if (mon->mhp < 1) {
 			if (your_fault)
@@ -1048,7 +1137,8 @@
     }
 
 	/* Note: potionbreathe() does its own docall() */
-	if (distance==0 || ((distance < 3) && rn2(5)))
+	if ((distance==0 || ((distance < 3) && rn2(5))) &&
+	    (!breathless(youmonst.data) || haseyes(youmonst.data)))
 		potionbreathe(obj);
 	else if (obj->dknown && !objects[obj->otyp].oc_name_known &&
 		   !objects[obj->otyp].oc_uname && cansee(mon->mx,mon->my))
@@ -1068,6 +1158,7 @@
 	obfree(obj, (struct obj *)0);
 }
 
+/* vapors are inhaled or get in your eyes */
 void
 potionbreathe(obj)
 register struct obj *obj;
@@ -1078,7 +1169,14 @@
 	case POT_RESTORE_ABILITY:
 	case POT_GAIN_ABILITY:
 		if(obj->cursed) {
-		    pline("Ulch!  That potion smells terrible!");
+		    if (!breathless(youmonst.data))
+			pline("Ulch!  That potion smells terrible!");
+		    else if (haseyes(youmonst.data)) {
+			int numeyes = eyecount(youmonst.data);
+			Your("%s sting%s!",
+			     (numeyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)),
+			     (numeyes == 1) ? "s" : "");
+		    }
 		    break;
 		} else {
 		    i = rn2(A_MAX);		/* start at a random point */
@@ -1136,16 +1234,16 @@
 		break;
 	case POT_PARALYSIS:
 		kn++;
-		if (!Free_action) {                
+		if (!Free_action) {
 		    pline("%s seems to be holding you.", Something);
 		    nomul(-rnd(5));
 		    nomovemsg = You_can_move_again;
 		    exercise(A_DEX, FALSE);
-		} else You("stiffen momentarily.");                
+		} else You("stiffen momentarily.");
 		break;
 	case POT_SLEEPING:
 		kn++;
-		if (!Free_action && !Sleep_resistance) {                
+		if (!Free_action && !Sleep_resistance) {
 		    You_feel("rather tired.");
 		    nomul(-rnd(5));
 		    nomovemsg = You_can_move_again;
@@ -1163,6 +1261,7 @@
 		    pline("It suddenly gets dark.");
 		}
 		make_blinded(itimeout_incr(Blinded, rnd(5)), FALSE);
+		if (!Blind && !u.usleep) Your(vision_clears);
 		break;
 	case POT_WATER:
 		if(u.umonnum == PM_GREMLIN) {
@@ -1175,6 +1274,7 @@
 		    else if (obj->cursed && !Upolyd)
 			you_were();
 		}
+		break;
 	case POT_ACID:
 	case POT_POLYMORPH:
 		exercise(A_CON, FALSE);
@@ -1232,7 +1332,7 @@
 			    case POT_GAIN_ENERGY:
 				return POT_FULL_HEALING;
 			}
-		case POT_FULL_HEALING:                
+		case POT_FULL_HEALING:
 			switch (o2->otyp) {
 			    case POT_GAIN_LEVEL:
 			    case POT_GAIN_ENERGY:
@@ -1307,7 +1407,7 @@
 	if (snuff_lit(obj)) return(TRUE);
 
 	if (obj->greased) {
-		grease_protect(obj,(char *)0,FALSE,&youmonst);
+		grease_protect(obj,(char *)0,&youmonst);
 		return(FALSE);
 	}
 	(void) Shk_Your(Your_buf, obj);
@@ -1359,9 +1459,8 @@
 		    ) {
 			if (!Blind) {
 				boolean oq1 = obj->quan == 1L;
-				pline_The("scroll%s fade%s.",
-					oq1 ? "" : "s",
-					oq1 ? "s" : "");
+				pline_The("scroll%s %s.",
+					  oq1 ? "" : "s", otense(obj, "fade"));
 			}
 			if(obj->unpaid && costly_spot(u.ux, u.uy)) {
 			    You("erase it, you pay for it.");
@@ -1382,7 +1481,7 @@
 			    if (!Blind) {
 				    boolean oq1 = obj->quan == 1L;
 				    pline_The("spellbook%s fade%s.",
-					oq1 ? "" : "s", oq1 ? "s" : "");
+					oq1 ? "" : "s", otense(obj, "fade"));
 			    }
 			    if(obj->unpaid && costly_spot(u.ux, u.uy)) {
 			        You("erase it, you pay for it.");
@@ -1402,6 +1501,7 @@
 dodip()
 {
 	register struct obj *potion, *obj;
+	struct obj *singlepotion;
 	const char *tmp;
 	uchar here;
 	char allowall[2];
@@ -1500,16 +1600,34 @@
 	    if (obj->otyp == potion->otyp ||	/* both POT_POLY */
 		    obj->otyp == WAN_POLYMORPH ||
 		    obj->otyp == SPE_POLYMORPH ||
+		    obj == uball || obj == uskin ||
 		    obj_resists(obj->otyp == POT_POLYMORPH ?
 				potion : obj, 5, 95)) {
 		pline(nothing_happens);
 	    } else {
+	    	boolean was_wep = FALSE, was_swapwep = FALSE, was_quiver = FALSE;
+		short save_otyp = obj->otyp;
 		/* KMH, conduct */
 		u.uconduct.polypiles++;
 
-		poly_obj(obj, STRANGE_OBJECT);
-		makeknown(POT_POLYMORPH);
-		useup(potion);
+		if (obj == uwep) was_wep = TRUE;
+		else if (obj == uswapwep) was_swapwep = TRUE;
+		else if (obj == uquiver) was_quiver = TRUE;
+
+		obj = poly_obj(obj, STRANGE_OBJECT);
+
+		if (was_wep) setuwep(obj);
+		else if (was_swapwep) setuswapwep(obj);
+		else if (was_quiver) setuqwep(obj);
+
+		if (obj->otyp != save_otyp) {
+			makeknown(POT_POLYMORPH);
+			useup(potion);
+			prinv((char *)0, obj, 0L);
+		} else {
+			pline("Nothing seems to happen.");
+			useup(potion);
+		}
 	    }
 	    return(1);
 	} else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) {
@@ -1519,7 +1637,8 @@
 		if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) {
 			pline("BOOM!  They explode!");
 			exercise(A_STR, FALSE);
-			potionbreathe(obj);
+			if (!breathless(youmonst.data) || haseyes(youmonst.data))
+				potionbreathe(obj);
 			useup(obj);
 			useup(potion);
 			losehp(rnd(10), "alchemic blast", KILLED_BY_AN);
@@ -1596,9 +1715,12 @@
 	if(is_poisonable(obj)) {
 	    if(potion->otyp == POT_SICKNESS && !obj->opoisoned) {
 		char buf[BUFSZ];
-		Strcpy(buf, The(xname(potion)));
-		pline("%s form%s a coating on %s.",
-			buf, potion->quan == 1L ? "s" : "", the(xname(obj)));
+		if (potion->quan > 1L)
+		    Sprintf(buf, "One of %s", the(xname(potion)));
+		else
+		    Strcpy(buf, The(xname(potion)));
+		pline("%s forms a coating on %s.",
+		      buf, the(xname(obj)));
 		obj->opoisoned = TRUE;
 		goto poof;
 	    } else if(obj->opoisoned &&
@@ -1611,19 +1733,20 @@
 	    }
 	}
 
-	if (potion->otyp == POT_OIL &&
-		(obj->oclass == WEAPON_CLASS || is_weptool(obj))) {
+	if (potion->otyp == POT_OIL) {
 	    boolean wisx = FALSE;
 	    if (potion->lamplit) {	/* burning */
 		int omat = objects[obj->otyp].oc_material;
-		if (obj->oerodeproof || obj_resists(obj, 5, 95) ||
-			/* `METAL' should not be confused with is_metallic() */
-			omat == METAL || omat == MITHRIL || omat == BONE) {
-		    pline("%s seem%s to burn for a moment.",
-			  Yname2(obj),
-			  (obj->quan > 1L) ? "" : "s");
+		/* the code here should be merged with fire_damage */
+		if (catch_lit(obj)) {
+		    /* catch_lit does all the work if true */
+		} else if (obj->oerodeproof || obj_resists(obj, 5, 95) ||
+			   !is_flammable(obj) || obj->oclass == FOOD_CLASS) {
+		    pline("%s %s to burn for a moment.",
+			  Yname2(obj), otense(obj, "seem"));
 		} else {
-		    if (omat == PLASTIC) obj->oeroded = MAX_ERODE;
+		    if ((omat == PLASTIC || omat == PAPER) && !obj->oartifact)
+			obj->oeroded = MAX_ERODE;
 		    pline_The("burning oil %s %s.",
 			    obj->oeroded == MAX_ERODE ? "destroys" : "damages",
 			    yname(obj));
@@ -1632,8 +1755,12 @@
 			obfree(obj, (struct obj *)0);
 			obj = (struct obj *) 0;
 		    } else {
-			/* should check for and do something about
-			   damaging unpaid shop goods here */
+			/* we know it's carried */
+			if (obj->unpaid) {
+			    /* create a dummy duplicate to put on bill */
+			    verbalize("You burnt it, you bought it!");
+			    bill_dummy_object(obj);
+			}
 			obj->oeroded++;
 		    }
 		}
@@ -1641,20 +1768,21 @@
 		pline_The("potion spills and covers your %s with oil.",
 			  makeplural(body_part(FINGER)));
 		incr_itimeout(&Glib, d(2,10));
+	    } else if (obj->oclass != WEAPON_CLASS && !is_weptool(obj)) {
+		/* the following cases apply only to weapons */
+		goto more_dips;
 	    /* Oil removes rust and corrosion, but doesn't unburn.
 	     * Arrows, etc are classed as metallic due to arrowhead
 	     * material, but dipping in oil shouldn't repair them.
 	     */
 	    } else if ((!is_rustprone(obj) && !is_corrodeable(obj)) ||
-				is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) {
+			is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) {
 		/* uses up potion, doesn't set obj->greased */
-		pline("%s gleam%s with an oily sheen.",
-		      Yname2(obj),
-		      (obj->quan > 1L) ? "" : "s");
+		pline("%s %s with an oily sheen.",
+		      Yname2(obj), otense(obj, "gleam"));
 	    } else {
 		pline("%s %s less %s.",
-		      Yname2(obj),
-		      (obj->quan > 1L) ? "are" : "is",
+		      Yname2(obj), otense(obj, "are"),
 		      (obj->oeroded && obj->oeroded2) ? "corroded and rusty" :
 			obj->oeroded ? "rusty" : "corroded");
 		if (obj->oeroded > 0) obj->oeroded--;
@@ -1666,6 +1794,7 @@
 	    useup(potion);
 	    return 1;
 	}
+    more_dips:
 
 	/* Allow filling of MAGIC_LAMPs to prevent identification by player */
 	if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP) &&
@@ -1673,7 +1802,7 @@
 	    /* Turn off engine before fueling, turn off fuel too :-)  */
 	    if (obj->lamplit || potion->lamplit) {
 		useup(potion);
-		explode(u.ux, u.uy, 11, d(6,6), 0);
+		explode(u.ux, u.uy, 11, d(6,6), 0, EXPL_FIERY);
 		exercise(A_WIS, FALSE);
 		return 1;
 	    }
@@ -1683,7 +1812,7 @@
 		obj->age = 0;
 	    }
 	    if (obj->age > 1000L) {
-		pline("%s is full.", Yname2(obj));
+		pline("%s %s full.", Yname2(obj), otense(obj, "are"));
 	    } else {
 		You("fill %s with oil.", yname(obj));
 		check_unpaid(potion);	/* Yendorian Fuel Tax */
@@ -1692,6 +1821,7 @@
 		useup(potion);
 		exercise(A_WIS, TRUE);
 	    }
+	    makeknown(POT_OIL);
 	    obj->spe = 1;
 	    update_inventory();
 	    return 1;
@@ -1699,34 +1829,45 @@
 
 	if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST) &&
 	    (mixture = mixtype(obj, potion)) != 0) {
-		/* with multiple merged potions, we should split off one and
-		   just clear it, but clearing them all together is easier */
-		boolean more_than_one = potion->quan > 1L;
-		potion->otyp = mixture;
-		potion->blessed = 0;
+		boolean more_than_one = potion->quan > 1;
+		/* with multiple merged potions, split off one and
+		   just clear it */
+		if (potion->quan > 1L) {
+		    singlepotion = splitobj(potion, 1L);
+		} else singlepotion = potion;
+		
+		if(singlepotion->unpaid && costly_spot(u.ux, u.uy)) {
+		    You("use it, you pay for it.");
+		    bill_dummy_object(singlepotion);
+		}
+		singlepotion->otyp = mixture;
+		singlepotion->blessed = 0;
 		if (mixture == POT_WATER)
-		    potion->cursed = potion->odiluted = 0;
+		    singlepotion->cursed = singlepotion->odiluted = 0;
 		else
-		    potion->cursed = obj->cursed;  /* odiluted left as-is */
+		    singlepotion->cursed = obj->cursed;  /* odiluted left as-is */
+		singlepotion->bknown = FALSE;
 		if (Blind)
-			potion->dknown = FALSE;
+			singlepotion->dknown = FALSE;
 		else {
 			if (mixture == POT_WATER &&
 #ifdef DCC30_BUG
-			    (potion->dknown = !Hallucination,
-			     potion->dknown != 0))
+			    (singlepotion->dknown = !Hallucination,
+			     singlepotion->dknown != 0))
 #else
-			    (potion->dknown = !Hallucination) != 0)
+			    (singlepotion->dknown = !Hallucination) != 0)
 #endif
-				pline_The("potion%s clear%s.",
-					more_than_one ? "s" : "",
-					more_than_one ? "" : "s");
+				pline_The("potion%s clears.",
+					more_than_one ? " that you dipped into" : "");
 			else
-				pline_The("potion%s turn%s %s.",
-					more_than_one ? "s" : "",
-					more_than_one ? "" : "s",
+				pline_The("potion%s turns %s.",
+					more_than_one ? " that you dipped into" : "",
 					hcolor(OBJ_DESCR(objects[mixture])));
 		}
+	    	obj_extract_self(singlepotion);
+		singlepotion = hold_another_object(singlepotion,
+					"You juggle and drop %s!",
+					doname(singlepotion), (const char *)0);
 		update_inventory();
 		return(1);
 	}
diff -Naurd ../nethack-3.3.1/src/pray.c ./src/pray.c
--- ../nethack-3.3.1/src/pray.c Tue Jul 4 02:39:25 2000
+++ ./src/pray.c Fri Mar 22 14:40:55 2002
@@ -9,6 +9,10 @@
 STATIC_DCL int NDECL(in_trouble);
 STATIC_DCL void FDECL(fix_worst_trouble,(int));
 STATIC_DCL void FDECL(angrygods,(ALIGNTYP_P));
+STATIC_DCL void FDECL(at_your_feet, (const char *));
+#ifdef ELBERETH
+STATIC_DCL void NDECL(gcrownu);
+#endif	/*ELBERETH*/
 STATIC_DCL void FDECL(pleased,(ALIGNTYP_P));
 STATIC_DCL void FDECL(godvoice,(ALIGNTYP_P,const char*));
 STATIC_DCL void FDECL(god_zaps_you,(ALIGNTYP_P));
@@ -164,7 +168,11 @@
 	if(Blinded > 1) return(TROUBLE_BLIND);
 	for(i=0; i<A_MAX; i++)
 	    if(ABASE(i) < AMAX(i)) return(TROUBLE_POISONED);
-	if(Wounded_legs) return (TROUBLE_WOUNDED_LEGS);
+	if(Wounded_legs
+#ifdef STEED
+		    && !u.usteed
+#endif
+				) return (TROUBLE_WOUNDED_LEGS);
 	if(u.uhs >= HUNGRY) return(TROUBLE_HUNGRY);
 	if(HStun) return (TROUBLE_STUNNED);
 	if(HConfusion) return (TROUBLE_CONFUSED);
@@ -188,11 +196,13 @@
 	    case TROUBLE_STONED:
 		    You_feel("more limber.");
 		    Stoned = 0;
+		    flags.botl = 1;
 		    delayed_killer = 0;
 		    break;
 	    case TROUBLE_SLIMED:
 		    pline_The("slime disappears.");
 		    Slimed = 0;
+		    flags.botl = 1;
 		    delayed_killer = 0;
 		    break;
 	    case TROUBLE_STRANGLED:
@@ -202,6 +212,7 @@
 		    }
 		    You("can breathe again.");
 		    Strangled = 0;
+		    flags.botl = 1;
 		    break;
 	    case TROUBLE_LAVA:
 		    You("are back on solid ground.");
@@ -215,7 +226,7 @@
 		    losestr(-1);
 		    /* fall into... */
 	    case TROUBLE_HUNGRY:
-		    Your("stomach feels content.");
+		    Your("%s feels content.", body_part(STOMACH));
 		    init_uhunger();
 		    flags.botl = 1;
 		    break;
@@ -325,8 +336,9 @@
 		    if (!Blind)
 			    Your("%s %s.",
 				 what ? what :
-				 (const char *)aobjnam (otmp, "softly glow"),
+				 (const char *)aobjnam(otmp, "softly glow"),
 				 hcolor(amber));
+		    update_inventory();
 		    break;
 	    case TROUBLE_POISONED:
 		    if (Hallucination)
@@ -342,9 +354,14 @@
 		    (void) encumber_msg();
 		    break;
 	    case TROUBLE_BLIND:
-		    Your("%s feel better.", makeplural(body_part(EYE)));
-		    make_blinded(0L,FALSE);
-		    break;
+	    	    {
+	    	    	int num_eyes = eyecount(youmonst.data);
+			Your("%s feel%s better.",
+			     (num_eyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)),
+			     (num_eyes == 1) ? "s" : "");
+			make_blinded(0L,FALSE);
+			break;
+		    }
 	    case TROUBLE_WOUNDED_LEGS:
 		    heal_legs();
 		    break;
@@ -437,7 +454,7 @@
 		summon_minion(resp_god, FALSE);
 		summon_minion(resp_god, FALSE);
 		summon_minion(resp_god, FALSE);
-		verbalize("Destroy %s, my servants!", him[flags.female]);
+		verbalize("Destroy %s, my servants!", uhim());
 	    }
 	}
 }
@@ -522,11 +539,180 @@
 	return;
 }
 
+/* helper to print "str appears at your feet", or appropriate */
+static void
+at_your_feet(str)
+	const char *str;
+{
+	if (Blind) str = Something;
+	if (u.uswallow) {
+	    /* barrier between you and the floor */
+	    pline("%s %s into %s %s.", str, vtense(str, "drop"),
+		  s_suffix(mon_nam(u.ustuck)), mbodypart(u.ustuck, STOMACH));
+	} else {
+	    pline("%s %s %s your %s!", str,
+		  Blind ? "lands" : vtense(str, "appear"),
+		  Levitation ? "beneath" : "at",
+		  makeplural(body_part(FOOT)));
+	}
+}
+
+#ifdef ELBERETH
+STATIC_OVL void
+gcrownu()
+{
+    struct obj *obj;
+    boolean already_exists, in_hand;
+    short class_gift;
+    int sp_no;
+#define ok_wep(o) ((o) && ((o)->oclass == WEAPON_CLASS || is_weptool(o)))
+
+    HSee_invisible |= FROMOUTSIDE;
+    HFire_resistance |= FROMOUTSIDE;
+    HCold_resistance |= FROMOUTSIDE;
+    HShock_resistance |= FROMOUTSIDE;
+    HSleep_resistance |= FROMOUTSIDE;
+    HPoison_resistance |= FROMOUTSIDE;
+    godvoice(u.ualign.type, (char *)0);
+
+    obj = ok_wep(uwep) ? uwep : 0;
+    already_exists = in_hand = FALSE;	/* lint suppression */
+    switch (u.ualign.type) {
+    case A_LAWFUL:
+	u.uevent.uhand_of_elbereth = 1;
+	verbalize("I crown thee...  The Hand of Elbereth!");
+	break;
+    case A_NEUTRAL:
+	u.uevent.uhand_of_elbereth = 2;
+	in_hand = (uwep && uwep->oartifact == ART_VORPAL_BLADE);
+	already_exists = exist_artifact(LONG_SWORD, artiname(ART_VORPAL_BLADE));
+	verbalize("Thou shalt be my Envoy of Balance!");
+	break;
+    case A_CHAOTIC:
+	u.uevent.uhand_of_elbereth = 3;
+	in_hand = (uwep && uwep->oartifact == ART_STORMBRINGER);
+	already_exists = exist_artifact(RUNESWORD, artiname(ART_STORMBRINGER));
+	verbalize("Thou art chosen to %s for My Glory!",
+		  already_exists && !in_hand ? "take lives" : "steal souls");
+	break;
+    }
+
+    class_gift = STRANGE_OBJECT;
+    /* 3.3.[01] had this in the A_NEUTRAL case below,
+       preventing chaotic wizards from receiving a spellbook */
+    if (Role_if(PM_WIZARD) &&
+	    (!uwep || (uwep->oartifact != ART_VORPAL_BLADE &&
+		       uwep->oartifact != ART_STORMBRINGER)) &&
+	    !carrying(SPE_FINGER_OF_DEATH)) {
+	class_gift = SPE_FINGER_OF_DEATH;
+ make_splbk:
+	obj = mksobj(class_gift, TRUE, FALSE);
+	bless(obj);
+	obj->bknown = TRUE;
+	at_your_feet("A spellbook");
+	dropy(obj);
+	u.ugifts++;
+	/* when getting a new book for known spell, enhance
+	   currently wielded weapon rather than the book */
+	for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
+	    if (spl_book[sp_no].sp_id == class_gift) {
+		if (ok_wep(uwep)) obj = uwep;	/* to be blessed,&c */
+		break;
+	    }
+    } else if (Role_if(PM_MONK) &&
+	    (!uwep || !uwep->oartifact) &&
+	    !carrying(SPE_RESTORE_ABILITY)) {
+	/* monks rarely wield a weapon */
+	class_gift = SPE_RESTORE_ABILITY;
+	goto make_splbk;
+    }
+
+    switch (u.ualign.type) {
+    case A_LAWFUL:
+	if (class_gift != STRANGE_OBJECT) {
+	    ;		/* already got bonus above */
+	} else if (obj && obj->otyp == LONG_SWORD && !obj->oartifact) {
+	    if (!Blind) Your("sword shines brightly for a moment.");
+	    obj = oname(obj, artiname(ART_EXCALIBUR));
+	    if (obj && obj->oartifact == ART_EXCALIBUR) u.ugifts++;
+	}
+	/* acquire Excalibur's skill regardless of weapon or gift */
+	unrestrict_weapon_skill(P_LONG_SWORD);
+	if (obj && obj->oartifact == ART_EXCALIBUR)
+	    discover_artifact(ART_EXCALIBUR);
+	break;
+    case A_NEUTRAL:
+	if (class_gift != STRANGE_OBJECT) {
+	    ;		/* already got bonus above */
+	} else if (in_hand) {
+	    Your("%s goes snicker-snack!", xname(obj));
+	    obj->dknown = TRUE;
+	} else if (!already_exists) {
+	    obj = mksobj(LONG_SWORD, FALSE, FALSE);
+	    obj = oname(obj, artiname(ART_VORPAL_BLADE));
+	    obj->spe = 1;
+	    at_your_feet("A sword");
+	    dropy(obj);
+	    u.ugifts++;
+	}
+	/* acquire Vorpal Blade's skill regardless of weapon or gift */
+	unrestrict_weapon_skill(P_LONG_SWORD);
+	if (obj && obj->oartifact == ART_VORPAL_BLADE)
+	    discover_artifact(ART_VORPAL_BLADE);
+	break;
+    case A_CHAOTIC:
+      {
+	char swordbuf[BUFSZ];
+
+	Sprintf(swordbuf, "%s sword", hcolor(Black));
+	if (class_gift != STRANGE_OBJECT) {
+	    ;		/* already got bonus above */
+	} else if (in_hand) {
+	    Your("%s hums ominously!", swordbuf);
+	    obj->dknown = TRUE;
+	} else if (!already_exists) {
+	    obj = mksobj(RUNESWORD, FALSE, FALSE);
+	    obj = oname(obj, artiname(ART_STORMBRINGER));
+	    at_your_feet(An(swordbuf));
+	    obj->spe = 1;
+	    dropy(obj);
+	    u.ugifts++;
+	}
+	/* acquire Stormbringer's skill regardless of weapon or gift */
+	unrestrict_weapon_skill(P_BROAD_SWORD);
+	if (obj && obj->oartifact == ART_STORMBRINGER)
+	    discover_artifact(ART_STORMBRINGER);
+	break;
+      }
+    default:
+	obj = 0;	/* lint */
+	break;
+    }
+
+    /* enhance weapon regardless of alignment or artifact status */
+    if (ok_wep(obj)) {
+	bless(obj);
+	obj->oeroded = obj->oeroded2 = 0;
+	obj->oerodeproof = TRUE;
+	obj->bknown = obj->rknown = TRUE;
+	if (obj->spe < 1) obj->spe = 1;
+	/* acquire skill in this weapon */
+	unrestrict_weapon_skill(weapon_type(obj));
+    } else if (class_gift == STRANGE_OBJECT) {
+	/* opportunity knocked, but there was nobody home... */
+	You_feel("unworthy.");
+    }
+    update_inventory();
+    return;
+}
+#endif	/*ELBERETH*/
+
 STATIC_OVL void
 pleased(g_align)
 	aligntyp g_align;
 {
-	int trouble = p_trouble;	/* what's your worst difficulty? */
+	/* don't use p_trouble, worst trouble may get fixed while praying */
+	int trouble = in_trouble();	/* what's your worst difficulty? */
 	int pat_on_head = 0, kick_on_butt;
 
 	You_feel("that %s is %s.", align_gname(g_align),
@@ -555,8 +741,10 @@
 	   If your luck is at least 0, then you are guaranteed rescued
 	   from your worst major problem. */
 
-	if (!trouble && u.ualign.record >= DEVOUT) pat_on_head = 1;
-	else {
+	if (!trouble && u.ualign.record >= DEVOUT) {
+	    /* if hero was in trouble, but got better, no special favor */
+	    if (p_trouble == 0) pat_on_head = 1;
+	} else {
 	    int action = rn1(on_altar() ? 3 + on_shrine() : 2, Luck+1);
 
 	    if (!on_altar()) action = max(action,2);
@@ -590,7 +778,7 @@
 		*repair_buf = '\0';
 		if (uwep->oeroded || uwep->oeroded2)
 		    Sprintf(repair_buf, " and %s now as good as new",
-			    uwep->quan == 1L ? "is" : "are");
+			    otense(uwep, "are"));
 
 		if (uwep->cursed) {
 		    uncurse(uwep);
@@ -623,6 +811,7 @@
 			Your("%s as good as new!",
 			     aobjnam(uwep, Blind ? "feel" : "look"));
 		}
+		update_inventory();
 	    }
 	    break;
 	case 3:
@@ -646,10 +835,18 @@
 	    /* Otherwise, falls into next case */
 	case 2:
 	    if (!Blind)
-		You("are surrounded by %s glow.",
-		    an(hcolor(golden)));
-	    if (Upolyd) u.mh = u.mhmax += 5;
-	    u.uhp = u.uhpmax += 5;
+		You("are surrounded by %s glow.", an(hcolor(golden)));
+	    /* if any levels have been lost (and not yet regained),
+	       treat this effect like blessed full healing */
+	    if (u.ulevel < u.ulevelmax) {
+		u.ulevelmax -= 1;	/* see potion.c */
+		pluslvl(FALSE);
+	    } else {
+		u.uhpmax += 5;
+		if (Upolyd) u.mhmax += 5;
+	    }
+	    u.uhp = u.uhpmax;
+	    if (Upolyd) u.mh = u.mhmax;
 	    ABASE(A_STR) = AMAX(A_STR);
 	    if (u.uhunger < 900) init_uhunger();
 	    if (u.uluck < 0) u.uluck = 0;
@@ -658,6 +855,7 @@
 	    break;
 	case 4: {
 	    register struct obj *otmp;
+	    int any = 0;
 
 	    if (Blind)
 		You_feel("the power of %s.", u_gname());
@@ -670,9 +868,11 @@
 			Your("%s %s.", aobjnam(otmp, "softly glow"),
 			     hcolor(amber));
 			otmp->bknown = TRUE;
+			++any;
 		    }
 		}
 	    }
+	    if (any) update_inventory();
 	    break;
 	}
 	case 5: {
@@ -703,133 +903,25 @@
 	case 9:		/* KMH -- can occur during full moons */
 #ifdef ELBERETH
 	    if (u.ualign.record >= PIOUS && !u.uevent.uhand_of_elbereth) {
-		register struct obj *obj = uwep;	/* to be blessed */
-		boolean already_exists, in_hand;
-		const char *dropped_item;
-		int sp_no;
-
-		HSee_invisible |= FROMOUTSIDE;
-		HFire_resistance |= FROMOUTSIDE;
-		HCold_resistance |= FROMOUTSIDE;
-		HPoison_resistance |= FROMOUTSIDE;
-		godvoice(u.ualign.type,(char *)0);
-
-		switch(u.ualign.type) {
-		case A_LAWFUL:
-		    u.uevent.uhand_of_elbereth = 1;
-		    verbalize("I crown thee...      The Hand of Elbereth!");
-		    if (obj && (obj->otyp == LONG_SWORD) && !obj->oartifact) {
-			obj = oname(obj, artiname(ART_EXCALIBUR));
-			if (obj && obj->oartifact == ART_EXCALIBUR) u.ugifts++;
-		    }
-		    /* acquire this skill regardless of weapon */
-		    unrestrict_weapon_skill(P_LONG_SWORD);
-		    if (obj && obj->oartifact == ART_EXCALIBUR)
-			discover_artifact(ART_EXCALIBUR);
-		    break;
-		case A_NEUTRAL:
-		    u.uevent.uhand_of_elbereth = 2;
-		    verbalize("Thou shalt be my Envoy of Balance!");
-		    dropped_item = 0;
-		    if (uwep && uwep->oartifact == ART_VORPAL_BLADE) {
-			obj = uwep;	/* to be blessed and rustproofed */
-			Your("%s goes snicker-snack!", xname(obj));
-			obj->dknown = TRUE;
-		    } else if (Role_if(PM_WIZARD) &&
-			    !carrying(SPE_FINGER_OF_DEATH)) {
-			obj = mksobj(SPE_FINGER_OF_DEATH, TRUE, FALSE);
-			bless(obj);
-			dropped_item = "A spellbook appears";
-		    } else if (!exist_artifact(LONG_SWORD,
-					       artiname(ART_VORPAL_BLADE))) {
-			obj = mksobj(LONG_SWORD, FALSE, FALSE);
-			obj = oname(obj, artiname(ART_VORPAL_BLADE));
-			obj->spe = 1;
-			dropped_item = "A sword appears";
-		    }
-		    if (dropped_item) {
-			if (Blind) dropped_item = "Something lands";
-			pline("%s %s your %s!", dropped_item,
-			      Levitation ? "beneath" : "at",
-			      makeplural(body_part(FOOT)));
-			dropy(obj);
-			u.ugifts++;
-		    }
-		    /* acquire this skill regardless of weapon (or book) */
-		    unrestrict_weapon_skill(P_LONG_SWORD);
-		    if (obj && obj->oartifact == ART_VORPAL_BLADE)
-			discover_artifact(ART_VORPAL_BLADE);
-		    /* when getting a new book for known spell, enhance
-		       currently wielded weapon rather than the book */
-		    if (obj && obj->otyp == SPE_FINGER_OF_DEATH) {
-			for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
-			    if (spl_book[sp_no].sp_id == SPE_FINGER_OF_DEATH) {
-				if (uwep) obj = uwep;	/* to be blessed,&c */
-				break;
-			    }
-		    }
-		    break;
-		case A_CHAOTIC:
-		    u.uevent.uhand_of_elbereth = 3;
-		    in_hand = (uwep && uwep->oartifact == ART_STORMBRINGER);
-		    already_exists = exist_artifact(RUNESWORD,
-						artiname(ART_STORMBRINGER));
-		    verbalize("Thou art chosen to %s for My Glory!",
-			      already_exists && !in_hand ?
-			      "take lives" : "steal souls");
-		    if (in_hand) {
-			obj = uwep;	/* to be blessed and rustproofed */
-		    } else if (!already_exists) {
-			obj = mksobj(RUNESWORD, FALSE, FALSE);
-			obj = oname(obj, artiname(ART_STORMBRINGER));
-			pline("%s %s %s your %s!", Blind ? Something :
-			      An(hcolor(Black)),
-			      Blind ? "lands" : "sword appears",
-			      Levitation ? "beneath" : "at",
-			      makeplural(body_part(FOOT)));
-			obj->spe = 1;
-			dropy(obj);
-			u.ugifts++;
-		    }
-		    /* acquire this skill regardless of weapon */
-		    unrestrict_weapon_skill(P_BROAD_SWORD);
-		    if (obj && obj->oartifact == ART_STORMBRINGER)
-			discover_artifact(ART_STORMBRINGER);
-		    break;
-		default:
-		    obj = 0;	/* lint */
-		    break;
-		}
-		/* enhance weapon regardless of alignment or artifact status */
-		if (obj && (obj->oclass == WEAPON_CLASS || is_weptool(obj))) {
-		    bless(obj);
-		    obj->oeroded = obj->oeroded2 = 0;
-		    obj->oerodeproof = TRUE;
-		    obj->bknown = obj->rknown = TRUE;
-		    if (obj->spe < 1) obj->spe = 1;
-		    /* acquire skill in this weapon */
-		    unrestrict_weapon_skill(weapon_type(obj));
-		} else if (obj && (obj->oclass == SPBOOK_CLASS)) {
-		    obj->bknown = TRUE;
-		} else	/* opportunity knocked, but there was nobody home... */
-		    You_feel("unworthy.");
+		gcrownu();
 		break;
-	    }
+	    } /* else FALLTHRU */
 #endif	/*ELBERETH*/
-
 	case 6:	{
 	    struct obj *otmp;
 	    int sp_no, trycnt = u.ulevel + 1;
 
-	    pline("An object appears at your %s!",
-		  makeplural(body_part(FOOT)));
+	    at_your_feet("An object");
 	    /* not yet known spells given preference over already known ones */
+	    /* Also, try to grant a spell for which there is a skill slot */
 	    otmp = mkobj(SPBOOK_CLASS, TRUE);
 	    while (--trycnt > 0) {
 		if (otmp->otyp != SPE_BLANK_PAPER) {
 		    for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
 			if (spl_book[sp_no].sp_id == otmp->otyp) break;
-		    if (sp_no == MAXSPELL) break;	/* not yet known */
+		    if (sp_no == MAXSPELL &&
+			!P_RESTRICTED(spell_skilltype(otmp->otyp)))
+			break;	/* usable, but not yet known */
 		} else {
 		    if (!objects[SPE_BLANK_PAPER].oc_name_known ||
 			    carrying(MAGIC_MARKER)) break;
@@ -867,7 +959,8 @@
 
     for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
 	/* turn water into (un)holy water */
-	if (otmp->otyp == POT_WATER && (boolean)otmp->blessed != bless_water) {
+	if (otmp->otyp == POT_WATER &&
+		(bless_water ? !otmp->blessed : !otmp->cursed)) {
 	    otmp->blessed = bless_water;
 	    otmp->cursed = !bless_water;
 	    otmp->bknown = bc_known;
@@ -1089,7 +1182,7 @@
     } /* corpse */
 
     if (otmp->otyp == AMULET_OF_YENDOR) {
-	if (!In_endgame(&u.uz)) {
+	if (!Is_astralevel(&u.uz)) {
 	    if (Hallucination)
 		    You_feel("homesick.");
 	    else
@@ -1294,14 +1387,15 @@
 	    /* you were already in pretty good standing */
 	    /* The player can gain an artifact */
 	    /* The chance goes down as the number of artifacts goes up */
-	    if (u.ulevel > 2 && !rn2(10 + (2 * u.ugifts * nartifacts))) {
+	    if (u.ulevel > 2 && u.uluck >= 0 &&
+		!rn2(10 + (2 * u.ugifts * nartifacts))) {
 		otmp = mk_artifact((struct obj *)0, a_align(u.ux,u.uy));
 		if (otmp) {
 		    if (otmp->spe < 0) otmp->spe = 0;
 		    if (otmp->cursed) uncurse(otmp);
+		    otmp->oerodeproof = TRUE;
 		    dropy(otmp);
-		    pline("An object appears at your %s!",
-			  makeplural(body_part(FOOT)));
+		    at_your_feet("An object");
 		    godvoice(u.ualign.type, "Use my gift wisely!");
 		    u.ugifts++;
 		    u.ublesscnt = rnz(300 + (50 * nartifacts));
@@ -1313,6 +1407,7 @@
 		}
 	    }
 	    change_luck((value * LUCKMAX) / (MAXVALUE * 2));
+	    if ((int)u.uluck < 0) u.uluck = 0;
 	    if (u.uluck != saved_luck) {
 		if (Blind)
 		    You("think %s brushed your %s.",something, body_part(FOOT));
@@ -1379,11 +1474,17 @@
 int
 dopray()
 {
-	/* Confirm accidental slips of Alt-P */
-	if (flags.prayconfirm)
-		if (yn("Are you sure you want to pray?") == 'n')
-			return (0);
-	u.uconduct.gnostic++;
+    /* Confirm accidental slips of Alt-P */
+    if (flags.prayconfirm)
+	if (yn("Are you sure you want to pray?") == 'n')
+	    return 0;
+
+    u.uconduct.gnostic++;
+    /* Praying implies that the hero is conscious and since we have
+       no deafness attribute this implies that all verbalized messages
+       can be heard.  So, in case the player has used the 'O' command
+       to toggle this accessible flag off, force it to be on. */
+    flags.soundok = 1;
 
     /* set up p_type and p_alignment */
     if (!can_pray(TRUE)) return 0;
@@ -1489,6 +1590,8 @@
 		You("don't know how to turn undead!");
 		return(0);
 	}
+	u.uconduct.gnostic++;
+
 	if ((u.ualign.type != A_CHAOTIC &&
 		    (is_demon(youmonst.data) || is_undead(youmonst.data))) ||
 				u.ugangr > 6 /* "Die, mortal!" */) {
@@ -1506,7 +1609,6 @@
 	}
 	pline("Calling upon %s, you chant an arcane formula.", u_gname());
 	exercise(A_WIS, TRUE);
-	u.uconduct.gnostic++;
 
 	/* note: does not perform unturn_dead() on victims' inventories */
 	range = BOLT_LIM + (u.ulevel / 5);	/* 5 to 11 */
@@ -1534,23 +1636,25 @@
 			switch (mtmp->data->mlet) {
 			    /* this is intentional, lichs are tougher
 			       than zombies. */
-			case S_LICH:    xlev += 2;
-			case S_GHOST:   xlev += 2;
-			case S_VAMPIRE: xlev += 2;
-			case S_WRAITH:  xlev += 2;
-			case S_MUMMY:   xlev += 2;
+			case S_LICH:    xlev += 2;  /*FALLTHRU*/
+			case S_GHOST:   xlev += 2;  /*FALLTHRU*/
+			case S_VAMPIRE: xlev += 2;  /*FALLTHRU*/
+			case S_WRAITH:  xlev += 2;  /*FALLTHRU*/
+			case S_MUMMY:   xlev += 2;  /*FALLTHRU*/
 			case S_ZOMBIE:
-			    mtmp->mflee = 1;	/* at least */
-			    if(u.ulevel >= xlev &&
-			       !resist(mtmp, '\0', 0, NOTELL)) {
-				if(u.ualign.type == A_CHAOTIC) {
+			    if (u.ulevel >= xlev &&
+				    !resist(mtmp, '\0', 0, NOTELL)) {
+				if (u.ualign.type == A_CHAOTIC) {
 				    mtmp->mpeaceful = 1;
+				    set_malign(mtmp);
 				} else { /* damn them */
 				    killed(mtmp);
 				}
-			    }
-			    break;
-			default:    mtmp->mflee = 1;
+				break;
+			    } /* else flee */
+			    /*FALLTHRU*/
+			default:
+			    monflee(mtmp, 0, FALSE, TRUE);
 			    break;
 			}
 		    }
diff -Naurd ../nethack-3.3.1/src/priest.c ./src/priest.c
--- ../nethack-3.3.1/src/priest.c Sun Jul 16 02:49:28 2000
+++ ./src/priest.c Fri Mar 22 14:41:06 2002
@@ -8,6 +8,9 @@
 #include "epri.h"
 #include "emin.h"
 
+/* this matches the categorizations shown by enlightenment */
+#define ALGN_SINNED	(-4)	/* worse than strayed */
+
 #ifdef OVLB
 
 STATIC_DCL boolean FDECL(histemple_at,(struct monst *,XCHAR_P,XCHAR_P));
@@ -190,9 +193,9 @@
 int sx, sy;
 boolean sanctum;   /* is it the seat of the high priest? */
 {
-	register struct monst *priest;
-	register struct obj *otmp;
-	register int cnt;
+	struct monst *priest;
+	struct obj *otmp;
+	int cnt;
 
 	if(MON_AT(sx+1, sy))
 		rloc(m_at(sx+1, sy)); /* insurance */
@@ -216,25 +219,17 @@
 		     on_level(&sanctum_level, &u.uz)) {
 			(void) mongets(priest, AMULET_OF_YENDOR);
 		}
-		/* Do NOT put the rest in m_initinv.    */
-		/* Priests created elsewhere than in a  */
-		/* temple should not carry these items, */
-		cnt = rn1(2,3);
-		while(cnt) {
-		    otmp = mkobj(SPBOOK_CLASS, FALSE);
-		    if(otmp) (void) mpickobj(priest, otmp);
-		    cnt--;
+		/* 2 to 4 spellbooks */
+		for (cnt = rn1(3,2); cnt > 0; --cnt) {
+		    (void) mpickobj(priest, mkobj(SPBOOK_CLASS, FALSE));
 		}
-		if(p_coaligned(priest))
-		    (void) mongets(priest, ROBE);
-		else {
-		    otmp = mksobj(ROBE, TRUE, FALSE);
-		    if(otmp) {
-			if(!rn2(2)) curse(otmp);
-			(void) mpickobj(priest, otmp);
-		    }
+		/* robe [via makemon()] */
+		if (rn2(2) && (otmp = which_armor(priest, W_ARMC)) != 0) {
+		    if (p_coaligned(priest))
+			uncurse(otmp);
+		    else
+			curse(otmp);
 		}
-		m_dowear(priest, TRUE);
 	}
 }
 
@@ -368,8 +363,8 @@
 		}
 		if(!sanctum) {
 		    /* !tended -> !shrined */
-		    if(!shrined || !p_coaligned(priest) ||
-						   u.ualign.record < -5)
+		    if (!shrined || !p_coaligned(priest) ||
+			    u.ualign.record <= ALGN_SINNED)
 			You("have a%s forbidding feeling...",
 				(!shrined) ? "" : " strange");
 		    else You("experience a strange sense of peace.");
@@ -405,7 +400,6 @@
 	boolean coaligned = p_coaligned(priest);
 	boolean strayed = (u.ualign.record < 0);
 
-
 	/* KMH, conduct */
 	u.uconduct.gnostic++;
 
@@ -427,7 +421,7 @@
 
 	    if(!priest->mcanmove || priest->msleeping) {
 		pline("%s breaks out of %s reverie!",
-		      Monnam(priest), his[pronoun_gender(priest)]);
+		      Monnam(priest), mhis(priest));
 		priest->mfrozen = priest->msleeping = 0;
 		priest->mcanmove = 1;
 	    }
@@ -443,7 +437,6 @@
 	    priest->mpeaceful = 0;
 	    return;
 	}
-
 	if(!u.ugold) {
 	    if(coaligned && !strayed) {
 		if (priest->mgold > 0L) {
@@ -480,7 +473,8 @@
 	    } else if(offer < (u.ulevel * 400)) {
 		verbalize("Thou art indeed a pious individual.");
 		if(u.ugold < (offer * 2L)) {
-		    if(coaligned && u.ualign.record < -5) adjalign(1);
+		    if (coaligned && u.ualign.record <= ALGN_SINNED)
+			adjalign(1);
 		    verbalize("I bestow upon thee a blessing.");
 		    incr_itimeout(&HClairvoyant, rn1(500,500));
 		}
@@ -565,7 +559,7 @@
 	    if (is_minion(mon->data) || is_rider(mon->data)) return FALSE;
 	    x = mon->mx, y = mon->my;
 	}
-	if (u.ualign.record < -3)		/* sinned or worse */
+	if (u.ualign.record <= ALGN_SINNED)	/* sinned or worse */
 	    return FALSE;
 	if ((roomno = temple_occupied(u.urooms)) == 0 ||
 		roomno != *in_rooms(x, y, TEMPLE))
diff -Naurd ../nethack-3.3.1/src/quest.c ./src/quest.c
--- ../nethack-3.3.1/src/quest.c Mon May 8 21:58:07 2000
+++ ./src/quest.c Fri Mar 22 14:40:55 2002
@@ -152,6 +152,7 @@
 		  !seal ? 1 : -1;
     schedule_goto(dest, FALSE, FALSE, portal_flag, (char *)0, (char *)0);
     if (seal) {	/* remove the portal to the quest - sealing it off */
+	int reexpelled = u.uevent.qexpelled;
 	u.uevent.qexpelled = 1;
 	/* Delete the near portal now; the far (main dungeon side)
 	   portal will be deleted as part of arrival on that level.
@@ -160,7 +161,7 @@
 	for (t = ftrap; t; t = t->ntrap)
 	    if (t->ttyp == MAGIC_PORTAL) break;
 	if (t) deltrap(t);	/* (display might be briefly out of sync) */
-	else impossible("quest portal already gone?");
+	else if (!reexpelled) impossible("quest portal already gone?");
     }
 }
 
@@ -194,6 +195,7 @@
 	    /* behave as if leader imparts sufficient info about the
 	       quest artifact */
 	    fully_identify_obj(obj);
+	    update_inventory();
 	}
 }
 
@@ -293,7 +295,8 @@
 {
 	if(!Qstat(in_battle)) {
 	  if(u.uhave.questart) qt_pager(QT_NEMWANTSIT);
-	  else if(Qstat(made_goal) == 1) qt_pager(QT_FIRSTNEMESIS);
+	  else if(Qstat(made_goal) == 1 || !Qstat(met_nemesis))
+	      qt_pager(QT_FIRSTNEMESIS);
 	  else if(Qstat(made_goal) < 4) qt_pager(QT_NEXTNEMESIS);
 	  else if(Qstat(made_goal) < 7) qt_pager(QT_OTHERNEMESIS);
 	  else if(!rn2(5))	qt_pager(rn1(10, QT_DISCOURAGE));
@@ -307,7 +310,10 @@
 chat_with_guardian()
 {
 /*	These guys/gals really don't have much to say... */
-	qt_pager(rn1(5, QT_GUARDTALK));
+	if (u.uhave.questart && Qstat(killed_nemesis))
+	    qt_pager(rn1(5, QT_GUARDTALK2));
+	else
+	    qt_pager(rn1(5, QT_GUARDTALK));
 }
 
 STATIC_OVL void
@@ -336,8 +342,11 @@
 quest_chat(mtmp)
 	register struct monst *mtmp;
 {
+    if (mtmp->m_id == Qstat(leader_m_id)) {
+	chat_with_leader();
+	return;
+    }
     switch(mtmp->data->msound) {
-	    case MS_LEADER:	chat_with_leader(); break;
 	    case MS_NEMESIS:	chat_with_nemesis(); break;
 	    case MS_GUARDIAN:	chat_with_guardian(); break;
 	    default:	impossible("quest_chat: Unknown quest character %s.",
@@ -349,8 +358,11 @@
 quest_talk(mtmp)
 	register struct monst *mtmp;
 {
+    if (mtmp->m_id == Qstat(leader_m_id)) {
+	leader_speaks(mtmp);
+	return;
+    }
     switch(mtmp->data->msound) {
-	    case MS_LEADER:	leader_speaks(mtmp); break;
 	    case MS_NEMESIS:	nemesis_speaks(); break;
 	    case MS_DJINNI:	prisoner_speaks(mtmp); break;
 	    default:		break;
diff -Naurd ../nethack-3.3.1/src/questpgr.c ./src/questpgr.c
--- ../nethack-3.3.1/src/questpgr.c Mon May 8 21:58:07 2000
+++ ./src/questpgr.c Fri Mar 22 14:40:55 2002
@@ -209,15 +209,6 @@
 	return(urole.homebase);
 }
 
-boolean
-leaderless()	/* return true iff leader is dead */
-{
-	int i = urole.ldrnum;
-	/* BUG: This doesn't take the possibility of resurrection
-		via wand or spell of undead turning into account. */
-	return (boolean)(mvitals[i].died > 0);
-}
-
 STATIC_OVL struct qtmsg *
 msg_in(qtm_list, msgnum)
 struct qtmsg *qtm_list;
diff -Naurd ../nethack-3.3.1/src/read.c ./src/read.c
--- ../nethack-3.3.1/src/read.c Sat Jul 22 23:04:06 2000
+++ ./src/read.c Fri Mar 22 14:40:55 2002
@@ -38,6 +38,7 @@
 static void FDECL(randomize,(int *, int));
 static void FDECL(forget_single_object, (int));
 static void FDECL(forget, (int));
+static void FDECL(maybe_tame, (struct monst *,struct obj *));
 
 STATIC_PTR void FDECL(set_lit, (int,int,genericptr_t));
 
@@ -62,6 +63,24 @@
 	    return(1);
 #ifdef TOURIST
 	} else if (scroll->otyp == T_SHIRT) {
+	    static const char *shirt_msgs[] = { /* Scott Bigham */
+    "I explored the Dungeons of Doom and all I got was this lousy T-shirt!",
+    "Is that Mjollnir in your pocket or are you just happy to see me?",
+    "It's not the size of your sword, it's how #enhance'd you are with it.",
+    "Madame Elvira's House O' Succubi Lifetime Customer",
+    "Madame Elvira's House O' Succubi Employee of the Month",
+    "Ludios Vault Guards Do It In Small, Dark Rooms",
+    "Yendor Military Soldiers Do It In Large Groups",
+    "I survived Yendor Military Boot Camp",
+    "Ludios Accounting School Intra-Mural Lacrosse Team",
+    "Oracle(TM) Fountains 10th Annual Wet T-Shirt Contest",
+    "Hey, black dragon!  Disintegrate THIS!",
+    "I'm With Stupid -->",
+    "Don't blame me, I voted for Izchak!",
+    "Don't Panic",				/* HHGTTG */
+    "Furinkan High School Athletic Dept.",	/* Ranma 1/2 */
+    "Hel-LOOO, Nurse!",			/* Animaniacs */
+	    };
 	    char buf[BUFSZ];
 	    int erosion;
 
@@ -72,13 +91,7 @@
 	    u.uconduct.literate++;
 	    if(flags.verbose)
 		pline("It reads:");
-	    Sprintf(buf,  "I explored the Dungeons of Doom, %s.",
-		    Hallucination ?
-			(scroll == uarmu ?
-			    /* (force these two to have identical length) */
-			    "and never did any laundry..." :
-			    "and couldn't find my way out") :
-			"but all I got was this lousy T-shirt");
+	    Strcpy(buf, shirt_msgs[scroll->o_id % SIZE(shirt_msgs)]);
 	    erosion = greatest_erosion(scroll);
 	    if (erosion)
 		wipeout_text(buf,
@@ -123,14 +136,16 @@
 	scroll->in_use = TRUE;	/* scroll, not spellbook, now being read */
 	if(scroll->otyp != SCR_BLANK_PAPER) {
 	  if(Blind)
-	    pline("As you pronounce the formula on it, the scroll disappears.");
+	    pline("As you %s the formula on it, the scroll disappears.",
+			is_silent(youmonst.data) ? "cogitate" : "pronounce");
 	  else
 	    pline("As you read the scroll, it disappears.");
 	  if(confused) {
 	    if (Hallucination)
 		pline("Being so trippy, you screw up...");
 	    else
-		pline("Being confused, you mispronounce the magic words...");
+		pline("Being confused, you mis%s the magic words...",
+			is_silent(youmonst.data) ? "understand" : "pronounce");
 	  }
 	}
 	if(!seffects(scroll))  {
@@ -158,7 +173,7 @@
 		    obj->spe = 0;
 		    if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN)
 			obj->age = 0;
-		    Your("%s vibrates briefly.",xname(obj));
+		    Your("%s %s briefly.",xname(obj), otense(obj, "vibrate"));
 		} else pline(nothing_happens);
 	}
 }
@@ -168,7 +183,7 @@
 register struct obj	*otmp;
 {
 	Your("%s %s briefly.", xname(otmp),
-		Blind ? "vibrates" : "glows");
+	     otense(otmp, Blind ? "vibrate" : "glow"));
 }
 
 static void
@@ -176,10 +191,11 @@
 register struct obj	*otmp;
 register const char *color;
 {
-	Your("%s %s%s for a moment.",
+	Your("%s %s%s%s for a moment.",
 		xname(otmp),
-		Blind ? "vibrates" : "glows ",
-		Blind ? (const char *)"" : hcolor(color));
+		otense(otmp, Blind ? "vibrate" : "glow"),
+		Blind ? "" : " ",
+		Blind ? nul : hcolor(color));
 }
 
 /* Is the object chargeable?  For purposes of inventory display; it is */
@@ -270,8 +286,8 @@
 
 	    /* destruction depends on current state, not adjustment */
 	    if (obj->spe > rn2(7) || obj->spe <= -5) {
-		Your("%s pulsates momentarily, then explodes!",
-		     xname(obj));
+		Your("%s %s momentarily, then %s!",
+		     xname(obj), otense(obj,"pulsate"), otense(obj,"explode"));
 		if (is_on) Ring_gone(obj);
 		s = rnd(3 * abs(obj->spe));	/* amount of damage */
 		useup(obj);
@@ -350,7 +366,7 @@
 		    stripspe(obj);
 		    if (obj->lamplit) {
 			if (!Blind)
-			    pline("%s goes out!", The(xname(obj)));
+			    pline("%s out!", Tobjnam(obj, "go"));
 			end_burn(obj, TRUE);
 		    }
 		} else if (is_blessed) {
@@ -430,12 +446,11 @@
 	objects[obj_id].oc_name_known = 0;
 	objects[obj_id].oc_pre_discovered = 0;	/* a discovery when relearned */
 	if (objects[obj_id].oc_uname) {
-	    /* this only works if oc_name_known is false */
-	    undiscover_object(obj_id);
-
 	    free((genericptr_t)objects[obj_id].oc_uname);
 	    objects[obj_id].oc_uname = 0;
 	}
+	undiscover_object(obj_id);	/* after clearing oc_name_known */
+
 	/* clear & free object names from matching inventory items too? */
 }
 
@@ -623,6 +638,22 @@
 	docrt();		/* this correctly will reset vision */
 }
 
+/* monster is hit by scroll of taming's effect */
+static void
+maybe_tame(mtmp, sobj)
+struct monst *mtmp;
+struct obj *sobj;
+{
+	if (sobj->cursed) {
+	    setmangry(mtmp);
+	} else {
+	    if (mtmp->isshk)
+		make_happy_shk(mtmp, FALSE);
+	    else if (!resist(mtmp, sobj->oclass, 0, NOTELL))
+		(void) tamedog(mtmp, (struct obj *) 0);
+	}
+}
+
 int
 seffects(sobj)
 register struct obj	*sobj;
@@ -664,26 +695,28 @@
 			otmp->oerodeproof = !(sobj->cursed);
 			if(Blind) {
 			    otmp->rknown = FALSE;
-			    Your("%s feels warm for a moment.",
-				xname(otmp));
+			    Your("%s %s warm for a moment.",
+				xname(otmp), otense(otmp, "feel"));
 			} else {
 			    otmp->rknown = TRUE;
-			    Your("%s is covered by a %s %s %s!",
-				xname(otmp),
+			    Your("%s %s covered by a %s %s %s!",
+				xname(otmp), otense(otmp, "are"),
 				sobj->cursed ? "mottled" : "shimmering",
 				hcolor(sobj->cursed ? Black : golden),
 				sobj->cursed ? "glow" :
 				  (is_shield(otmp) ? "layer" : "shield"));
 			}
-			if (otmp->oerodeproof && (otmp->oeroded || otmp->oeroded2)) {
+			if (otmp->oerodeproof &&
+			    (otmp->oeroded || otmp->oeroded2)) {
 			    otmp->oeroded = otmp->oeroded2 = 0;
 			    Your("%s %s as good as new!",
-				 xname(otmp), Blind ? "feels" : "looks");
+				 xname(otmp),
+				 otense(otmp, Blind ? "feel" : "look"));
 			}
 			break;
 		}
 		special_armor = is_elven_armor(otmp) ||
-				(Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM);
+			(Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM);
 		if (sobj->cursed)
 		    same_color =
 			(otmp->otyp == BLACK_DRAGON_SCALE_MAIL ||
@@ -698,11 +731,13 @@
 		/* KMH -- catch underflow */
 		s = sobj->cursed ? -otmp->spe : otmp->spe;
 		if (s > (special_armor ? 5 : 3) && rn2(s)) {
-		Your("%s violently %s%s%s for a while, then evaporates.",
-			    xname(otmp),
-			    Blind ? "vibrates" : "glows",
-			    (!Blind && !same_color) ? " " : nul,
-			    (Blind || same_color) ? nul : hcolor(sobj->cursed ? Black : silver));
+		Your("%s violently %s%s%s for a while, then %s.",
+		     xname(otmp),
+		     otense(otmp, Blind ? "vibrate" : "glow"),
+		     (!Blind && !same_color) ? " " : nul,
+		     (Blind || same_color) ? nul :
+			hcolor(sobj->cursed ? Black : silver),
+		     otense(otmp, "evaporate"));
 			if(is_cloak(otmp)) (void) Cloak_off();
 			if(is_boots(otmp)) (void) Boots_off();
 			if(is_helmet(otmp)) (void) Helmet_off();
@@ -735,7 +770,7 @@
 		Your("%s %s%s%s%s for a %s.",
 			xname(otmp),
 		        s == 0 ? "violently " : nul,
-			Blind ? "vibrates" : "glows",
+			otense(otmp, Blind ? "vibrate" : "glow"),
 			(!Blind && !same_color) ? " " : nul,
 			(Blind || same_color) ? nul : hcolor(sobj->cursed ? Black : silver),
 			  (s*s>1) ? "while" : "moment");
@@ -750,8 +785,8 @@
 
 		if ((otmp->spe > (special_armor ? 5 : 3)) &&
 		    (special_armor || !rn2(7)))
-			Your("%s suddenly vibrates %s.",
-				xname(otmp),
+			Your("%s suddenly %s %s.",
+				xname(otmp), otense(otmp, "vibrate"),
 				Blind ? "again" : "unexpectedly");
 		break;
 	    }
@@ -778,7 +813,7 @@
 		    } else
 			known = TRUE;
 		} else {	/* armor and scroll both cursed */
-		    Your("%s vibrates.", xname(otmp));
+		    Your("%s %s.", xname(otmp), otense(otmp, "vibrate"));
 		    if (otmp->spe >= -6) otmp->spe--;
 		    make_stunned(HStun + rn1(10, 10), TRUE);
 		}
@@ -821,7 +856,11 @@
 				makeplural(body_part(HAND)),
 				u.umconf ? "n even more" : "",
 				hcolor(red));
-			u.umconf += rn1(8, 2);
+			/* after a while, repeated uses become less effective */
+			if (u.umconf >= 40)
+			    u.umconf++;
+			else
+			    u.umconf += rn1(8, 2);
 		    }
 		}
 		break;
@@ -838,7 +877,7 @@
 			    mtmp->mcanmove = 1;
 			} else
 			    if (! resist(mtmp, sobj->oclass, 0, NOTELL))
-				mtmp->mflee = 1;
+				monflee(mtmp, 0, FALSE, FALSE);
 			if(!mtmp->mtame) ct++;	/* pets don't laugh at you */
 		    }
 		}
@@ -873,16 +912,21 @@
 		    else
 			You_feel("like someone is helping you.");
 
-		if(sobj->cursed) pline_The("scroll disintegrates.");
-		else {
-		    for(obj = invent; obj ; obj = obj->nobj)
-			if(sobj->blessed || obj->owornmask ||
-			   (obj->otyp == LOADSTONE)) {
+		if (sobj->cursed) {
+		    pline_The("scroll disintegrates.");
+		} else {
+		    for (obj = invent; obj; obj = obj->nobj)
+			if (sobj->blessed ||
+			     (obj->owornmask &&
+			       ((obj->owornmask & ~W_SWAPWEP) || u.twoweap)) ||
+			     obj->otyp == LOADSTONE ||
+			     (obj->otyp == LEASH && obj->leashmon)) {
 			    if(confused) blessorcurse(obj, 2);
 			    else uncurse(obj);
 			}
 		}
 		if(Punished && !confused) unpunish();
+		update_inventory();
 		break;
 	    }
 	case SCR_CREATE_MONSTER:
@@ -924,23 +968,19 @@
 		break;
 	case SCR_TAMING:
 	case SPE_CHARM_MONSTER:
-	    {	register int i,j;
-		register int bd = confused ? 5 : 1;
-		register struct monst *mtmp;
+		if (u.uswallow) {
+		    maybe_tame(u.ustuck, sobj);
+		} else {
+		    int i, j, bd = confused ? 5 : 1;
+		    struct monst *mtmp;
 
-		for(i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++)
-		if(isok(u.ux+i, u.uy+j) && (mtmp = m_at(u.ux+i, u.uy+j))) {
-		    if(sobj->cursed) {
-			setmangry(mtmp);
-		    } else {
-			if (mtmp->isshk)
-			    make_happy_shk(mtmp, FALSE);
-			else if (!resist(mtmp, sobj->oclass, 0, NOTELL))
-			    (void) tamedog(mtmp, (struct obj *) 0);
+		    for (i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++) {
+			if (!isok(u.ux + i, u.uy + j)) continue;
+			if ((mtmp = m_at(u.ux + i, u.uy + j)) != 0)
+			    maybe_tame(mtmp, sobj);
 		    }
 		}
 		break;
-	    }
 	case SCR_GENOCIDE:
 		You("have found a scroll of genocide!");
 		known = TRUE;
@@ -987,6 +1027,7 @@
 			/* Note: if rn2(5)==0, identify all items */
 			if (cval == 1 && sobj->blessed && Luck > 0) ++cval;
 		} else	cval = 1;
+		if(!objects[sobj->otyp].oc_name_known) more_experienced(0,10);
 		useup(sobj);
 		makeknown(SCR_IDENTIFY);
 	id:
@@ -1065,6 +1106,7 @@
 		 * some damage under all potential cases.
 		 */
 		cval = bcsign(sobj);
+		if(!objects[sobj->otyp].oc_name_known) more_experienced(0,10);
 		useup(sobj);
 		makeknown(SCR_FIRE);
 		if(confused) {
@@ -1089,7 +1131,7 @@
 		    burn_away_slime();
 		}
 		explode(u.ux, u.uy, 11, (2*(rn1(3, 3) + 2 * cval) + 1)/3,
-							SCROLL_CLASS);
+							SCROLL_CLASS, EXPL_FIERY);
 		return(1);
 	case SCR_EARTH:
 	    /* TODO: handle steeds */
@@ -1153,7 +1195,7 @@
 					if (canspotmon(mtmp))
 					    pline("%s's %s does not protect %s.",
 						Monnam(mtmp), xname(helmet),
-						him[pronoun_gender(mtmp)]);
+						mhim(mtmp));
 				    }
 				}
 	    	    	    	mtmp->mhp -= mdmg;
@@ -1286,7 +1328,11 @@
 			pline("It seems even darker in here than before.");
 			return;
 		    }
-		    You("are surrounded by darkness!");
+		    if (uwep && artifact_light(uwep) && uwep->lamplit)
+			pline("Suddenly, the only light left comes from %s!",
+				the(xname(uwep)));
+		    else
+			You("are surrounded by darkness!");
 		}
 
 		/* the magic douses lamps, et al, too */
@@ -1298,8 +1344,9 @@
 		if (Blind) goto do_it;
 		if(u.uswallow){
 			if (is_animal(u.ustuck->data))
-				pline("%s stomach is lit.",
-				         s_suffix(Monnam(u.ustuck)));
+				pline("%s %s is lit.",
+				        s_suffix(Monnam(u.ustuck)),
+					mbodypart(u.ustuck, STOMACH));
 			else
 				if (is_whirly(u.ustuck->data))
 					pline("%s shines briefly.",
@@ -1376,6 +1423,9 @@
 			buf);
 		    (void)mungspaces(buf);
 		} while (buf[0]=='\033' || !buf[0]);
+		/* choosing "none" preserves genocideless conduct */
+		if (!strcmpi(buf, "none")) return;
+
 		if (strlen(buf) == 1) {
 		    if (buf[0] == ILLOBJ_SYM)
 			buf[0] = def_monsyms[S_MIMIC];
@@ -1499,12 +1549,14 @@
 
 #define REALLY 1
 #define PLAYER 2
+#define ONTHRONE 4
 void
 do_genocide(how)
 int how;
 /* 0 = no genocide; create monsters (cursed scroll) */
 /* 1 = normal genocide */
 /* 3 = forced genocide of player */
+/* 5 (4 | 1) = normal genocide from throne */
 {
 	char buf[BUFSZ];
 	register int	i, killplayer = 0;
@@ -1525,6 +1577,17 @@
 		}
 		getlin("What monster do you want to genocide? [type the name]",
 			buf);
+		(void)mungspaces(buf);
+		/* choosing "none" preserves genocideless conduct */
+		if (!strcmpi(buf, "none")) {
+		    /* ... but no free pass if cursed */
+		    if (!(how & REALLY)) {
+			ptr = rndmonst();
+			if (!ptr) return; /* no message, like normal case */
+			mndx = monsndx(ptr);
+			break;		/* remaining checks don't apply */
+		    } else return;
+		}
 
 		mndx = name_to_mon(buf);
 		if (mndx == NON_PM || (mvitals[mndx].mvflags & G_GENOD)) {
@@ -1591,11 +1654,17 @@
 		    mvitals[urace.malenum].mvflags |= (G_GENOD | G_NOCORPSE);
 
 		u.uhp = -1;
-		killer_format = KILLED_BY_AN;
-		if (how & PLAYER)
-			killer = "genocidal confusion";
-		else /* selected player deliberately, not confused */
-			killer = "scroll of genocide";
+		if (how & PLAYER) {
+		    killer_format = KILLED_BY;
+		    killer = "genocidal confusion";
+		} else if (how & ONTHRONE) {
+		    /* player selected while on a throne */
+		    killer_format = KILLED_BY_AN;
+		    killer = "imperious order";
+		} else { /* selected player deliberately, not confused */
+		    killer_format = KILLED_BY_AN;
+		    killer = "scroll of genocide";
+		}
 
 	/* Polymorphed characters will die as soon as they're rehumanized. */
 	/* KMH -- Unchanging prevents rehumanization */
@@ -1700,23 +1769,56 @@
 boolean
 create_particular()
 {
-	char buf[BUFSZ];
-	int which, tries = 0;
+	char buf[BUFSZ], monclass = MAXMCLASSES;
+	int which = PM_PLAYERMON, tries = 0, i;
+	struct permonst *whichpm = 0;
+	struct monst *mtmp;
+	boolean madeany = FALSE;
+	boolean maketame = FALSE;
 
 	do {
-	    getlin("Create what kind of monster? [type the name]", buf);
+	    getlin("Create what kind of monster? [type the name or symbol]",
+		   buf);
 	    if (buf[0] == '\033') return FALSE;
-	    which = name_to_mon(buf);
-	    if (which < LOW_PM) pline("I've never heard of such monsters.");
-	    else break;
+	    (void)mungspaces(buf);
+	    if (strlen(buf) == 1) {
+		monclass = def_char_to_monclass(buf[0]);
+		if (monclass == MAXMCLASSES)
+		    pline("I've never heard of such monsters.");
+		else break;
+	    } else {
+		if (!strncmpi(buf, "tame ", 5)) {
+		    which = name_to_mon(buf+5);
+		    maketame = TRUE;
+		} else {
+		    which = name_to_mon(buf);
+		    maketame = FALSE;
+		}
+		if (which < LOW_PM) pline("I've never heard of such monsters.");
+		else {
+		    whichpm = &mons[which];
+		    break;
+		}
+	    }
 	} while (++tries < 5);
 	if (tries == 5) pline(thats_enough_tries);
 	else {
 	    (void) cant_create(&which, FALSE);
-	    return((boolean)(makemon(&mons[which],
-				u.ux, u.uy, NO_MM_FLAGS) != 0));
+	    for (i = 0; i <= multi; i++) {
+		if (monclass != MAXMCLASSES)
+		    whichpm = mkclass(monclass, 0);
+		if (maketame) {
+		    mtmp = makemon(whichpm, u.ux, u.uy, MM_EDOG);
+		    if (mtmp) {
+			initedog(mtmp);
+			set_malign(mtmp);
+		    }
+		} else
+		    mtmp = makemon(whichpm, u.ux, u.uy, NO_MM_FLAGS);
+		if (mtmp) madeany = TRUE;
+	    }
 	}
-	return FALSE;
+	return madeany;
 }
 #endif /* WIZARD */
 
diff -Naurd ../nethack-3.3.1/src/region.c ./src/region.c
--- ../nethack-3.3.1/src/region.c Sat Apr 22 03:30:14 2000
+++ ./src/region.c Fri Mar 22 14:40:55 2002
@@ -1,8 +1,9 @@
-/*	SCCS Id: @(#)region.c	3.3	1999/12/29	*/
+/*	SCCS Id: @(#)region.c	3.4	1999/12/29	*/
 /* Copyright (c) 1996 by Jean-Christophe Collet	 */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
+#include "lev.h"
 
 /*
  * This should really go into the level structure, but
@@ -391,7 +392,7 @@
 	/* Check if any monster is inside region */
 	if (f_indx != NO_CALLBACK) {
 	    for (j = 0; j < regions[i]->n_monst; j++) {
-		struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_EVERYWHERE);
+		struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_FMON);
 
 		if (!mtmp || mtmp->mhp <= 0 ||
 				(*callbacks[f_indx])(regions[i], mtmp)) {
@@ -613,6 +614,8 @@
     int i, j;
     unsigned n;
 
+    if (!perform_bwrite(mode)) goto skip_lots;
+
     bwrite(fd, (genericptr_t) &moves, sizeof (moves));	/* timestamp */
     bwrite(fd, (genericptr_t) &n_regions, sizeof (n_regions));
     for (i = 0; i < n_regions; i++) {
@@ -647,6 +650,10 @@
 	bwrite(fd, (genericptr_t) &regions[i]->glyph, sizeof (int));
 	bwrite(fd, (genericptr_t) &regions[i]->arg, sizeof (genericptr_t));
     }
+
+skip_lots:
+    if (release_data(mode))
+	clear_regions();
 }
 
 void
@@ -883,6 +890,10 @@
 	    if (cansee(mtmp->mx, mtmp->my))
 		pline("%s coughs!", Monnam(mtmp));
 	    setmangry(mtmp);
+	    if (haseyes(mtmp->data) && mtmp->mcansee) {
+		mtmp->mblinded = 1;
+		mtmp->mcansee = 0;
+	    }
 	    if (resists_poison(mtmp))
 		return FALSE;
 	    mtmp->mhp -= rnd(dam) + 5;
diff -Naurd ../nethack-3.3.1/src/restore.c ./src/restore.c
--- ../nethack-3.3.1/src/restore.c Thu Aug 3 22:12:48 2000
+++ ./src/restore.c Fri Mar 22 14:40:55 2002
@@ -8,6 +8,7 @@
 
 #ifdef MICRO
 extern int dotcnt;	/* shared with save */
+extern int dotrow;	/* shared with save */
 #endif
 
 #ifdef USE_TILES
@@ -220,6 +221,7 @@
 		    for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj)
 			otmp3->ocontainer = otmp;
 		}
+		if (otmp->bypass) otmp->bypass = 0;
 
 		otmp2 = otmp;
 	}
@@ -338,9 +340,9 @@
 
 STATIC_OVL
 boolean
-restgamestate(fd, mid, steedid)
+restgamestate(fd, stuckid, steedid)
 register int fd;
-unsigned int *mid, *steedid;	/* STEED */
+unsigned int *stuckid, *steedid;	/* STEED */
 {
 	struct obj *otmp;
 	int uid;
@@ -357,6 +359,8 @@
 	}
 
 	mread(fd, (genericptr_t) &flags, sizeof(struct flag));
+	flags.bypasses = 0;	/* never use the saved value of bypasses */
+
 	role_init();	/* Reset the initial role, race, gender, and alignment */
 #ifdef AMII_GRAPHICS
 	amii_setpens(amii_numcolors);	/* use colors from save file */
@@ -366,7 +370,7 @@
 #ifdef CLIPPING
 	cliparound(u.ux, u.uy);
 #endif
-	if(u.uhp <= 0) {
+	if(u.uhp <= 0 && (!Upolyd || u.mh <= 0)) {
 	    u.ux = u.uy = 0;	/* affects pline() [hence You()] */
 	    You("were not healthy enough to survive restoration.");
 	    /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
@@ -409,10 +413,10 @@
 				sizeof(struct spell) * (MAXSPELL + 1));
 	restore_artifacts(fd);
 	restore_oracles(fd);
-	if(u.ustuck)
-		mread(fd, (genericptr_t) mid, sizeof (*mid));
+	if (u.ustuck)
+		mread(fd, (genericptr_t) stuckid, sizeof (*stuckid));
 #ifdef STEED
-	if(u.usteed)
+	if (u.usteed)
 		mread(fd, (genericptr_t) steedid, sizeof (*steedid));
 #endif
 	mread(fd, (genericptr_t) pl_character, sizeof pl_character);
@@ -434,19 +438,19 @@
  * don't dereference a wild u.ustuck when saving the game state, for instance)
  */
 STATIC_OVL void
-restlevelstate(mid, steedid)
-unsigned int mid, steedid;	/* STEED */
+restlevelstate(stuckid, steedid)
+unsigned int stuckid, steedid;	/* STEED */
 {
 	register struct monst *mtmp;
 
-	if (u.ustuck) {
+	if (stuckid) {
 		for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
-			if (mtmp->m_id == mid) break;
+			if (mtmp->m_id == stuckid) break;
 		if (!mtmp) panic("Cannot find the monster ustuck.");
 		u.ustuck = mtmp;
 	}
 #ifdef STEED
-	if (u.usteed) {
+	if (steedid) {
 		for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
 			if (mtmp->m_id == steedid) break;
 		if (!mtmp) panic("Cannot find the monster usteed.");
@@ -461,7 +465,7 @@
 restlevelfile(fd, ltmp)
 register int fd;
 xchar ltmp;
-#ifdef applec
+#if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
 # pragma unused(fd)
 #endif
 {
@@ -519,14 +523,14 @@
 dorecover(fd)
 register int fd;
 {
-	unsigned int mid = 0, steedid = 0;	/* not a register */
+	unsigned int stuckid = 0, steedid = 0;	/* not a register */
 	xchar ltmp;
 	int rtmp;
 	struct obj *otmp;
 
 	restoring = TRUE;
 	getlev(fd, 0, (xchar)0, FALSE);
-	if (!restgamestate(fd, &mid, &steedid)) {
+	if (!restgamestate(fd, &stuckid, &steedid)) {
 		display_nhwindow(WIN_MESSAGE, TRUE);
 		savelev(-1, 0, FREE_SAVE);	/* discard current level */
 		(void) close(fd);
@@ -534,13 +538,23 @@
 		restoring = FALSE;
 		return(0);
 	}
-	restlevelstate(mid, steedid);
+	restlevelstate(stuckid, steedid);
 #ifdef INSURANCE
 	savestateinlock();
 #endif
 	rtmp = restlevelfile(fd, ledger_no(&u.uz));
 	if (rtmp < 2) return(rtmp);  /* dorecover called recursively */
 
+	/* these pointers won't be valid while we're processing the
+	 * other levels, but they'll be reset again by restlevelstate()
+	 * afterwards, and in the meantime at least u.usteed may mislead
+	 * place_monster() on other levels
+	 */
+	u.ustuck = (struct monst *)0;
+#ifdef STEED
+	u.usteed = (struct monst *)0;
+#endif
+
 #ifdef MICRO
 # ifdef AMII_GRAPHICS
 	{
@@ -560,6 +574,7 @@
 		flags.explore ? " while in explore mode" : "");
 	curs(WIN_MAP, 1, 1);
 	dotcnt = 0;
+	dotrow = 2;
 	if (strncmpi("X11", windowprocs.name, 3))
     	  putstr(WIN_MAP, 0, "Restoring:");
 #endif
@@ -572,7 +587,11 @@
 			break;
 		getlev(fd, 0, ltmp, FALSE);
 #ifdef MICRO
-		curs(WIN_MAP, 1+dotcnt++, 2);
+		curs(WIN_MAP, 1+dotcnt++, dotrow);
+		if (dotcnt >= (COLNO - 1)) {
+			dotrow++;
+			dotcnt = 0;
+		}
 		if (strncmpi("X11", windowprocs.name, 3)){
 		  putstr(WIN_MAP, 0, ".");
 		}
@@ -599,7 +618,7 @@
 #ifdef USE_TILES
 	substitute_tiles(&u.uz);
 #endif
-	restlevelstate(mid, steedid);
+	restlevelstate(stuckid, steedid);
 #ifdef MFLOPPY
 	gameDiskPrompt();
 #endif
@@ -732,6 +751,10 @@
 	mread(fd, (genericptr_t)&level.flags, sizeof(level.flags));
 	mread(fd, (genericptr_t)doors, sizeof(doors));
 	rest_rooms(fd);		/* No joke :-) */
+	if (nroom)
+	    doorindex = rooms[nroom - 1].fdoor + rooms[nroom - 1].doorct;
+	else
+	    doorindex = 0;
 
 	restore_timers(fd, RANGE_LEVEL, ghostly, monstermoves - omoves);
 	restore_light_sources(fd);
@@ -920,7 +943,12 @@
 {
     struct obj *otmp;
     unsigned oldid, nid;
-    for (otmp = fobj; otmp; otmp = otmp->nobj)
+    for (otmp = fobj; otmp; otmp = otmp->nobj) {
+	if (ghostly && otmp->oattached == OATTACHED_MONST && otmp->oxlth) {
+	    struct monst *mtmp = (struct monst *)otmp->oextra;
+
+	    mtmp->m_id = 0;
+	}
 	if (ghostly && otmp->oattached == OATTACHED_M_ID) {
 	    (void) memcpy((genericptr_t)&oldid, (genericptr_t)otmp->oextra,
 								sizeof(oldid));
@@ -930,6 +958,7 @@
 	    else
 		otmp->oattached = OATTACHED_NOTHING;
 	}
+    }
 }
 
 
diff -Naurd ../nethack-3.3.1/src/role.c ./src/role.c
--- ../nethack-3.3.1/src/role.c Mon May 22 18:31:42 2000
+++ ./src/role.c Fri Mar 22 14:40:55 2002
@@ -108,9 +108,9 @@
 	{"Empiric",        0},
 	{"Embalmer",       0},
 	{"Dresser",        0},
-	{"Medici ossium",  0},
+	{"Medicus ossium", "Medica ossium"},
 	{"Herbalist",      0},
-	{"Magister",       0},
+	{"Magister",       "Magistra"},
 	{"Physician",      0},
 	{"Chirurgeon",     0} },
 	"_Athena", "Hermes", "Poseidon", /* Greek */
@@ -122,7 +122,7 @@
 	MH_HUMAN|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
 	/* Str Int Wis Dex Con Cha */
 	{   7,  7, 13,  7, 11, 16 },
-	{  15, 20, 20, 15, 25, 10 },
+	{  15, 20, 20, 15, 25, 5 },
 	/* Init   Lower  Higher */
 	{ 11, 0,  0, 8,  1, 0 },	/* Hit points */
 	{  1, 4,  0, 1,  0, 2 },20,	/* Energy */
@@ -135,8 +135,8 @@
 	{"Sergeant",    0},
 	{"Knight",      0},
 	{"Banneret",    0},
-	{"Chevalier",   0},
-	{"Seignieur",   0},
+	{"Chevalier",   "Chevaliere"},
+	{"Seignieur",   "Dame"},
 	{"Paladin",     0} },
 	"Lugh", "_Brigit", "Manannan Mac Lir", /* Celtic */
 	"Kni", "Camelot Castle", "the Isle of Glass",
@@ -147,7 +147,7 @@
 	MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
 	/* Str Int Wis Dex Con Cha */
 	{  13,  7, 14,  8, 10, 17 },
-	{  20, 15, 15, 10, 20, 10 },
+	{  30, 15, 15, 10, 20, 10 },
 	/* Init   Lower  Higher */
 	{ 14, 0,  0, 8,  2, 0 },	/* Hit points */
 	{  1, 4,  0, 1,  0, 2 },10,	/* Energy */
@@ -277,7 +277,7 @@
 {	{"Samurai", 0}, {
 	{"Hatamoto",    0},  /* Banner Knight */
 	{"Ronin",       0},  /* no allegiance */
-	{"Ninja",       0},  /* secret society */
+	{"Ninja",       "Kunoichi"},  /* secret society */
 	{"Joshu",       0},  /* heads a castle */
 	{"Ryoshu",      0},  /* has a territory */
 	{"Kokushu",     0},  /* heads a province */
@@ -293,7 +293,7 @@
 	MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
 	/* Str Int Wis Dex Con Cha */
 	{  10,  8,  7, 10, 17,  6 },
-	{  30, 10, 10, 30, 14, 10 },
+	{  30, 10,  8, 30, 14,  8 },
 	/* Init   Lower  Higher */
 	{ 13, 0,  0, 8,  1, 0 },	/* Hit points */
 	{  1, 0,  0, 1,  0, 1 },11,	/* Energy */
@@ -362,9 +362,9 @@
 	{"Wizard",      0},
 	{"Mage",        0} },
 	"Ptah", "Thoth", "Anhur", /* Egyptian */
-	"Wiz", "the Tower of the Balance", "the Tower of Darkness",
+	"Wiz", "the Lonely Tower", "the Tower of Darkness",
 	PM_WIZARD, NON_PM, PM_KITTEN,
-	PM_WIZARD_OF_BALANCE, PM_APPRENTICE, PM_DARK_ONE,
+	PM_NEFERET_THE_GREEN, PM_APPRENTICE, PM_DARK_ONE,
 	PM_VAMPIRE_BAT, PM_XORN, S_BAT, S_WRAITH,
 	ART_EYE_OF_THE_AETHIOPICA,
 	MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE |
@@ -502,6 +502,10 @@
 	{"evil",	"unaligned",	"Una",	0,		A_NONE}
 };
 
+STATIC_DCL char * FDECL(promptsep, (char *, int));
+STATIC_DCL int FDECL(role_gendercount, (int));
+STATIC_DCL int FDECL(race_alignmentcount, (int));
+
 /* used by str2XXX() */
 static char NEARDATA randomstr[] = "random";
 
@@ -789,9 +793,11 @@
 }
 
 /* pick a random role subject to any racenum/gendnum/alignnum constraints */
+/* If pickhow == PICK_RIGID a role is returned only if there is  */
+/* a single possibility */
 int
-pick_role(racenum, gendnum, alignnum)
-int racenum, gendnum, alignnum;
+pick_role(racenum, gendnum, alignnum, pickhow)
+int racenum, gendnum, alignnum, pickhow;
 {
     int i;
     int roles_ok = 0;
@@ -800,7 +806,7 @@
 	if (ok_role(i, racenum, gendnum, alignnum))
 	    roles_ok++;
     }
-    if (roles_ok == 0)
+    if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID))
 	return ROLE_NONE;
     roles_ok = rn2(roles_ok);
     for (i = 0; i < SIZE(roles)-1; i++) {
@@ -853,9 +859,11 @@
 }
 
 /* pick a random race subject to any rolenum/gendnum/alignnum constraints */
+/* If pickhow == PICK_RIGID a race is returned only if there is  */
+/* a single possibility */
 int
-pick_race(rolenum, gendnum, alignnum)
-int rolenum, gendnum, alignnum;
+pick_race(rolenum, gendnum, alignnum, pickhow)
+int rolenum, gendnum, alignnum, pickhow;
 {
     int i;
     int races_ok = 0;
@@ -864,7 +872,7 @@
 	if (ok_race(rolenum, i, gendnum, alignnum))
 	    races_ok++;
     }
-    if (races_ok == 0)
+    if (races_ok == 0 || (races_ok > 1 && pickhow == PICK_RIGID))
 	return ROLE_NONE;
     races_ok = rn2(races_ok);
     for (i = 0; i < SIZE(races)-1; i++) {
@@ -913,9 +921,11 @@
 
 /* pick a random gender subject to any rolenum/racenum/alignnum constraints */
 /* gender and alignment are not comparable (and also not constrainable) */
+/* If pickhow == PICK_RIGID a gender is returned only if there is  */
+/* a single possibility */
 int
-pick_gend(rolenum, racenum, alignnum)
-int rolenum, racenum, alignnum;
+pick_gend(rolenum, racenum, alignnum, pickhow)
+int rolenum, racenum, alignnum, pickhow;
 {
     int i;
     int gends_ok = 0;
@@ -924,7 +934,7 @@
 	if (ok_gend(rolenum, racenum, i, alignnum))
 	    gends_ok++;
     }
-    if (gends_ok == 0)
+    if (gends_ok == 0 || (gends_ok > 1 && pickhow == PICK_RIGID))
 	return ROLE_NONE;
     gends_ok = rn2(gends_ok);
     for (i = 0; i < ROLE_GENDERS; i++) {
@@ -973,9 +983,11 @@
 
 /* pick a random alignment subject to any rolenum/racenum/gendnum constraints */
 /* alignment and gender are not comparable (and also not constrainable) */
+/* If pickhow == PICK_RIGID an alignment is returned only if there is  */
+/* a single possibility */
 int
-pick_align(rolenum, racenum, gendnum)
-int rolenum, racenum, gendnum;
+pick_align(rolenum, racenum, gendnum, pickhow)
+int rolenum, racenum, gendnum, pickhow;
 {
     int i;
     int aligns_ok = 0;
@@ -984,7 +996,7 @@
 	if (ok_align(rolenum, racenum, gendnum, i))
 	    aligns_ok++;
     }
-    if (aligns_ok == 0)
+    if (aligns_ok == 0 || (aligns_ok > 1 && pickhow == PICK_RIGID))
 	return ROLE_NONE;
     aligns_ok = rn2(aligns_ok);
     for (i = 0; i < ROLE_ALIGNS; i++) {
@@ -998,6 +1010,283 @@
     return ROLE_NONE;
 }
 
+void
+rigid_role_checks()
+{
+    /* Some roles are limited to a single race, alignment, or gender and
+     * calling this routine prior to XXX_player_selection() will help
+     * prevent an extraneous prompt that actually doesn't allow
+     * you to choose anything further. Note the use of PICK_RIGID which
+     * causes the pick_XX() routine to return a value only if there is one
+     * single possible selection, otherwise it returns ROLE_NONE.
+     *
+     */
+    if (flags.initrole == ROLE_RANDOM) {
+	/* If the role was explicitly specified as ROLE_RANDOM
+	 * via -uXXXX-@ then choose the role in here to narrow down
+	 * later choices. Pick a random role in this case.
+	 */
+	flags.initrole = pick_role(flags.initrace, flags.initgend,
+					flags.initalign, PICK_RANDOM);
+	if (flags.initrole < 0)
+	    flags.initrole = randrole();
+    }
+    if (flags.initrole != ROLE_NONE) {
+	if (flags.initrace == ROLE_NONE)
+	     flags.initrace = pick_race(flags.initrole, flags.initgend,
+						flags.initalign, PICK_RIGID);
+	if (flags.initalign == ROLE_NONE)
+	     flags.initalign = pick_align(flags.initrole, flags.initrace,
+						flags.initgend, PICK_RIGID);
+	if (flags.initgend == ROLE_NONE)
+	     flags.initgend = pick_gend(flags.initrole, flags.initrace,
+						flags.initalign, PICK_RIGID);
+    }
+}
+
+#define BP_ALIGN	0
+#define BP_GEND		1
+#define BP_RACE		2
+#define BP_ROLE		3
+#define NUM_BP		4
+
+STATIC_VAR char pa[NUM_BP], post_attribs;
+
+STATIC_OVL char *
+promptsep(buf, num_post_attribs)
+char *buf;
+int num_post_attribs;
+{
+	const char *conj = "and ";
+	if (num_post_attribs > 1
+	    && post_attribs < num_post_attribs && post_attribs > 1)
+	 	Strcat(buf, ","); 
+	Strcat(buf, " ");
+	--post_attribs;
+	if (!post_attribs && num_post_attribs > 1) Strcat(buf, conj);
+	return buf;
+}
+
+STATIC_OVL int
+role_gendercount(rolenum)
+int rolenum;
+{
+	int gendcount = 0;
+	if (validrole(rolenum)) {
+		if (roles[rolenum].allow & ROLE_MALE) ++gendcount;
+		if (roles[rolenum].allow & ROLE_FEMALE) ++gendcount;
+		if (roles[rolenum].allow & ROLE_NEUTER) ++gendcount;
+	}
+	return gendcount;
+}
+
+STATIC_OVL int
+race_alignmentcount(racenum)
+int racenum;
+{
+	int aligncount = 0;
+	if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
+		if (races[racenum].allow & ROLE_CHAOTIC) ++aligncount;
+		if (races[racenum].allow & ROLE_LAWFUL) ++aligncount;
+		if (races[racenum].allow & ROLE_NEUTRAL) ++aligncount;
+	}
+	return aligncount;
+}
+
+char *
+root_plselection_prompt(suppliedbuf, buflen, rolenum, racenum, gendnum, alignnum)
+char *suppliedbuf;
+int buflen, rolenum, racenum, gendnum, alignnum;
+{
+	int k, gendercount = 0, aligncount = 0;
+	char buf[BUFSZ];
+	char *err_ret = " character's";
+	boolean donefirst = FALSE;
+
+	if (!suppliedbuf || buflen < 1) return err_ret;
+
+	/* initialize these static variables each time this is called */
+	post_attribs = 0;
+	for (k=0; k < NUM_BP; ++k)
+		pa[k] = 0;
+	buf[0] = '\0';
+	*suppliedbuf = '\0';
+	
+	/* How many alignments are allowed for the desired race? */
+	if (racenum != ROLE_NONE && racenum != ROLE_RANDOM)
+		aligncount = race_alignmentcount(racenum);
+
+	if (alignnum != ROLE_NONE && alignnum != ROLE_RANDOM) {
+		/* if race specified, and multiple choice of alignments for it */
+		if ((racenum >= 0) && (aligncount > 1)) {
+			if (donefirst) Strcat(buf, " ");
+			Strcat(buf, aligns[alignnum].adj);
+			donefirst = TRUE;
+		} else {
+			if (donefirst) Strcat(buf, " ");
+			Strcat(buf, aligns[alignnum].adj);
+			donefirst = TRUE;
+		}
+	} else {
+		/* if alignment not specified, but race is specified
+			and only one choice of alignment for that race then
+			don't include it in the later list */
+		if ((((racenum != ROLE_NONE && racenum != ROLE_RANDOM) &&
+			ok_race(rolenum, racenum, gendnum, alignnum))
+		      && (aligncount > 1))
+		     || (racenum == ROLE_NONE || racenum == ROLE_RANDOM)) {
+			pa[BP_ALIGN] = 1;
+			post_attribs++;
+		}
+	}
+	/* <your lawful> */
+
+	/* How many genders are allowed for the desired role? */
+	if (validrole(rolenum))
+		gendercount = role_gendercount(rolenum);
+
+	if (gendnum != ROLE_NONE  && gendnum != ROLE_RANDOM) {
+		if (validrole(rolenum)) {
+		     /* if role specified, and multiple choice of genders for it,
+			and name of role itself does not distinguish gender */
+			if ((rolenum != ROLE_NONE) && (gendercount > 1)
+						&& !roles[rolenum].name.f) {
+				if (donefirst) Strcat(buf, " ");
+				Strcat(buf, genders[gendnum].adj);
+				donefirst = TRUE;
+			}
+	        } else {
+			if (donefirst) Strcat(buf, " ");
+	        	Strcat(buf, genders[gendnum].adj);
+			donefirst = TRUE;
+	        }
+	} else {
+		/* if gender not specified, but role is specified
+			and only one choice of gender then
+			don't include it in the later list */
+		if ((validrole(rolenum) && (gendercount > 1)) || !validrole(rolenum)) {
+			pa[BP_GEND] = 1;
+			post_attribs++;
+		}
+	}
+	/* <your lawful female> */
+
+	if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
+		if (validrole(rolenum) && ok_race(rolenum, racenum, gendnum, alignnum)) {
+			if (donefirst) Strcat(buf, " "); 
+			Strcat(buf, (rolenum == ROLE_NONE) ?
+				races[racenum].noun :
+				races[racenum].adj);
+			donefirst = TRUE;
+		} else if (!validrole(rolenum)) {
+			if (donefirst) Strcat(buf, " ");
+			Strcat(buf, races[racenum].noun);
+			donefirst = TRUE;
+		} else {
+			pa[BP_RACE] = 1;
+			post_attribs++;
+		}
+	} else {
+		pa[BP_RACE] = 1;
+		post_attribs++;
+	}
+	/* <your lawful female gnomish> || <your lawful female gnome> */
+
+	if (validrole(rolenum)) {
+		if (donefirst) Strcat(buf, " ");
+		if (gendnum != ROLE_NONE) {
+		    if (gendnum == 1  && roles[rolenum].name.f)
+			Strcat(buf, roles[rolenum].name.f);
+		    else
+  			Strcat(buf, roles[rolenum].name.m);
+		} else {
+			if (roles[rolenum].name.f) {
+				Strcat(buf, roles[rolenum].name.m);
+				Strcat(buf, "/");
+				Strcat(buf, roles[rolenum].name.f);
+			} else 
+				Strcat(buf, roles[rolenum].name.m);
+		}
+		donefirst = TRUE;
+	} else if (rolenum == ROLE_NONE) {
+		pa[BP_ROLE] = 1;
+		post_attribs++;
+	}
+	
+	if ((racenum == ROLE_NONE || racenum == ROLE_RANDOM) && !validrole(rolenum)) {
+		if (donefirst) Strcat(buf, " ");
+		Strcat(buf, "character");
+		donefirst = TRUE;
+	}
+	/* <your lawful female gnomish cavewoman> || <your lawful female gnome>
+	 *    || <your lawful female character>
+	 */
+	if (buflen > (int) (strlen(buf) + 1)) {
+		Strcpy(suppliedbuf, buf);
+		return suppliedbuf;
+	} else
+		return err_ret;
+}
+
+char *
+build_plselection_prompt(buf, buflen, rolenum, racenum, gendnum, alignnum)
+char *buf;
+int buflen, rolenum, racenum, gendnum, alignnum;
+{
+	const char *defprompt = "Shall I pick a character for you? [ynq] ";
+	int num_post_attribs = 0;
+	char tmpbuf[BUFSZ];
+	
+	if (buflen < QBUFSZ)
+		return (char *)defprompt;
+
+	Strcpy(tmpbuf, "Shall I pick ");
+	if (racenum != ROLE_NONE || validrole(rolenum))
+		Strcat(tmpbuf, "your ");
+	else {
+		Strcat(tmpbuf, "a ");
+	}
+	/* <your> */
+
+	(void)  root_plselection_prompt(eos(tmpbuf), buflen - strlen(tmpbuf),
+					rolenum, racenum, gendnum, alignnum);
+	Sprintf(buf, "%s", s_suffix(tmpbuf));
+
+	/* buf should now be:
+	 * < your lawful female gnomish cavewoman's> || <your lawful female gnome's>
+	 *    || <your lawful female character's>
+	 *
+         * Now append the post attributes to it
+	 */
+
+	num_post_attribs = post_attribs;
+	if (post_attribs) {
+		if (pa[BP_RACE]) {
+			(void) promptsep(eos(buf), num_post_attribs);
+			Strcat(buf, "race");
+		}
+		if (pa[BP_ROLE]) {
+			(void) promptsep(eos(buf), num_post_attribs);
+			Strcat(buf, "role");
+		}
+		if (pa[BP_GEND]) {
+			(void) promptsep(eos(buf), num_post_attribs);
+			Strcat(buf, "gender");
+		}
+		if (pa[BP_ALIGN]) {
+			(void) promptsep(eos(buf), num_post_attribs);
+			Strcat(buf, "alignment");
+		}
+	}
+	Strcat(buf, " for you? [ynq] ");
+	return buf;
+}
+
+#undef BP_ALIGN
+#undef BP_GEND
+#undef BP_RACE
+#undef BP_ROLE
+#undef NUM_BP
 
 void
 plnamesuffix()
diff -Naurd ../nethack-3.3.1/src/rumors.c ./src/rumors.c
--- ../nethack-3.3.1/src/rumors.c Mon May 22 18:10:48 2000
+++ ./src/rumors.c Fri Mar 22 14:41:06 2002
@@ -146,11 +146,16 @@
 	boolean reading = (mechanism == BY_COOKIE ||
 			   mechanism == BY_PAPER);
 
-	if (reading && Blind) {
+	if (reading) {
+	    /* deal with various things that prevent reading */
+	    if (is_fainted() && mechanism == BY_COOKIE)
+	    	return;
+	    else if (Blind) {
 		if (mechanism == BY_COOKIE)
 			pline(fortune_msg);
 		pline("What a pity that you cannot read it!");
-		return;
+	    	return;
+	    }
 	}
 	line = getrumor(truth, buf, reading ? FALSE : TRUE);
 	if (!*line)
@@ -300,8 +305,8 @@
 	}
 
 	Sprintf(qbuf,
-		"\"Wilt thou settle for a minor consultation?\" (%d zorkmids)",
-		minor_cost);
+		"\"Wilt thou settle for a minor consultation?\" (%d %s)",
+		minor_cost, currency((long)minor_cost));
 	switch (ynq(qbuf)) {
 	    default:
 	    case 'q':
@@ -317,8 +322,8 @@
 		if (u.ugold <= (long)minor_cost ||	/* don't even ask */
 		    (oracle_cnt == 1 || oracle_flg < 0)) return 0;
 		Sprintf(qbuf,
-			"\"Then dost thou desire a major one?\" (%d zorkmids)",
-			major_cost);
+			"\"Then dost thou desire a major one?\" (%d %s)",
+			major_cost, currency((long)major_cost));
 		if (yn(qbuf) != 'y') return 0;
 		u_pay = (u.ugold < (long)major_cost ? (int)u.ugold
 						    : major_cost);
diff -Naurd ../nethack-3.3.1/src/save.c ./src/save.c
--- ../nethack-3.3.1/src/save.c Thu Aug 3 22:12:48 2000
+++ ./src/save.c Fri Mar 22 14:40:55 2002
@@ -20,7 +20,7 @@
 #endif
 
 #ifdef MICRO
-int dotcnt;	/* also used in restore */
+int dotcnt, dotrow;	/* also used in restore */
 #endif
 
 #ifdef ZEROCOMP
@@ -94,14 +90,18 @@
 	terminate(EXIT_FAILURE);
 #  endif
 # else	/* SAVEONHANGUP */
-	if (!program_state.done_hup++ && program_state.something_worth_saving) {
+	if (!program_state.done_hup++) {
+	    if (program_state.something_worth_saving)
 		(void) dosave0();
 #  ifdef VMS
-		/* don't call exit when already within an exit handler;
-		   that would cancel any other pending user-mode handlers */
-		if (!program_state.exiting)
+	    /* don't call exit when already within an exit handler;
+	       that would cancel any other pending user-mode handlers */
+	    if (!program_state.exiting)
 #  endif
-			terminate(EXIT_FAILURE);
+	    {
+		clearlocks();
+		terminate(EXIT_FAILURE);
+	    }
 	}
 # endif
 	return;
@@ -156,6 +156,9 @@
 		return(0);
 	}
 
+	vision_recalc(2);	/* shut down vision to prevent problems
+				   in the event of an impossible() call */
+	
 	/* undo date-dependent luck adjustments made at startup time */
 	if(flags.moonphase == FULL_MOON)	/* ut-sally!fletcher */
 		change_luck(-1);		/* and unido!ab */
@@ -166,6 +169,7 @@
 
 #ifdef MICRO
 	dotcnt = 0;
+	dotrow = 2;
 	curs(WIN_MAP, 1, 1);
 	if (strncmpi("X11", windowprocs.name, 3))
 	  putstr(WIN_MAP, 0, "Saving:");
@@ -216,12 +217,23 @@
 	 */
 	uz_save = u.uz;
 	u.uz.dnum = u.uz.dlevel = 0;
+	/* these pointers are no longer valid, and at least u.usteed
+	 * may mislead place_monster() on other levels
+	 */
+	u.ustuck = (struct monst *)0;
+#ifdef STEED
+	u.usteed = (struct monst *)0;
+#endif
 
 	for(ltmp = (xchar)1; ltmp <= maxledgerno(); ltmp++) {
 		if (ltmp == ledger_no(&uz_save)) continue;
 		if (!(level_info[ltmp].flags & LFILE_EXISTS)) continue;
 #ifdef MICRO
-		curs(WIN_MAP, 1 + dotcnt++, 2);
+		curs(WIN_MAP, 1 + dotcnt++, dotrow);
+		if (dotcnt >= (COLNO - 1)) {
+			dotrow++;
+			dotcnt = 0;
+		}
 		if (strncmpi("X11", windowprocs.name, 3)){
 		  putstr(WIN_MAP, 0, ".");
 		}
@@ -831,7 +840,8 @@
 	    if (Has_contents(otmp))
 		saveobjchn(fd,otmp->cobj,mode);
 	    if (release_data(mode)) {
-		if(otmp->oclass == FOOD_CLASS) food_disappears(otmp);
+		if (otmp->oclass == FOOD_CLASS) food_disappears(otmp);
+		if (otmp->oclass == SPBOOK_CLASS) book_disappears(otmp);
 		otmp->where = OBJ_FREE;	/* set to free so dealloc will work */
 		otmp->timed = 0;	/* not timed any more */
 		otmp->lamplit = 0;	/* caller handled lights */
@@ -981,6 +991,14 @@
 	free_waterlevel();
 	free_dungeons();
 
+	/* some pointers in iflags */
+	if (iflags.wc_font_map) free(iflags.wc_font_map);
+	if (iflags.wc_font_message) free(iflags.wc_font_message);
+	if (iflags.wc_font_text) free(iflags.wc_font_text);
+	if (iflags.wc_font_menu) free(iflags.wc_font_menu);
+	if (iflags.wc_font_status) free(iflags.wc_font_status);
+	if (iflags.wc_tile_file) free(iflags.wc_tile_file);
+
 #endif	/* FREE_ALL_MEMORY */
 	return;
 }
diff -Naurd ../nethack-3.3.1/src/shk.c ./src/shk.c
--- ../nethack-3.3.1/src/shk.c Sun Jul 16 02:49:54 2000
+++ ./src/shk.c Fri Mar 22 14:41:06 2002
@@ -63,6 +63,9 @@
 STATIC_DCL void FDECL(add_to_billobjs, (struct obj *));
 STATIC_DCL void FDECL(bill_box_content, (struct obj *, BOOLEAN_P, BOOLEAN_P,
 				     struct monst *));
+#ifdef OVL1
+static void FDECL(rob_shop, (struct monst *));
+#endif
 
 #ifdef OVLB
 /*
@@ -70,6 +73,8 @@
 		obj->quan <= bp->bquan
  */
 
+
+
 STATIC_OVL struct monst *
 next_shkp(shkp, withbill)
 register struct monst *shkp;
@@ -99,24 +104,36 @@
 
 void
 shkgone(mtmp)				/* called in mon.c */
-register struct monst *mtmp;
+struct monst *mtmp;
 {
-	register struct eshk *eshk = ESHK(mtmp);
+	struct eshk *eshk = ESHK(mtmp);
+	struct mkroom *sroom = &rooms[eshk->shoproom - ROOMOFFSET];
+	struct obj *otmp;
+	char *p;
+	int sx, sy;
 
-	if(on_level(&(eshk->shoplevel), &u.uz)) {
-		remove_damage(mtmp, TRUE);
-		rooms[eshk->shoproom - ROOMOFFSET].resident
-						  = (struct monst *)0;
-		if(!search_special(ANY_SHOP))
-		    level.flags.has_shop = 0;
-	}
-	/* make sure bill is set only when the
-	 * dead shk is the resident shk.	*/
-	if(*u.ushops == eshk->shoproom) {
-	    setpaid(mtmp);
-	    /* dump core when referenced */
-	    ESHK(mtmp)->bill_p = (struct bill_x *) -1000;
-	    u.ushops[0] = '\0';
+	/* [BUG: some of this should be done on the shop level */
+	/*       even when the shk dies on a different level.] */
+	if (on_level(&eshk->shoplevel, &u.uz)) {
+	    remove_damage(mtmp, TRUE);
+	    sroom->resident = (struct monst *)0;
+	    if (!search_special(ANY_SHOP))
+		level.flags.has_shop = 0;
+
+	    /* items on shop floor revert to ordinary objects */
+	    for (sx = sroom->lx; sx <= sroom->hx; sx++)
+	      for (sy = sroom->ly; sy <= sroom->hy; sy++)
+		for (otmp = level.objects[sx][sy]; otmp; otmp = otmp->nexthere)
+		    otmp->no_charge = 0;
+
+	    /* Make sure bill is set only when the
+	       dead shk is the resident shk. */
+	    if ((p = index(u.ushops, eshk->shoproom)) != 0) {
+		setpaid(mtmp);
+		eshk->bill_p = (struct bill_x *)0;
+		/* remove eshk->shoproom from u.ushops */
+		do { *p = *(p + 1); } while (*++p);
+	    }
 	}
 }
 
@@ -178,13 +195,17 @@
 #endif /*OVL3*/
 #ifdef OVLB
 
+/* either you paid or left the shop or the shopkeeper died */
 STATIC_OVL void
-setpaid(shkp)	/* either you paid or left the shop or the shopkeeper died */
+setpaid(shkp)
 register struct monst *shkp;
 {
 	register struct obj *obj;
 	register struct monst *mtmp;
 
+	/* FIXME: object handling should be limited to
+	   items which are on this particular shk's bill */
+
 	clear_unpaid(invent);
 	clear_unpaid(fobj);
 	clear_unpaid(level.buriedobjlist);
@@ -292,12 +313,11 @@
 
 void
 u_left_shop(leavestring, newlev)
-register char *leavestring;
-register boolean newlev;
+char *leavestring;
+boolean newlev;
 {
-	register struct monst *shkp;
-	register struct eshk *eshkp;
-	register long total;
+	struct monst *shkp;
+	struct eshk *eshkp;
 
 	/*
 	 * IF player
@@ -311,14 +331,11 @@
 	    return;
 
 	shkp = shop_keeper(*u.ushops0);
-
-	if(!shkp || !inhishop(shkp))
-				/* shk died, teleported, changed levels... */
-	    return;
+	if (!shkp || !inhishop(shkp))
+	    return;	/* shk died, teleported, changed levels... */
 
 	eshkp = ESHK(shkp);
-
-	if(!eshkp->billct && !eshkp->debit)	/* bill is settled */
+	if (!eshkp->billct && !eshkp->debit)	/* bill is settled */
 	    return;
 
 	if (!*leavestring && shkp->mcanmove && !shkp->msleeping) {
@@ -332,10 +349,54 @@
 		      plname);
 	    return;
 	}
+
+	rob_shop(shkp);
+
+#ifdef KOPS
+	call_kops(shkp, (!newlev && levl[u.ux0][u.uy0].edge));
+#else
+	(void) angry_guards(FALSE);
+#endif
+}
+
+/* robbery from outside the shop via telekinesis or grappling hook */
+void
+remote_burglary(x, y)
+xchar x, y;
+{
+	struct monst *shkp;
+	struct eshk *eshkp;
+
+	shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
+	if (!shkp || !inhishop(shkp))
+	    return;	/* shk died, teleported, changed levels... */
+
+	eshkp = ESHK(shkp);
+	if (!eshkp->billct && !eshkp->debit)	/* bill is settled */
+	    return;
+
+	rob_shop(shkp);
+
+#ifdef KOPS
+	/* [might want to set 2nd arg based on distance from shop doorway] */
+	call_kops(shkp, FALSE);
+#else
+	(void) angry_guards(FALSE);
+#endif
+}
+
+static void
+rob_shop(shkp)
+struct monst *shkp;
+{
+	struct eshk *eshkp;
+	long total;
+
+	eshkp = ESHK(shkp);
 	total = (addupbill(shkp) + eshkp->debit);
 	if (eshkp->credit >= total) {
-	    Your("credit of %ld zorkmid%s is used to cover your shopping bill.",
-		 eshkp->credit, plur(eshkp->credit));
+	    Your("credit of %ld %s is used to cover your shopping bill.",
+		 eshkp->credit, currency(eshkp->credit));
 	    total = 0L;		/* credit gets cleared by setpaid() */
 	} else {
 	    You("escaped the shop without paying!");
@@ -346,17 +407,12 @@
 
 	/* by this point, we know an actual robbery has taken place */
 	eshkp->robbed += total;
-	You("stole %ld zorkmid%s worth of merchandise.",
-	    total, plur(total));
+	You("stole %ld %s worth of merchandise.",
+	    total, currency(total));
 	if (!Role_if(PM_ROGUE))	/* stealing is unlawful */
 	    adjalign(-sgn(u.ualign.type));
 
 	hot_pursuit(shkp);
-#ifdef KOPS
-	call_kops(shkp, (!newlev && levl[u.ux0][u.uy0].edge));
-#else
-	(void) angry_guards(FALSE);
-#endif
 }
 
 void
@@ -452,6 +508,8 @@
 		    tool = "mattock";
 		    while ((mattock = mattock->nobj) != 0)
 			if (mattock->otyp == DWARVISH_MATTOCK) ++cnt;
+		    /* [ALI] Shopkeeper identifies mattock(s) */
+		    if (!Blind) makeknown(DWARVISH_MATTOCK);
 		}
 		verbalize(NOTANGRY(shkp) ?
 			  "Will you please leave your %s%s outside?" :
@@ -488,14 +546,14 @@
 
 	/* look up the first object by finding shk whose bill it's on */
 	for (shkp1 = next_shkp(fmon, TRUE); shkp1;
-		shkp1 = next_shkp(shkp1, TRUE))
+		shkp1 = next_shkp(shkp1->nmon, TRUE))
 	    if ((bp1 = onbill(obj1, shkp1, TRUE)) != 0) break;
 	/* second object is probably owned by same shk; if not, look harder */
 	if (shkp1 && (bp2 = onbill(obj2, shkp1, TRUE)) != 0) {
 	    shkp2 = shkp1;
 	} else {
 	    for (shkp2 = next_shkp(fmon, TRUE); shkp2;
-		    shkp2 = next_shkp(shkp2, TRUE))
+		    shkp2 = next_shkp(shkp2->nmon, TRUE))
 		if ((bp2 = onbill(obj2, shkp2, TRUE)) != 0) break;
 	}
 
@@ -546,14 +604,14 @@
 		if ((shkp != this_shkp) ^ pass) continue;
 		eshkp = ESHK(shkp);
 		if ((amt = eshkp->credit) != 0)
-		    You("have %ld zorkmid%s credit at %s %s.",
-			amt, plur(amt), s_suffix(shkname(shkp)),
+		    You("have %ld %s credit at %s %s.",
+			amt, currency(amt), s_suffix(shkname(shkp)),
 			shtypes[eshkp->shoptype - SHOPBASE].name);
 		else if (shkp == this_shkp)
 		    You("have no credit in here.");
 		if ((amt = shop_debt(eshkp)) != 0)
-		    You("owe %s %ld zorkmid%s.",
-			shkname(shkp), amt, plur(amt));
+		    You("owe %s %ld %s.",
+			shkname(shkp), amt, currency(amt));
 		else if (shkp == this_shkp)
 		    You("don't owe any money here.");
 	    }
@@ -642,16 +700,16 @@
 	register struct bill_x *bpm;
 	register struct monst *shkp;
 
-
 	if (obj->otyp == LEASH && obj->leashmon) o_unleash(obj);
 	if (obj->oclass == FOOD_CLASS) food_disappears(obj);
+	if (obj->oclass == SPBOOK_CLASS) book_disappears(obj);
 	if (Has_contents(obj)) delete_contents(obj);
 
 	shkp = 0;
 	if (obj->unpaid) {
 	    /* look for a shopkeeper who owns this object */
 	    for (shkp = next_shkp(fmon, TRUE); shkp;
-		    shkp = next_shkp(shkp, TRUE))
+		    shkp = next_shkp(shkp->nmon, TRUE))
 		if (onbill(obj, shkp, TRUE)) break;
 	}
 	/* sanity check, more or less */
@@ -927,7 +985,7 @@
 	register struct monst *shkp;
 	struct monst *nxtm, *resident;
 	long ltmp;
-	int pass, tmp, shk_pronoun, sk = 0, seensk = 0;
+	int pass, tmp, sk = 0, seensk = 0;
 	boolean paid = FALSE, stashed_gold = (hidden_gold() > 0L);
 
 	multi = 0;
@@ -1027,9 +1085,9 @@
 		return 0;
 	}
 	eshkp = ESHK(shkp);
-	shk_pronoun = pronoun_gender(shkp);
 
 	ltmp = eshkp->robbed;
+
 	if(shkp != resident && NOTANGRY(shkp)) {
 		if(!ltmp)
 		    You("do not owe %s anything.", mon_nam(shkp));
@@ -1039,10 +1097,9 @@
 			pline("But you have some gold stashed away.");
 		} else {
 		    long ugold = u.ugold;
-
 		    if(ugold > ltmp) {
 			You("give %s the %ld gold piece%s %s asked for.",
-			    mon_nam(shkp), ltmp, plur(ltmp), he[shk_pronoun]);
+			    mon_nam(shkp), ltmp, plur(ltmp), mhe(shkp));
 			pay(ltmp, shkp);
 		    } else {
 			You("give %s all your%s gold.", mon_nam(shkp),
@@ -1052,7 +1109,7 @@
 		    }
 		    if((ugold < ltmp/2L) || (ugold < ltmp && stashed_gold))
 			pline("Unfortunately, %s doesn't look satisfied.",
-			      he[shk_pronoun]);
+			      mhe(shkp));
 		    else
 			make_happy_shk(shkp, FALSE);
 		}
@@ -1071,14 +1128,15 @@
 				(u.ugold < ltmp && stashed_gold)) {
 			if (!u.ugold)
 			    pline(no_money, stashed_gold ? " seem to" : "");
-			else pline(not_enough_money, him[shk_pronoun]);
+			else pline(not_enough_money, mhim(shkp));
 			return(1);
 		    }
 		    pline("But since %s shop has been robbed recently,",
-			  his[shk_pronoun]);
+			  mhis(shkp));
 		    pline("you %scompensate %s for %s losses.",
-			  (u.ugold < ltmp) ? "partially " : "",
-			  mon_nam(shkp), his[shk_pronoun]);
+			  (u.ugold < ltmp) ? 
+			  "partially " : "",
+			  mon_nam(shkp), mhis(shkp));
 		    pay(u.ugold < ltmp ? u.ugold : ltmp, shkp);
 		    make_happy_shk(shkp, FALSE);
 		} else {
@@ -1089,12 +1147,12 @@
 		    if(u.ugold < 1000L) {
 			if (!u.ugold)
 			    pline(no_money, stashed_gold ? " seem to" : "");
-			else pline(not_enough_money, him[shk_pronoun]);
+			else pline(not_enough_money, mhim(shkp));
 			return(1);
 		    }
 		    You("try to appease %s by giving %s 1000 gold pieces.",
 			x_monnam(shkp, ARTICLE_THE, "angry", 0, FALSE),
-			him[shk_pronoun]);
+			mhim(shkp));
 		    pay(1000L,shkp);
 		    if (strncmp(eshkp->customer, plname, PL_NSIZ) || rn2(3))
 			make_happy_shk(shkp, FALSE);
@@ -1107,15 +1165,14 @@
 		impossible("dopay: not to shopkeeper?");
 		if(resident) setpaid(resident);
 		return(0);
-	}
+	}        
 	/* pay debt, if any, first */
 	if(eshkp->debit) {
 		long dtmp = eshkp->debit;
 		long loan = eshkp->loan;
 		char sbuf[BUFSZ];
-
-		Sprintf(sbuf, "You owe %s %ld zorkmid%s ",
-					   shkname(shkp), dtmp, plur(dtmp));
+		Sprintf(sbuf, "You owe %s %ld %s ",
+					   shkname(shkp), dtmp, currency(dtmp));
 		if(loan) {
 		    if(loan == dtmp)
 			Strcat(sbuf, "you picked up in the store.");
@@ -1136,7 +1193,7 @@
 			Your("debt is covered by your credit.");
 		    } else if (!eshkp->credit) {
 			u.ugold -= dtmp;
-			shkp->mgold += dtmp;
+ 			shkp->mgold += dtmp;
 			eshkp->debit = 0L;
 			eshkp->loan = 0L;
 			You("pay that debt.");
@@ -1158,7 +1215,6 @@
 	/* now check items on bill */
 	if (eshkp->billct) {
 	    register boolean itemize;
-
 	    if (!u.ugold && !eshkp->credit) {
 		You("%shave no money or credit%s.",
 				    stashed_gold ? "seem to " : "",
@@ -1283,8 +1339,8 @@
 
 	if (itemize) {
 	    char qbuf[BUFSZ];
-	    Sprintf(qbuf,"%s for %ld zorkmid%s.  Pay?", quan == 1L ?
-		    Doname2(obj) : doname(obj), ltmp, plur(ltmp));
+	    Sprintf(qbuf,"%s for %ld %s.  Pay?", quan == 1L ?
+		    Doname2(obj) : doname(obj), ltmp, currency(ltmp));
 	    if (yn(qbuf) == 'n') {
 		buy = PAY_SKIP;		/* don't want to buy */
 	    } else if (quan < bp->bquan && !consumed) { /* partly used goods */
@@ -1381,6 +1437,7 @@
 	struct eshk *eshkp = ESHK(shkp);
 	boolean take = FALSE, taken = FALSE;
 	int roomno = *u.ushops;
+	char takes[BUFSZ];
 
 	/* the simplifying principle is that first-come */
 	/* already took everything you had.		*/
@@ -1417,6 +1474,12 @@
 
 	if (eshkp->following || ANGRY(shkp) || take) {
 		if (!invent && !u.ugold) goto skip;
+		takes[0] = '\0';
+		if (shkp->msleeping || !shkp->mcanmove)
+			Strcat(takes, "wakes up and ");
+		if (distu(shkp->mx, shkp->my) > 2)
+			Strcat(takes, "comes and ");
+		Strcat(takes, "takes");
 
 		if (loss > u.ugold || !loss || roomno == eshkp->shoproom) {
 			eshkp->robbed -= u.ugold;
@@ -1424,12 +1487,8 @@
 			shkp->mgold += u.ugold;
 			u.ugold = 0L;
 			flags.botl = 1;
-			pline("%s %s%stakes all your possessions.",
-			      shkname(shkp),
-			      (shkp->msleeping || !shkp->mcanmove) ?
-					"wakes up and " : "",
-			      (distu(shkp->mx, shkp->my) > 2) ?
-					"comes and " : "");
+			pline("%s %s all your possessions.",
+			      shkname(shkp), takes);
 			taken = TRUE;
 			/* where to put player's invent (after disclosure) */
 			set_repo_loc(eshkp);
@@ -1437,11 +1496,9 @@
 			shkp->mgold += loss;
 			u.ugold -= loss;
 			flags.botl = 1;
-			pline("%s %sand takes %ld zorkmid%s %sowed %s.",
-			      Monnam(shkp),
-			      (shkp->msleeping || !shkp->mcanmove) ?
-					"wakes up " : "comes ",
-			      loss, plur(loss),
+			pline("%s %s the %ld %s %sowed %s.",
+			      Monnam(shkp), takes,
+			      loss, currency(loss),
 			      strncmp(eshkp->customer, plname, PL_NSIZ) ?
 					"" : "you ",
 			      shkp->female ? "her" : "him");
@@ -1568,16 +1625,50 @@
 	/* shopkeeper may notice if the player isn't very knowledgeable -
 	   especially when gem prices are concerned */
 	if (!obj->dknown || !objects[obj->otyp].oc_name_known) {
-		if (obj->oclass == GEM_CLASS) {
-			/* all gems are priced high - real or not */
-			if (objects[obj->otyp].oc_material == GLASS) {
-			    int i = obj->otyp - LUCKSTONE + JADE + 1;
-			    /* real gem's cost (worthless gems come
-			       after jade but before luckstone) */
-			    tmp = (long) objects[i].oc_cost;
-			}
+		if (obj->oclass == GEM_CLASS &&
+			objects[obj->otyp].oc_material == GLASS) {
+		    int i;
+		    /* get a value that's 'random' from game to game, but the
+		       same within the same game */
+		    boolean pseudorand =
+			(((int)u.ubirthday % obj->otyp) >= obj->otyp/2);
+
+		    /* all gems are priced high - real or not */
+		    switch(obj->otyp - LAST_GEM) {
+			case 1: /* white */
+			    i = pseudorand ? DIAMOND : OPAL;
+			    break;
+			case 2: /* blue */
+			    i = pseudorand ? SAPPHIRE : AQUAMARINE;
+			    break;
+			case 3: /* red */
+			    i = pseudorand ? RUBY : JASPER;
+			    break;
+			case 4: /* yellowish brown */
+			    i = pseudorand ? AMBER : TOPAZ;
+			    break;
+			case 5: /* orange */
+			    i = pseudorand ? JACINTH : AGATE;
+			    break;
+			case 6: /* yellow */
+			    i = pseudorand ? CITRINE : CHRYSOBERYL;
+			    break;
+			case 7: /* black */
+			    i = pseudorand ? BLACK_OPAL : JET;
+			    break;
+			case 8: /* green */
+			    i = pseudorand ? EMERALD : JADE;
+			    break;
+			case 9: /* violet */
+			    i = pseudorand ? AMETHYST : FLUORITE;
+			    break;
+			default: impossible("bad glass gem %d?", obj->otyp);
+			    i = STRANGE_OBJECT;
+			    break;
+		    }
+		    tmp = (long) objects[i].oc_cost;
 		} else if (!(obj->o_id % 4)) /* arbitrarily impose surcharge */
-			tmp += tmp / 3L;
+		    tmp += tmp / 3L;
 	}
 #ifdef TOURIST
 	if ((Role_if(PM_TOURIST) && u.ulevel < (MAXULEV/2))
@@ -1608,11 +1699,12 @@
  * a different price quoted for selling as vs. buying.
  */
 long
-contained_cost(obj, shkp, price, usell)
+contained_cost(obj, shkp, price, usell, unpaid_only)
 register struct obj *obj;
 register struct monst *shkp;
 long price;
 register boolean usell;
+register boolean unpaid_only;
 {
 	register struct obj *otmp;
 
@@ -1627,12 +1719,13 @@
 			!(Is_candle(otmp) && otmp->age <
 				20L * (long)objects[otmp->otyp].oc_cost))
 		    price += set_cost(otmp, shkp);
-	    } else if (!otmp->no_charge) {
+	    } else if (!otmp->no_charge &&
+		      (!unpaid_only || (unpaid_only && otmp->unpaid))) {
 		    price += get_cost(otmp, shkp) * otmp->quan;
 	    }
 
 	    if (Has_contents(otmp))
-		    price += contained_cost(otmp, shkp, price, usell);
+		    price += contained_cost(otmp, shkp, price, usell, unpaid_only);
 	}
 
 	return(price);
@@ -1801,7 +1894,7 @@
     if (obj->where != OBJ_FREE)
 	panic("add_to_billobjs: obj not free");
     if (obj->timed)
-	panic("add_to_billobjs: obj is timed");
+	obj_stop_timers(obj);
 
     obj->nobj = billobjs;
     billobjs = obj;
@@ -1914,7 +2007,7 @@
 		    goto speak;
 		}
 	    } else {
-		cltmp += contained_cost(obj, shkp, cltmp, FALSE);
+		cltmp += contained_cost(obj, shkp, cltmp, FALSE, FALSE);
 		gltmp += contained_gold(obj);
 	    }
 
@@ -1962,12 +2055,12 @@
 			(quan > 1L) ? "per" : "for this", xname(obj));
 		obj->quan = quan;
 	    } else
-		pline("%s will cost you %ld zorkmid%s%s.",
-			The(xname(obj)), ltmp, plur(ltmp),
+		pline("%s will cost you %ld %s%s.",
+			The(xname(obj)), ltmp, currency(ltmp),
 			(obj->quan > 1L) ? " each" : "");
 	} else if(!silent) {
-	    if(ltmp) pline_The("list price of %s is %ld zorkmid%s%s.",
-				   the(xname(obj)), ltmp, plur(ltmp),
+	    if(ltmp) pline_The("list price of %s is %ld %s%s.",
+				   the(xname(obj)), ltmp, currency(ltmp),
 				   (obj->quan > 1L) ? " each" : "");
 	    else pline("%s does not notice.", Monnam(shkp));
 	}
@@ -2102,14 +2195,11 @@
 	    if (!Has_contents(otmp)) {
 		if(ininv) {
 		    if(otmp->unpaid)
-			price += get_cost(otmp, shkp);
+			price += otmp->quan * get_cost(otmp, shkp);
 		} else {
 		    if(!otmp->no_charge) {
-			if(!(otmp->oclass == BALL_CLASS ||
-			    (otmp->oclass == FOOD_CLASS && otmp->oeaten) ||
-			    (Is_candle(otmp) && otmp->age <
-				  20L * (long)objects[otmp->otyp].oc_cost))
-			  ) price += get_cost(otmp, shkp);
+			if(otmp->oclass != FOOD_CLASS || !otmp->oeaten)
+			    price += otmp->quan * get_cost(otmp, shkp);
 		    }
 		    otmp->no_charge = 0;
 		}
@@ -2150,15 +2240,28 @@
 	value += gvalue;
 
 	if(peaceful) {
+	    boolean credit_use = !!ESHK(shkp)->credit;
 	    value = check_credit(value, shkp);
 	    ESHK(shkp)->debit += value;
 
 	    if(!silent) {
+		char *still = "";
+		if (credit_use) {
+		    if (ESHK(shkp)->credit) {
+			You("have %ld %s credit remaining.",
+				 ESHK(shkp)->credit, currency(ESHK(shkp)->credit));
+			return value;
+		    } else if (!value) {
+			You("have no credit remaining.");
+			return 0;
+		    }
+		    still = "still ";
+		}
 		if(obj->oclass == GOLD_CLASS)
-		    You("owe %s %ld zorkmids!", mon_nam(shkp), value);
-		else You("owe %s %ld zorkmids for %s!",
+		    You("%sowe %s %ld %s!", still, mon_nam(shkp), value, currency(value));
+		else You("%sowe %s %ld %s for %s!", still,
 			mon_nam(shkp),
-			value,
+			value, currency(value),
 			obj->quan > 1L ? "them" : "it");
 	    }
 	} else {
@@ -2180,20 +2283,24 @@
 
 /* auto-response flag for/from "sell foo?" 'a' => 'y', 'q' => 'n' */
 static char sell_response = 'a';
-static boolean sell_voluntarily = FALSE;
+static int sell_how = SELL_NORMAL;
+/* can't just use sell_response='y' for auto_credit because the 'a' response
+   shouldn't carry over from ordinary selling to credit selling */
+static boolean auto_credit = FALSE;
 
 void
-sellobj_state(deliberate)	/* called from dodrop(do.c) and doddrop() */
-boolean deliberate;
+sellobj_state(deliberate)
+int deliberate;
 {
 	/* If we're deliberately dropping something, there's no automatic
-	response to the shopkeeper's "want to sell" query; however, if we
-	accidentally drop anything, the shk will buy it/them without asking.
-	This retains the old pre-query risk that slippery fingers while in
-	shops entailed:  you drop it, you've lost it.
+	   response to the shopkeeper's "want to sell" query; however, if we
+	   accidentally drop anything, the shk will buy it/them without asking.
+	   This retains the old pre-query risk that slippery fingers while in
+	   shops entailed:  you drop it, you've lost it.
 	 */
-	sell_response = deliberate ? '\0' : 'a';
-	sell_voluntarily = deliberate;
+	sell_response = (deliberate != SELL_NORMAL) ? '\0' : 'a';
+	sell_how = deliberate;
+	auto_credit = FALSE;
 }
 
 void
@@ -2206,6 +2313,7 @@
 	long ltmp = 0L, cltmp = 0L, gltmp = 0L, offer;
 	boolean saleitem, cgold = FALSE, container = Has_contents(obj);
 	boolean isgold = (obj->oclass == GOLD_CLASS);
+	boolean only_partially_your_contents = FALSE;
 
 	if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) ||
 	   !inhishop(shkp)) return;
@@ -2218,7 +2326,7 @@
 	}
 	if(container) {
 		/* find the price of content before subfrombill */
-		cltmp += contained_cost(obj, shkp, cltmp, TRUE);
+		cltmp += contained_cost(obj, shkp, cltmp, TRUE, FALSE);
 		/* find the value of contained gold */
 		gltmp += contained_gold(obj);
 		cgold = (gltmp > 0L);
@@ -2231,7 +2339,8 @@
 	offer = ltmp + cltmp;
 
 	/* get one case out of the way: nothing to sell, and no gold */
-	if(!isgold && (offer + gltmp) == 0L) {
+	if(!isgold &&
+	   ((offer + gltmp) == 0L || sell_how == SELL_DONTSELL)) {
 		register boolean unpaid = (obj->unpaid ||
 				  (container && count_unpaid(obj->cobj)));
 
@@ -2243,7 +2352,7 @@
 			    subfrombill(obj, shkp);
 		} else obj->no_charge = 1;
 
-		if(!unpaid)
+		if(!unpaid && (sell_how != SELL_DONTSELL))
 		    pline("%s seems uninterested.", Monnam(shkp));
 		return;
 	}
@@ -2305,8 +2414,8 @@
 			eshkp->loan = 0L;
 			Your("debt is paid off.");
 		    }
-		    pline("%ld zorkmid%s added to your credit.",
-				delta, delta > 1L ? "s are" : " is");
+		    pline("%ld %s %s added to your credit.",
+				delta, currency(delta), delta > 1L ? "are" : "is");
 		}
 		if(offer) goto move_on;
 		else {
@@ -2334,25 +2443,31 @@
 		obj->no_charge = 1;
 		return;
 	}
-
+        
 	if(!shkp->mgold) {
 		char c, qbuf[BUFSZ];
 		long tmpcr = ((offer * 9L) / 10L) + (offer <= 1L);
 
-		if (!sell_voluntarily) {
+		if (sell_how == SELL_NORMAL || auto_credit) {
 		    c = sell_response = 'y';
 		} else if (sell_response != 'n') {
 		    pline("%s cannot pay you at present.", Monnam(shkp));
 		    Sprintf(qbuf,
-			    "Will you accept %ld zorkmid%s in credit for %s?",
-			    tmpcr, plur(tmpcr), doname(obj));
+			    "Will you accept %ld %s in credit for %s?",
+			    tmpcr, currency(tmpcr), doname(obj));
 		    /* won't accept 'a' response here */
-		    c = ynq(qbuf);
+		    /* KLY - 3/2000 yes, we will, it's a damn nuisance
+                       to have to constantly hit 'y' to sell for credit */
+		    c = ynaq(qbuf);
+		    if (c == 'a') {
+			c = 'y';
+			auto_credit = TRUE;
+		    }
 		} else		/* previously specified "quit" */
 		    c = 'n';
 
 		if (c == 'y') {
-		    shk_names_obj(shkp, obj, sell_voluntarily ?
+		    shk_names_obj(shkp, obj, (sell_how != SELL_NORMAL) ?
 			    "traded %s for %ld zorkmid%s in %scredit." :
 			"relinquish %s and acquire %ld zorkmid%s in %scredit.",
 			    tmpcr,
@@ -2369,17 +2484,21 @@
 	} else {
 		char qbuf[BUFSZ];
 		boolean short_funds = (offer > shkp->mgold);
-
 		if (short_funds) offer = shkp->mgold;
-
 		if (!sell_response) {
+		    only_partially_your_contents =
+			(contained_cost(obj, shkp, 0L, FALSE, FALSE) !=
+			 contained_cost(obj, shkp, 0L, FALSE, TRUE));
 		    Sprintf(qbuf,
 			 "%s offers%s %ld gold piece%s for%s %s %s.  Sell %s?",
 			    Monnam(shkp), short_funds ? " only" : "",
 			    offer, plur(offer),
-			    (!ltmp && cltmp) ? " the contents of" : "",
-			    obj->unpaid ? "the" : "your", xname(obj),
-			    (obj->quan == 1L) ? "it" : "them");
+			    (!ltmp && cltmp && only_partially_your_contents) ?
+			     " your items in" : (!ltmp && cltmp) ? " the contents of" : "",
+			    obj->unpaid ? "the" : "your", cxname(obj),
+			    (obj->quan == 1L &&
+			    !(!ltmp && cltmp && only_partially_your_contents)) ?
+			    "it" : "them");
 		} else  qbuf[0] = '\0';		/* just to pacify lint */
 
 		switch (sell_response ? sell_response : ynaq(qbuf)) {
@@ -2395,7 +2514,9 @@
 			    if (!obj->unpaid && !saleitem) obj->no_charge = 1;
 			    subfrombill(obj, shkp);
 			    pay(-offer, shkp);
-			    shk_names_obj(shkp, obj, sell_voluntarily ?
+			    shk_names_obj(shkp, obj, (sell_how != SELL_NORMAL) ?
+				    (!ltmp && cltmp && only_partially_your_contents) ?
+			    	    "sold some items inside %s for %ld gold pieces%s.%s" :
 				    "sold %s for %ld gold piece%s.%s" :
 	       "relinquish %s and receive %ld gold piece%s in compensation.%s",
 				    offer, "");
@@ -2461,11 +2582,9 @@
 		uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
 		thisused = bp->price * uquan;
 		totused += thisused;
-		obj->quan = uquan;		/* cheat doname */
 		obj->unpaid = 0;		/* ditto */
 		/* Why 'x'?  To match `I x', more or less. */
-		buf_p = xprname(obj, (char *)0, 'x', FALSE, thisused);
-		obj->quan = oquan;		/* restore value */
+		buf_p = xprname(obj, (char *)0, 'x', FALSE, thisused, uquan);
 #ifdef __SASC
 				/* SAS/C 6.2 can't cope for some reason */
 		sasc_bug(obj,save_unpaid);
@@ -2481,10 +2600,10 @@
 	    totused += eshkp->debit;
 	    buf_p = xprname((struct obj *)0,
 			    "usage charges and/or other fees",
-			    GOLD_SYM, FALSE, eshkp->debit);
+			    GOLD_SYM, FALSE, eshkp->debit, 0L);
 	    putstr(datawin, 0, buf_p);
 	}
-	buf_p = xprname((struct obj *)0, "Total:", '*', FALSE, totused);
+	buf_p = xprname((struct obj *)0, "Total:", '*', FALSE, totused, 0L);
 	putstr(datawin, 0, "");
 	putstr(datawin, 0, buf_p);
 	display_nhwindow(datawin, FALSE);
@@ -2502,6 +2621,10 @@
 {
 	register long tmp = (long) objects[obj->otyp].oc_cost;
 
+	if (obj->oartifact) {
+	    tmp = arti_cost(obj);
+	    if (shk_buying) tmp /= 4;
+	}
 	switch(obj->oclass) {
 	case FOOD_CLASS:
 		/* simpler hunger check, (2-4)*cost */
@@ -2525,12 +2648,11 @@
 		    tmp /= 2L;
 		break;
 	}
-	if (obj->oartifact) tmp *= 25L;
 	return tmp;
 }
 
 /* shk catches thrown pick-axe */
-int
+struct monst *
 shkcatch(obj, x, y)
 register struct obj *obj;
 register xchar x, y;
@@ -2548,8 +2670,10 @@
 		if (mnearto(shkp, x, y, TRUE))
 		    verbalize("Out of my way, scum!");
 		if (cansee(x, y)) {
-		    pline("%s nimbly catches %s.",
-			  Monnam(shkp), the(xname(obj)));
+		    pline("%s nimbly%s catches %s.",
+			  Monnam(shkp),
+			  (x == shkp->mx && y == shkp->my) ? "" : " reaches over and",
+			  the(xname(obj)));
 		    if (!canspotmon(shkp))
 			map_invisible(x, y);
 		    delay_output();
@@ -2557,9 +2681,9 @@
 		}
 		subfrombill(obj, shkp);
 		(void) mpickobj(shkp, obj);
-		return(1);
+		return shkp;
 	}
-	return(0);
+	return (struct monst *)0;
 }
 
 void
@@ -2745,17 +2869,21 @@
 		(void) mpickobj(shkp, otmp);
 	    }
 	    deltrap(ttmp);
+	    if(IS_DOOR(tmp_dam->typ)) {
+		levl[x][y].doormask = D_CLOSED; /* arbitrary */
+		block_point(x, y);
+	    } else if (IS_WALL(tmp_dam->typ)) {
+		levl[x][y].typ = tmp_dam->typ;
+		block_point(x, y);
+	    }
 	    newsym(x, y);
 	    return(3);
 	}
 	if (IS_ROOM(tmp_dam->typ)) {
-	    /* No messages if player already filled trap door */
-	    if (catchup || !ttmp)
-		return(1);
-	    newsym(x, y);
-	    return(2);
+	    /* No messages, because player already filled trap door */
+	    return(1);
 	}
-	if (!ttmp && (tmp_dam->typ == levl[x][y].typ) &&
+	if ((tmp_dam->typ == levl[x][y].typ) &&
 	    (!IS_DOOR(tmp_dam->typ) || (levl[x][y].doormask > D_BROKEN)))
 	    /* No messages if player already replaced shop door */
 	    return(1);
@@ -2887,7 +3015,14 @@
 	gy = eshkp->shk.y;
 	satdoor = (gx == omx && gy == omy);
 	if(eshkp->following || ((z = holetime()) >= 0 && z*z <= udist)){
-		if(udist > 4)
+		/* [This distance check used to apply regardless of
+		    whether the shk was following, but that resulted in
+		    m_move() sometimes taking the shk out of the shop if
+		    the player had fenced him in with boulders or traps.
+		    Such voluntary abandonment left unpaid objects in
+		    invent, triggering billing impossibilities on the
+		    next level once the character fell through the hole.] */
+		if (udist > 4 && eshkp->following)
 		    return(-1);	/* leave it to m_move */
 		gx = u.ux;
 		gy = u.uy;
@@ -2951,9 +3086,17 @@
 register int fall;
 {
     register struct monst *shkp = shop_keeper(*u.ushops);
+    int lang = 0;
+    char *grabs = "grabs";
 
     if(!shkp) return;
 
+    /* 0 == can't speak, 1 == makes animal noises, 2 == speaks */
+    if (!is_silent(shkp->data) && shkp->data->msound <= MS_ANIMAL)
+    	lang = 1;
+    else if (shkp->data->msound >= MS_HUMANOID)
+	lang = 2;
+
     if(!inhishop(shkp)) {
 	if (Role_if(PM_KNIGHT)) {
 	    You_feel("like a common thief.");
@@ -2963,12 +3106,15 @@
     }
 
     if(!fall) {
-	if(u.utraptype == TT_PIT)
-	    verbalize("Be careful, %s, or you might fall through the floor.",
-		flags.female ? "madam" : "sir");
-	else
-	    verbalize("%s, do not damage the floor here!",
+	if (lang == 2) {
+	    if(u.utraptype == TT_PIT)
+		verbalize(
+			"Be careful, %s, or you might fall through the floor.",
+			flags.female ? "madam" : "sir");
+	    else
+		verbalize("%s, do not damage the floor here!",
 			flags.female ? "Madam" : "Sir");
+	}
 	if (Role_if(PM_KNIGHT)) {
 	    You_feel("like a common thief.");
 	    adjalign(-sgn(u.ualign.type));
@@ -2977,23 +3123,40 @@
 		!shkp->msleeping && shkp->mcanmove &&
 		(ESHK(shkp)->billct || ESHK(shkp)->debit)) {
 	    register struct obj *obj, *obj2;
-
+	    if (nolimbs(shkp->data)) {
+		grabs = "knocks off";
+#if 0
+	       /* This is what should happen, but for balance
+	        * reasons, it isn't currently.
+	        */
+		if (lang == 2)
+		    pline("%s curses %s inability to grab your backpack!",
+			  shkname(shkp), mhim(shkp));
+		rile_shk(shkp);
+		return;
+#endif
+	    }
 	    if (distu(shkp->mx, shkp->my) > 2) {
 		mnexto(shkp);
 		/* for some reason the shopkeeper can't come next to you */
 		if (distu(shkp->mx, shkp->my) > 2) {
-		    pline("%s curses you in anger and frustration!",
-					shkname(shkp));
+		    if (lang == 2)
+			pline("%s curses you in anger and frustration!",
+			      shkname(shkp));
 		    rile_shk(shkp);
 		    return;
-		} else pline("%s leaps, and grabs your backpack!",
-					shkname(shkp));
-	    } else pline("%s grabs your backpack!", shkname(shkp));
+		} else
+		    pline("%s %s, and %s your backpack!",
+			  shkname(shkp),
+			  makeplural(locomotion(shkp->data,"leap")), grabs);
+	    } else
+		pline("%s %s your backpack!", shkname(shkp), grabs);
 
 	    for(obj = invent; obj; obj = obj2) {
 		obj2 = obj->nobj;
-		if(obj->owornmask) continue;
-		if(obj->otyp == LEASH && obj->leashmon) continue;
+		if ((obj->owornmask & ~(W_SWAPWEP|W_QUIVER)) != 0 ||
+			(obj == uswapwep && u.twoweap) ||
+			(obj->otyp == LEASH && obj->leashmon)) continue;
 		freeinv(obj);
 		subfrombill(obj, shkp);
 		(void) add_to_minv(shkp, obj);	/* may free obj */
@@ -3156,9 +3319,9 @@
 		return;
 	}
 
-	if(Invis) Your("invisibility does not fool %s!", shkname(shkp));
-	Sprintf(qbuf,"\"Cad!  You did %ld zorkmids worth of damage!\"  Pay? ",
-		 cost_of_damage);
+	if (Invis) Your("invisibility does not fool %s!", shkname(shkp));
+	Sprintf(qbuf,"\"Cad!  You did %ld %s worth of damage!\"  Pay? ",
+		 cost_of_damage, currency(cost_of_damage));
 	if(yn(qbuf) != 'n') {
 		cost_of_damage = check_credit(cost_of_damage, shkp);
 		u.ugold -= cost_of_damage;
@@ -3236,11 +3399,11 @@
 	cost = (otmp->no_charge || otmp == uball || otmp == uchain) ? 0L :
 		get_cost(otmp, (struct monst *)0);
 	if (Has_contents(otmp))
-	    cost += contained_cost(otmp, shkp, 0L, FALSE);
+	    cost += contained_cost(otmp, shkp, 0L, FALSE, FALSE);
 	if (!cost) {
 	    Strcpy(price, "no charge");
 	} else {
-	    Sprintf(price, "%ld zorkmid%s%s", cost, plur(cost),
+	    Sprintf(price, "%ld %s%s", cost, currency(cost),
 		    otmp->quan > 1L ? " each" : "");
 	}
 	Sprintf(buf, "%s, %s", doname(otmp), price);
@@ -3255,9 +3418,9 @@
 	    /* print cost in slightly different format, so can't reuse buf */
 	    cost = get_cost(first_obj, (struct monst *)0);
 	    if (Has_contents(first_obj))
-		cost += contained_cost(first_obj, shkp, 0L, FALSE);
-	    pline("%s, price %ld zorkmid%s%s%s", doname(first_obj),
-		cost, plur(cost), first_obj->quan > 1L ? " each" : "",
+		cost += contained_cost(first_obj, shkp, 0L, FALSE, FALSE);
+	    pline("%s, price %ld %s%s%s", doname(first_obj),
+		cost, currency(cost), first_obj->quan > 1L ? " each" : "",
 		shk_embellish(first_obj, cost));
 	}
     }
@@ -3313,13 +3476,26 @@
 
 void
 shk_chat(shkp)
-register struct monst *shkp;
+struct monst *shkp;
 {
-	register struct eshk *eshk = ESHK(shkp);
+	struct eshk *eshk;
+	if (!shkp->isshk) {
+		/* The monster type is shopkeeper, but this monster is
+		   not actually a shk, which could happen if someone
+		   wishes for a shopkeeper statue and then animates it.
+		   (Note: shkname() would be "" in a case like this.) */
+		pline("%s asks whether you've seen any untended shops recently.",
+		      Monnam(shkp));
+		/* [Perhaps we ought to check whether this conversation
+		   is taking place inside an untended shop, but a shopless
+		   shk can probably be expected to be rather disoriented.] */
+		return;
+	}
 
+	eshk = ESHK(shkp);
 	if (ANGRY(shkp))
 		pline("%s mentions how much %s dislikes %s customers.",
-			shkname(shkp), he[shkp->female],
+			shkname(shkp), mhe(shkp),
 			eshk->robbed ? "non-paying" : "rude");
 	else if (eshk->following) {
 		if (strncmp(eshk->customer, plname, PL_NSIZ)) {
@@ -3327,19 +3503,20 @@
 			    Hello(shkp), plname, eshk->customer);
 		    eshk->following = 0;
 		} else {
-		    verbalize("%s %s!  Didn't you forget to pay?", Hello(shkp), plname);
+		    verbalize("%s %s!  Didn't you forget to pay?",
+			      Hello(shkp), plname);
 		}
 	} else if (eshk->billct) {
 		register long total = addupbill(shkp) + eshk->debit;
-		pline("%s says that your bill comes to %ld zorkmid%s.",
-		      shkname(shkp), total, plur(total));
+		pline("%s says that your bill comes to %ld %s.",
+		      shkname(shkp), total, currency(total));
 	} else if (eshk->debit)
-		pline("%s reminds you that you owe %s %ld zorkmid%s.",
-		      shkname(shkp), him[shkp->female],
-		      eshk->debit, plur(eshk->debit));
+		pline("%s reminds you that you owe %s %ld %s.",
+		      shkname(shkp), mhim(shkp),
+		      eshk->debit, currency(eshk->debit));
 	else if (eshk->credit)
-		pline("%s encourages you to use your %ld zorkmid%s of credit.",
-		      shkname(shkp), eshk->credit, plur(eshk->credit));
+		pline("%s encourages you to use your %ld %s of credit.",
+		      shkname(shkp), eshk->credit, currency(eshk->credit));
 	else if (eshk->robbed)
 		pline("%s complains about a recent robbery.", shkname(shkp));
 	else if (shkp->mgold < 50)
@@ -3413,13 +3590,17 @@
 		 (otmp->otyp >= MAGIC_FLUTE &&
 		  otmp->otyp <= DRUM_OF_EARTHQUAKE) ||	 /* 5 - 9 */
 		  otmp->oclass == WAND_CLASS) {		 /* 3 - 11 */
-		    if (otmp->spe > 1) tmp /= 4L;
+		if (otmp->spe > 1) tmp /= 4L;
 	} else if (otmp->oclass == SPBOOK_CLASS) {
-		    tmp -= tmp / 5L;
-	} else if (otmp->otyp == CAN_OF_GREASE) {
-		    tmp /= 10L;
+		tmp -= tmp / 5L;
+	} else if (otmp->otyp == CAN_OF_GREASE
+#ifdef TOURIST
+		   || otmp->otyp == EXPENSIVE_CAMERA
+#endif
+		   ) {
+		tmp /= 10L;
 	} else if (otmp->otyp == POT_OIL) {
-		    tmp /= 5L;
+		tmp /= 5L;
 	}
 	return(tmp);
 }
@@ -3450,19 +3631,19 @@
 
 	arg1 = arg2 = "";
 	if (otmp->oclass == SPBOOK_CLASS) {
-	    fmt = "%sYou owe%s %ld zorkmids.";
+	    fmt = "%sYou owe%s %ld %s.";
 	    arg1 = rn2(2) ? "This is no free library, cad!  " : "";
 	    arg2 = ESHK(shkp)->debit > 0L ? " an additional" : "";
 	} else if (otmp->otyp == POT_OIL) {
-	    fmt = "%s%sThat will cost you %ld zorkmids (Yendorian Fuel Tax).";
+	    fmt = "%s%sThat will cost you %ld %s (Yendorian Fuel Tax).";
 	} else {
-	    fmt = "%s%sUsage fee, %ld zorkmids.";
+	    fmt = "%s%sUsage fee, %ld %s.";
 	    if (!rn2(3)) arg1 = "Hey!  ";
 	    if (!rn2(3)) arg2 = "Ahem.  ";
 	}
 
 	if (shkp->mcanmove || !shkp->msleeping)
-	    verbalize(fmt, arg1, arg2, tmp);
+	    verbalize(fmt, arg1, arg2, tmp, currency(tmp));
 	ESHK(shkp)->debit += tmp;
 	exercise(A_WIS, TRUE);		/* you just got info */
 }
@@ -3491,8 +3672,8 @@
 	eshkp = ESHK(shkp);
 	if(eshkp->credit >= amount) {
 	    if(eshkp->credit > amount)
-		Your("credit is reduced by %ld zorkmid%s.",
-					amount, plur(amount));
+		Your("credit is reduced by %ld %s.",
+					amount, currency(amount));
 	    else Your("credit is erased.");
 	    eshkp->credit -= amount;
 	} else {
@@ -3500,10 +3681,10 @@
 	    if(eshkp->credit)
 		Your("credit is erased.");
 	    if(eshkp->debit)
-		Your("debt increases by %ld zorkmid%s.",
-					delta, plur(delta));
-	    else You("owe %s %ld zorkmid%s.",
-				shkname(shkp), delta, plur(delta));
+		Your("debt increases by %ld %s.",
+					delta, currency(delta));
+	    else You("owe %s %ld %s.",
+				shkname(shkp), delta, currency(delta));
 	    eshkp->debit += delta;
 	    eshkp->loan += delta;
 	    eshkp->credit = 0L;
diff -Naurd ../nethack-3.3.1/src/shknam.c ./src/shknam.c
--- ../nethack-3.3.1/src/shknam.c Wed Aug 9 19:38:03 2000
+++ ./src/shknam.c Fri Mar 22 14:41:06 2002
@@ -115,7 +115,7 @@
     "Erreip", "Nehpets", "Mron", "Snivek", "Lapu", "Kahztiy",
 #endif
 #ifdef WIN32
-    "Lechaim",
+    "Lechaim", "Lexa", "Niod",
 #endif
 #ifdef MAC
     "Nhoj-lee", "Evad\'kh", "Ettaw-noj", "Tsew-mot", "Ydna-s",
@@ -246,21 +246,25 @@
 const struct shclass *shp;
 int sx, sy;
 {
-	register struct monst *mtmp;
+	struct monst *mtmp;
 	int atype;
 	struct permonst *ptr;
 
 	if (rn2(100) < depth(&u.uz) &&
-	    !MON_AT(sx, sy) && (ptr = mkclass(S_MIMIC,0)) &&
-	    (mtmp=makemon(ptr,sx,sy,NO_MM_FLAGS))) {
-		/* note: makemon will set the mimic symbol to a shop item */
-		if (rn2(10) >= depth(&u.uz)) {
-			mtmp->m_ap_type = M_AP_OBJECT;
-			mtmp->mappearance = STRANGE_OBJECT;
-		}
-	} else if ((atype = get_shop_item(shp - shtypes)) < 0)
-		(void) mksobj_at(-atype, sx, sy, TRUE);
-	else (void) mkobj_at(atype, sx, sy, TRUE);
+		!MON_AT(sx, sy) && (ptr = mkclass(S_MIMIC,0)) &&
+		(mtmp = makemon(ptr,sx,sy,NO_MM_FLAGS)) != 0) {
+	    /* note: makemon will set the mimic symbol to a shop item */
+	    if (rn2(10) >= depth(&u.uz)) {
+		mtmp->m_ap_type = M_AP_OBJECT;
+		mtmp->mappearance = STRANGE_OBJECT;
+	    }
+	} else {
+	    atype = get_shop_item(shp - shtypes);
+	    if (atype < 0)
+		(void) mksobj_at(-atype, sx, sy, TRUE, TRUE);
+	    else
+		(void) mkobj_at(atype, sx, sy, TRUE);
+	}
 }
 
 /* extract a shopkeeper name for the given shop type */
@@ -403,6 +407,8 @@
 	ESHK(shk)->following = 0;
 	ESHK(shk)->billct = 0;
 	shk->mgold = 1000L + 30L*(long)rnd(100);	/* initial capital */
+	if (shp->shknms == shkrings)
+	    (void) mongets(shk, TOUCHSTONE);
 	nameshk(shk, shp->shknms);
 
 	return(sh);
diff -Naurd ../nethack-3.3.1/src/sit.c ./src/sit.c
--- ../nethack-3.3.1/src/sit.c Sat Apr 22 03:31:56 2000
+++ ./src/sit.c Fri Mar 22 14:41:07 2002
@@ -38,6 +38,8 @@
 	    else
 		You("are sitting on air.");
 	    return 0;
+	} else if (is_pool(u.ux, u.uy) && !Underwater) {  /* water walking */
+	    goto in_water;
 	}
 
 	if(OBJ_AT(u.ux, u.uy)) {
@@ -45,7 +47,8 @@
 
 	    obj = level.objects[u.ux][u.uy];
 	    You("sit on %s.", the(xname(obj)));
-	    if(!Is_box(obj)) pline("It's not very comfortable...");
+	    if (!(Is_box(obj) || objects[obj->otyp].oc_material == CLOTH))
+		pline("It's not very comfortable...");
 
 	} else if ((trap = t_at(u.ux, u.uy)) != 0) {
 
@@ -76,7 +79,7 @@
 		}
 	    } else {
 	        You("sit down.");
-		dotrap(trap);
+		dotrap(trap, 0);
 	    }
 	} else if(Underwater || Is_waterlevel(&u.uz)) {
 	    if (Is_waterlevel(&u.uz))
@@ -84,7 +87,7 @@
 	    else
 		You("sit down on the muddy bottom.");
 	} else if(is_pool(u.ux, u.uy)) {
-
+ in_water:
 	    You("sit in the water.");
 	    if (!rn2(10) && uarm)
 		(void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst);
@@ -191,7 +194,7 @@
 			pline("A voice echoes:");
 			verbalize("By thy Imperious order, %s...",
 				  flags.female ? "Dame" : "Sire");
-			do_genocide(1);
+			do_genocide(5);	/* REALLY|ONTHRONE, see do_genocide() */
 			break;
 		    case 9:
 			pline("A voice echoes:");
@@ -241,13 +244,18 @@
 		    default:	impossible("throne effect");
 				break;
 		}
-	    } else	You_feel("somehow out of place...");
+	    } else {
+		if (is_prince(youmonst.data))
+		    You_feel("very comfortable here.");
+		else
+		    You_feel("somehow out of place...");
+	    }
 
 	    if (!rn2(3) && IS_THRONE(levl[u.ux][u.uy].typ)) {
 		/* may have teleported */
 		pline_The("throne vanishes in a puff of logic.");
 		levl[u.ux][u.uy].typ = ROOM;
-		if(Invisible) newsym(u.ux,u.uy);
+		newsym(u.ux,u.uy);
 	    }
 
 	} else if (lays_eggs(youmonst.data)) {
@@ -301,7 +309,7 @@
 
 	for (otmp = invent; otmp; otmp = otmp->nobj)  nobj++;
 
-	if (nobj)
+	if (nobj) {
 	    for (cnt = rnd(6/((!!Antimagic) + (!!Half_spell_damage) + 1));
 		 cnt > 0; cnt--)  {
 		onum = rn2(nobj);
@@ -310,7 +318,7 @@
 
 		if(otmp->oartifact && spec_ability(otmp, SPFX_INTEL) &&
 		   rn2(10) < 8) {
-		    pline("%s resists!", The(xname(otmp)));
+		    pline("%s!", Tobjnam(otmp, "resist"));
 		    continue;
 		}
 
@@ -319,6 +327,8 @@
 		else
 			curse(otmp);
 	    }
+	    update_inventory();
+	}
 }
 
 void
diff -Naurd ../nethack-3.3.1/src/sounds.c ./src/sounds.c
--- ../nethack-3.3.1/src/sounds.c Thu Aug 3 20:31:44 2000
+++ ./src/sounds.c Fri Mar 22 14:41:07 2002
@@ -1,9 +1,12 @@
-/*	SCCS Id: @(#)sounds.c	3.3	2000/07/24	*/
+/*	SCCS Id: @(#)sounds.c	3.4	2001/02/14	*/
 /*	Copyright (c) 1989 Janet Walz, Mike Threepoint */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 #include "edog.h"
+#ifdef USER_SOUNDS
+#include <regex.h>
+#endif
 
 #ifdef OVLB
 
@@ -77,7 +80,7 @@
 		int which = rn2(3)+hallu;
 
 		if (which != 2) You_hear(throne_msg[which]);
-		else		pline(throne_msg[2], his[flags.female]);
+		else		pline(throne_msg[2], uhis());
 		return;
 	    }
 	}
@@ -319,7 +322,7 @@
     else
 	growl_verb = growl_sound(mtmp);
     if (growl_verb) {
-	pline("%s %s!", Monnam(mtmp), makeplural(growl_verb));
+	pline("%s %s!", Monnam(mtmp), vtense((char *)0, growl_verb));
 	if(flags.run) nomul(0);
 	wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 18);
     }
@@ -360,7 +363,7 @@
 	    break;
     }
     if (yelp_verb) {
-	pline("%s %ss!", Monnam(mtmp), yelp_verb);
+	pline("%s %s!", Monnam(mtmp), vtense((char *)0, yelp_verb));
 	if(flags.run) nomul(0);
 	wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 12);
     }
@@ -392,7 +395,7 @@
 	    break;
     }
     if (whimper_verb) {
-	pline("%s %ss.", Monnam(mtmp), whimper_verb);
+	pline("%s %s.", Monnam(mtmp), vtense((char *)0, whimper_verb));
 	if(flags.run) nomul(0);
 	wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 6);
     }
@@ -408,7 +411,7 @@
 	return;
 
     /* presumably nearness and soundok checks have already been made */
-    if (mtmp->data->msound != MS_SILENT && mtmp->data->msound <= MS_ANIMAL)
+    if (!is_silent(mtmp->data) && mtmp->data->msound <= MS_ANIMAL)
 	(void) domonnoise(mtmp);
     else if (mtmp->data->msound >= MS_HUMANOID) {
 	if (!canspotmon(mtmp))
@@ -428,7 +431,7 @@
 
     /* presumably nearness and sleep checks have already been made */
     if (!flags.soundok) return(0);
-    if (ptr->msound == MS_SILENT) return(0);
+    if (is_silent(ptr)) return(0);
 
     /* be sure to do this before talking; the monster might teleport away, in
      * which case we want to check its pre-teleport position
@@ -500,7 +503,14 @@
 	    		};
 			if (kindred)
 			    verbl_msg = "This is my hunting ground that you dare to prowl!";
-			else {
+			else if (youmonst.data == &mons[PM_SILVER_DRAGON] ||
+				 youmonst.data == &mons[PM_BABY_SILVER_DRAGON]) {
+			    /* Silver dragons are silver in color, not made of silver */
+			    Sprintf(verbuf, "%s! Your silver sheen does not frighten me!",
+					youmonst.data == &mons[PM_SILVER_DRAGON] ?
+					"Fool" : "Young Fool");
+			    verbl_msg = verbuf; 
+			} else {
 			    vampindex = rn2(SIZE(vampmsg));
 			    if (vampindex == 0) {
 				Sprintf(verbuf, vampmsg[vampindex], body_part(BLOOD));
@@ -518,7 +528,7 @@
 	case MS_WERE:
 	    if (flags.moonphase == FULL_MOON && (night() ^ !rn2(13))) {
 		pline("%s throws back %s head and lets out a blood curdling %s!",
-		      Monnam(mtmp), his[pronoun_gender(mtmp)],
+		      Monnam(mtmp), mhis(mtmp),
 		      ptr == &mons[PM_HUMAN_WERERAT] ? "shriek" : "howl");
 		wake_nearto(mtmp->mx, mtmp->my, 11*11);
 	    } else
@@ -636,7 +646,7 @@
 	    if (!mtmp->mpeaceful) {
 		switch (rn2(4)) {
 		case 0: pline("%s boasts about %s gem collection.",
-			      Monnam(mtmp), his[pronoun_gender(mtmp)]);
+			      Monnam(mtmp), mhis(mtmp));
 			break;
 		case 1: pline_msg = "complains about a diet of mutton.";
 			break;
@@ -817,7 +827,7 @@
     register int tx,ty;
     struct obj *otmp;
 
-    if (youmonst.data->msound == MS_SILENT) {
+    if (is_silent(youmonst.data)) {
 	pline("As %s, you cannot speak.", an(youmonst.data->mname));
 	return(0);
     }
@@ -846,7 +856,7 @@
 	return(1);
     }
 
-    (void) getdir("Talk to whom? [in what direction]");
+    (void) getdir("Talk to whom? (in what direction)");
 
 #ifdef STEED
     if (u.usteed && u.dz > 0)
@@ -898,6 +908,92 @@
     return domonnoise(mtmp);
 }
 
+#ifdef USER_SOUNDS
+
+extern void FDECL(play_usersound, (const char*, int));
+
+typedef struct audio_mapping_rec {
+	struct re_pattern_buffer regex;
+	char *filename;
+	int volume;
+	struct audio_mapping_rec *next;
+} audio_mapping;
+
+static audio_mapping *soundmap = 0;
+
+char* sounddir = ".";
+
+/* adds a sound file mapping, returns 0 on failure, 1 on success */
+int
+add_sound_mapping(mapping)
+const char *mapping;
+{
+	char text[256];
+	char filename[256];
+	char filespec[256];
+	int volume;
+
+	if (sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d",
+		   text, filename, &volume) == 3) {
+	    const char *err;
+	    audio_mapping *new_map;
+
+	    if (strlen(sounddir) + strlen(filename) > 254) {
+		raw_print("sound file name too long");
+		return 0;
+	    }
+	    Sprintf(filespec, "%s/%s", sounddir, filename);
+
+	    if (can_read_file(filespec)) {
+		new_map = (audio_mapping *)alloc(sizeof(audio_mapping));
+		new_map->regex.translate = 0;
+		new_map->regex.fastmap = 0;
+		new_map->regex.buffer = 0;
+		new_map->regex.allocated = 0;
+		new_map->regex.regs_allocated = REGS_FIXED;
+		new_map->filename = strdup(filespec);
+		new_map->volume = volume;
+		new_map->next = soundmap;
+
+		err = re_compile_pattern(text, strlen(text), &new_map->regex);
+
+		if (err) {
+		    raw_print(err);
+		    free(new_map->filename);
+		    free(new_map);
+		    return 0;
+		} else {
+		    soundmap = new_map;
+		}
+	    } else {
+		Sprintf(text, "cannot read %.243s", filespec);
+		raw_print(text);
+		return 0;
+	    }
+	} else {
+	    raw_print("syntax error in SOUND");
+	    return 0;
+	}
+
+	return 1;
+}
+
+void
+play_sound_for_message(msg)
+const char* msg;
+{
+	audio_mapping* cursor = soundmap;
+
+	while (cursor) {
+	    if (re_search(&cursor->regex, msg, strlen(msg), 0, 9999, 0) >= 0) {
+		play_usersound(cursor->filename, cursor->volume);
+	    }
+	    cursor = cursor->next;
+	}
+}
+
+#endif /* USER_SOUNDS */
+
 #endif /* OVLB */
 
 /*sounds.c*/
diff -Naurd ../nethack-3.3.1/src/sp_lev.c ./src/sp_lev.c
--- ../nethack-3.3.1/src/sp_lev.c Sat Aug 5 00:43:26 2000
+++ ./src/sp_lev.c Fri Mar 22 14:40:55 2002
@@ -850,8 +850,8 @@
 
 		    case M_AP_OBJECT:
 			for (i = 0; i < NUM_OBJECTS; i++)
-			    if (!strcmp(OBJ_NAME(objects[i]),
-					m->appear_as.str))
+			    if (OBJ_NAME(objects[i]) &&
+				!strcmp(OBJ_NAME(objects[i]),m->appear_as.str))
 				break;
 			if (i == NUM_OBJECTS) {
 			    impossible(
@@ -912,8 +912,10 @@
     struct obj *otmp;
     schar x, y;
     char c;
+    boolean named;	/* has a name been supplied in level description? */
 
     if (rn2(100) < o->chance) {
+	named = o->name.str ? TRUE : FALSE;
 
 	x = o->x; y = o->y;
 	if (croom)
@@ -929,9 +931,9 @@
 	    c = 0;
 
 	if (!c)
-	    otmp = mkobj_at(RANDOM_CLASS, x, y, TRUE);
+	    otmp = mkobj_at(RANDOM_CLASS, x, y, !named);
 	else if (o->id != -1)
-	    otmp = mksobj_at(o->id, x, y, TRUE);
+	    otmp = mksobj_at(o->id, x, y, TRUE, !named);
 	else {
 	    /*
 	     * The special levels are compiled with the default "text" object
@@ -944,9 +946,9 @@
 
 	    /* KMH -- Create piles of gold properly */
 	    if (oclass == GOLD_CLASS)
-	    	otmp = mkgold(0L, x, y);
+		otmp = mkgold(0L, x, y);
 	    else
-	    	otmp = mkobj_at(oclass, x, y, TRUE);
+		otmp = mkobj_at(oclass, x, y, !named);
 	}
 
 	if (o->spe != -127)	/* That means NOT RANDOM! */
@@ -973,9 +975,8 @@
 		attach_egg_hatch_timeout(otmp);	/* attach new hatch timeout */
 	}
 
-	if (o->name.str) {	/* Give a name to that object */
+	if (named)
 	    otmp = oname(otmp, o->name.str);
-	}
 
 	switch(o->containment) {
 	    static struct obj *container = 0;
@@ -987,7 +988,7 @@
 		    break;
 		}
 		remove_object(otmp);
-		add_to_container(container, otmp);
+		(void) add_to_container(container, otmp);
 		goto o_done;		/* don't stack, but do other cleanup */
 	    /* container */
 	    case 2:
@@ -1025,8 +1026,9 @@
 		obj = was->minvent;
 		obj->owornmask = 0;
 		obj_extract_self(obj);
-		add_to_container(otmp, obj);
+		(void) add_to_container(otmp, obj);
 	    }
+	    otmp->owt = weight(otmp);
 	    mongone(was);
 	}
 
@@ -1128,7 +1130,7 @@
 	levl[x][y].typ = ALTAR;
 	levl[x][y].altarmask = amask;
 
-	if (a->shrine == -11) a->shrine = rn2(1);  /* handle random case */
+	if (a->shrine < 0) a->shrine = rn2(2);	/* handle random case */
 
 	if (oldtyp == FOUNTAIN)
 	    level.flags.nfountains--;
@@ -1309,7 +1311,7 @@
 		if(ftyp != CORR || rn2(100)) {
 			crm->typ = ftyp;
 			if(nxcor && !rn2(50))
-				(void) mksobj_at(BOULDER, xx, yy, TRUE);
+				(void) mksobj_at(BOULDER, xx, yy, TRUE, FALSE);
 		} else {
 			crm->typ = SCORR;
 		}
@@ -2187,7 +2189,8 @@
 			if (x != xstart && (IS_WALL(levl[x-1][y].typ) ||
 					    levl[x-1][y].horizontal))
 			    levl[x][y].horizontal = 1;
-		    } else if(levl[x][y].typ == HWALL)
+		    } else if(levl[x][y].typ == HWALL ||
+				levl[x][y].typ == IRONBARS)
 			levl[x][y].horizontal = 1;
 		    else if(levl[x][y].typ == LAVAPOOL)
 			levl[x][y].lit = 1;
@@ -2584,7 +2587,7 @@
 	    }
 	    for(x = rnd((int) (12 * mapfact) / 100); x; x--) {
 		    maze1xy(&mm, DRY);
-		    (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE);
+		    (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
 	    }
 	    for (x = rn2(2); x; x--) {
 		maze1xy(&mm, DRY);
diff -Naurd ../nethack-3.3.1/src/spell.c ./src/spell.c
--- ../nethack-3.3.1/src/spell.c Wed Feb 16 21:50:45 2000
+++ ./src/spell.c Fri Mar 22 14:40:55 2002
@@ -20,17 +20,17 @@
 #define spellet(spell)	\
 	((char)((spell < 26) ? ('a' + spell) : ('A' + spell - 26)))
 
-static int FDECL(spell_let_to_idx, (CHAR_P));
-static void FDECL(cursed_book, (int));
-static void FDECL(deadbook, (struct obj *));
+STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P));
+STATIC_DCL void FDECL(cursed_book, (int));
+STATIC_DCL void FDECL(deadbook, (struct obj *));
 STATIC_PTR int NDECL(learn);
-static boolean FDECL(getspell, (int *));
-static boolean FDECL(dospellmenu, (const char *,int,int *));
-static int FDECL(percent_success, (int));
-static int NDECL(throwspell);
-static void NDECL(cast_protection);
-static const char *FDECL(spelltypemnemonic, (int));
-static int FDECL(isqrt, (int));
+STATIC_DCL boolean FDECL(getspell, (int *));
+STATIC_DCL boolean FDECL(dospellmenu, (const char *,int,int *));
+STATIC_DCL int FDECL(percent_success, (int));
+STATIC_DCL int NDECL(throwspell);
+STATIC_DCL void NDECL(cast_protection);
+STATIC_DCL const char *FDECL(spelltypemnemonic, (int));
+STATIC_DCL int FDECL(isqrt, (int));
 
 /* The roles[] table lists the role-specific values for tuning
  * percent_success().
@@ -53,7 +53,7 @@
  *
  *	The arms penalty is lessened for trained fighters Bar, Kni, Ran,
  *	Sam, Val -
- *	the penalty is its metal interference, not encumberance.
+ *	the penalty is its metal interference, not encumbrance.
  *	The `spelspec' is a single spell which is fundamentally easier
  *	 for that role to cast.
  *
@@ -89,7 +89,7 @@
 static const char explodes[] = "radiates explosive energy";
 
 /* convert a letter into a number in the range 0..51, or -1 if not a letter */
-static int
+STATIC_OVL int
 spell_let_to_idx(ilet)
 char ilet;
 {
@@ -102,7 +102,7 @@
     return -1;
 }
 
-static void
+STATIC_OVL void
 cursed_book(lev)
 	register int	lev;
 {
@@ -131,10 +131,15 @@
 		    if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {
 			Your("gloves seem unaffected.");
 		    } else if (uarmg->oeroded2 < MAX_ERODE) {
-			Your("gloves corrode%s!",
-			     uarmg->oeroded2+1 == MAX_ERODE ? " completely" :
-			     uarmg->oeroded2 ? " further" : "");
-			uarmg->oeroded2++;
+			if (uarmg->greased) {
+			    grease_protect(uarmg, "gloves", &youmonst);
+			} else {
+			    Your("gloves corrode%s!",
+				 uarmg->oeroded2+1 == MAX_ERODE ?
+				 " completely" : uarmg->oeroded2 ?
+				 " further" : "");
+			    uarmg->oeroded2++;
+			}
 		    } else
 			Your("gloves %s completely corroded.",
 			     Blind ? "feel" : "look");
@@ -162,7 +167,7 @@
 }
 
 /* special effects for The Book of the Dead */
-static void
+STATIC_OVL void
 deadbook(book2)
 struct obj *book2;
 {
@@ -238,7 +243,9 @@
     } else if(book2->blessed) {
 	for(mtmp = fmon; mtmp; mtmp = mtmp2) {
 	    mtmp2 = mtmp->nmon;		/* tamedog() changes chain */
-	    if(!DEADMONSTER(mtmp) && is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {
+	    if (DEADMONSTER(mtmp)) continue;
+
+	    if (is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {
 		mtmp->mpeaceful = TRUE;
 		if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
 		   && distu(mtmp->mx, mtmp->my) < 4)
@@ -247,7 +254,7 @@
 			    mtmp->mtame++;
 		    } else
 			(void) tamedog(mtmp, (struct obj *)0);
-		else mtmp->mflee = TRUE;
+		else monflee(mtmp, 0, FALSE, TRUE);
 	    }
 	}
     } else {
@@ -273,6 +280,8 @@
 	char splname[BUFSZ];
 	boolean costly = TRUE;
 
+	/* JDS: lenses give 50% faster reading; 33% smaller read time */
+	if (delay && ublindf && ublindf->otyp == LENSES && rn2(2)) delay++;
 	if (delay) {	/* not if (delay++), so at end delay == 0 */
 		delay++;
 		return(1); /* still busy */
@@ -372,29 +381,31 @@
 
 		/* Books are often wiser than their readers (Rus.) */
 		spellbook->in_use = TRUE;
-		if (!spellbook->blessed && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
-			if (spellbook->cursed) {
-			    too_hard = TRUE;
-			} else {
-			    /* uncursed - chance to fail */
-			    int read_ability = ACURR(A_INT) + 4 + u.ulevel/2
-					       - 2*objects[booktype].oc_level;
-			    /* only wizards know if a spell is too difficult */
-			    if (Role_if(PM_WIZARD) && read_ability < 20) {
-				char qbuf[QBUFSZ];
-				Sprintf(qbuf,
+		if (!spellbook->blessed &&
+		    spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
+		    if (spellbook->cursed) {
+			too_hard = TRUE;
+		    } else {
+			/* uncursed - chance to fail */
+			int read_ability = ACURR(A_INT) + 4 + u.ulevel/2
+			    - 2*objects[booktype].oc_level
+			    + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0);
+			/* only wizards know if a spell is too difficult */
+			if (Role_if(PM_WIZARD) && read_ability < 20) {
+			    char qbuf[QBUFSZ];
+			    Sprintf(qbuf,
 		      "This spellbook is %sdifficult to comprehend. Continue?",
-					(read_ability < 12 ? "very " : ""));
-				if (yn(qbuf) != 'y') {
-				    spellbook->in_use = FALSE;
-				    return(1);
-				}
-			    }
-			    /* its up to random luck now */
-			    if (rnd(20) > read_ability) {
-				too_hard = TRUE;
+				    (read_ability < 12 ? "very " : ""));
+			    if (yn(qbuf) != 'y') {
+				spellbook->in_use = FALSE;
+				return(1);
 			    }
 			}
+			/* its up to random luck now */
+			if (rnd(20) > read_ability) {
+			    too_hard = TRUE;
+			}
+		    }
 		}
 
 		if (too_hard) {
@@ -442,6 +453,15 @@
 	return(1);
 }
 
+/* a spellbook has been destroyed or the character has changed levels;
+   the stored address for the current book is no longer valid */
+void
+book_disappears(obj)
+struct obj *obj;
+{
+	if (obj == book) book = (struct obj *)0;
+}
+
 /* renaming an object usually results in it having a different address;
    so the sequence start reading, get interrupted, name the book, resume
    reading would read the "new" book from scratch */
@@ -473,7 +493,7 @@
  * Return TRUE if a spell was picked, with the spell index in the return
  * parameter.  Otherwise return FALSE.
  */
-static boolean
+STATIC_OVL boolean
 getspell(spell_no)
 	int *spell_no;
 {
@@ -526,7 +546,7 @@
 	return 0;
 }
 
-static const char *
+STATIC_OVL const char *
 spelltypemnemonic(skill)
 int skill;
 {
@@ -558,7 +578,7 @@
 	return (objects[booktype].oc_skill);
 }
 
-static void
+STATIC_OVL void
 cast_protection()
 {
 	int loglev = 0;
@@ -742,15 +762,17 @@
 		    n=rnd(8)+1;
 		    while(n--) {
 			if(!u.dx && !u.dy && !u.dz) {
-			    if ((damage = zapyourself(pseudo, TRUE)) != 0)
-				losehp(damage,
-				     self_pronoun("zapped %sself with a spell",
-						"him"),
-				       NO_KILLER_PREFIX);
+			    if ((damage = zapyourself(pseudo, TRUE)) != 0) {
+				char buf[BUFSZ];
+				Sprintf(buf, "zapped %sself with a spell", uhim());
+				losehp(damage, buf, NO_KILLER_PREFIX);
+			    }
 			} else {
 			    explode(u.dx, u.dy,
 				    pseudo->otyp - SPE_MAGIC_MISSILE + 10,
-				    u.ulevel/2 + 1 + spell_damage_bonus(), 0);
+				    u.ulevel/2 + 1 + spell_damage_bonus(), 0,
+					(pseudo->otyp == SPE_CONE_OF_COLD) ?
+						EXPL_FROSTY : EXPL_FIERY);
 			}
 			u.dx = cc.x+rnd(3)-2; u.dy = cc.y+rnd(3)-2;
 			if (!isok(u.dx,u.dy) || !cansee(u.dx,u.dy) ||
@@ -787,13 +809,14 @@
 			if (atme) u.dx = u.dy = u.dz = 0;
 			else (void) getdir((char *)0);
 			if(!u.dx && !u.dy && !u.dz) {
-			    if ((damage = zapyourself(pseudo, TRUE)) != 0)
-				losehp(damage,
-				     self_pronoun("zapped %sself with a spell",
-						  "him"),
-				     NO_KILLER_PREFIX);
+			    if ((damage = zapyourself(pseudo, TRUE)) != 0) {
+				char buf[BUFSZ];
+				Sprintf(buf, "zapped %sself with a spell", uhim());
+				losehp(damage, buf, NO_KILLER_PREFIX);
+			    }
 			} else weffects(pseudo);
 		} else weffects(pseudo);
+		update_inventory();	/* spell may modify inventory */
 		break;
 
 	/* these are all duplicates of scroll effects */
@@ -832,6 +855,7 @@
 		if (Slimed) {
 		    pline_The("slime disappears!");
 		    Slimed = 0;
+		 /* flags.botl = 1; -- healup() handles this */
 		}
 		healup(0, 0, TRUE, FALSE);
 		break;
@@ -868,7 +892,7 @@
 }
 
 /* Choose location where spell takes effect. */
-static int
+STATIC_OVL int
 throwspell()
 {
 	coord cc;
@@ -948,7 +972,7 @@
 	return 0;
 }
 
-static boolean
+STATIC_OVL boolean
 dospellmenu(prompt, splaction, spell_no)
 const char *prompt;
 int splaction;	/* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */
@@ -973,10 +997,14 @@
 	 * To do it right would require that we implement columns
 	 * in the window-ports (say via a tab character).
 	 */
-	Sprintf(buf, "%-20s     Level  %-12s Fail", "    Name", "Category");
+	if (!iflags.menu_tab_sep)
+		Sprintf(buf, "%-20s     Level  %-12s Fail", "    Name", "Category");
+	else
+		Sprintf(buf, "Name\tLevel\tCategory\tFail");
 	add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
 	for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) {
-	        Sprintf(buf, "%-20s  %2d%s   %-12s %3d%%",
+		Sprintf(buf, iflags.menu_tab_sep ?
+			"%s\t%-d%s\t%s\t%-d%%" : "%-20s  %2d%s   %-12s %3d%%",
 			spellname(i), spellev(i),
 			spellknow(i) ? " " : "*",
 			spelltypemnemonic(spell_skilltype(spellid(i))),
@@ -1015,7 +1043,7 @@
 }
 
 /* Integer square root function without using floating point. */
-static int
+STATIC_OVL int
 isqrt(val)
 int val;
 {
@@ -1029,7 +1057,7 @@
     return rt;
 }
 
-static int
+STATIC_OVL int
 percent_success(spell)
 int spell;
 {
@@ -1084,7 +1112,8 @@
 	 * The difficulty is based on the hero's level and their skill level
 	 * in that spell type.
 	 */
-	skill = P_SKILL(spell_skilltype(spellid(spell)))-1;
+	skill = P_SKILL(spell_skilltype(spellid(spell)));
+	skill = max(skill,P_UNSKILLED) - 1;	/* unskilled => 0 */
 	difficulty= (spellev(spell)-1) * 4 - ((skill * 6) + (u.ulevel/3) + 1);
 
 	if (difficulty > 0) {
@@ -1122,8 +1151,8 @@
 
 	/* Finally, chance (based on player intell/wisdom and level) is
 	 * combined with ability (based on player intrinsics and
-	 * encumberances).  No matter how intelligent/wise and advanced
-	 * a player is, intrinsics and encumberance can prevent casting;
+	 * encumbrances).  No matter how intelligent/wise and advanced
+	 * a player is, intrinsics and encumbrance can prevent casting;
 	 * and no matter how able, learning is always required.
 	 */
 	chance = chance * (20-splcaster) / 15 - splcaster;
@@ -1160,5 +1189,4 @@
 	return;
 }
 
-
 /*spell.c*/
diff -Naurd ../nethack-3.3.1/src/steal.c ./src/steal.c
--- ../nethack-3.3.1/src/steal.c Sun Jul 16 02:50:17 2000
+++ ./src/steal.c Fri Mar 22 14:41:07 2002
@@ -20,7 +20,7 @@
 		(otmp == uarmf) ? "boots" :
 		(otmp == uarms) ? "shield" :
 		(otmp == uarmg) ? "gloves" :
-		(otmp == uarmc) ? "cloak" :
+		(otmp == uarmc) ? cloak_simple_name(otmp) :
 		(otmp == uarmh) ? "helmet" : "armor");
 }
 
@@ -50,18 +50,19 @@
 		    Monnam(mtmp), makeplural(body_part(FOOT)));
 	    if(!u.ugold || !rn2(5)) {
 		if (!tele_restrict(mtmp)) rloc(mtmp);
-		mtmp->mflee = 1;
+		monflee(mtmp, 0, FALSE, FALSE);
 	    }
 	} else if(u.ugold) {
 	    u.ugold -= (tmp = somegold());
 	    Your("purse feels lighter.");
 	    mtmp->mgold += tmp;
-	    if (!tele_restrict(mtmp)) rloc(mtmp);
-	    mtmp->mflee = 1;
+	if (!tele_restrict(mtmp)) rloc(mtmp);
+	    monflee(mtmp, 0, FALSE, FALSE);
 	    flags.botl = 1;
 	}
 }
 
+
 /* steal armor after you finish taking it off */
 unsigned int stealoid;		/* object to be stolen */
 unsigned int stealmid;		/* monster doing the stealing */
@@ -84,7 +85,7 @@
 			freeinv(otmp);
 			pline("%s steals %s!", Monnam(mtmp), doname(otmp));
 			(void) mpickobj(mtmp,otmp);	/* may free otmp */
-			mtmp->mflee = 1;
+			monflee(mtmp, 0, FALSE, FALSE);
 			if (!tele_restrict(mtmp)) rloc(mtmp);
 		        break;
 		    }
@@ -96,7 +97,7 @@
 	return 0;
 }
 
-/* An object you're wearing has been taken off my a monster (theft or
+/* An object you're wearing has been taken off by a monster (theft or
    seduction).  Also used if a worn item gets transformed (stone to flesh). */
 void
 remove_worn_item(obj)
@@ -107,18 +108,11 @@
 	if (!obj->owornmask)
 	    return;
 
-	switch (obj->oclass) {
-	 case TOOL_CLASS:
-	    if (obj == ublindf) Blindf_off(obj);
-	    break;
-	 case AMULET_CLASS:
-	    Amulet_off();
-	    break;
-	 case RING_CLASS:
-	 case FOOD_CLASS: /* meat ring */
-	    Ring_gone(obj);
-	    break;
-	 case ARMOR_CLASS:
+	if (obj->owornmask & W_ARMOR) {
+	    if (obj == uskin) {
+		impossible("Removing embedded scales?");
+		skinback(TRUE);		/* uarm = uskin; uskin = 0; */
+	    }
 	    if (obj == uarm) (void) Armor_off();
 	    else if (obj == uarmc) (void) Cloak_off();
 	    else if (obj == uarmf) (void) Boots_off();
@@ -126,12 +120,25 @@
 	    else if (obj == uarmh) (void) Helmet_off();
 	    else if (obj == uarms) (void) Shield_off();
 	    else setworn((struct obj *)0, obj->owornmask & W_ARMOR);
-	    break;
-	 default:
-	    /* shouldn't reach here, but just in case... */
-	    setnotworn(obj);
-	    break;
+	} else if (obj->owornmask & W_AMUL) {
+	    Amulet_off();
+	} else if (obj->owornmask & W_RING) {
+	    Ring_gone(obj);
+	} else if (obj->owornmask & W_TOOL) {
+	    Blindf_off(obj);
+	} else if (obj->owornmask & (W_BALL|W_CHAIN)) {
+	    unpunish();
+	} else if (obj->owornmask & (W_WEP|W_SWAPWEP|W_QUIVER)) {
+	    if (obj == uwep)
+		uwepgone();
+	    else if (obj == uswapwep)
+		uswapwepgone();
+	    else if (obj == uquiver)
+		uqwepgone();
 	}
+
+	/* catchall */
+	if (obj->owornmask) setnotworn(obj);
 }
 
 /* Returns 1 when something was stolen (or at least, when N should flee now)
@@ -139,15 +146,23 @@
  * Avoid stealing the object stealoid
  */
 int
-steal(mtmp)
+steal(mtmp, objnambuf)
 struct monst *mtmp;
+char *objnambuf;
 {
 	struct obj *otmp;
-	int tmp, could_petrify, named = 0;
+	int tmp, could_petrify, named = 0, armordelay;
+	boolean monkey_business; /* true iff an animal is doing the thievery */
 
+	if (objnambuf) *objnambuf = '\0';
 	/* the following is true if successful on first of two attacks. */
 	if(!monnear(mtmp, u.ux, u.uy)) return(0);
 
+	/* food being eaten might already be used up but will not have
+	   been removed from inventory yet; we don't want to steal that,
+	   so this will cause it to be removed now */
+	if (occupation) (void) maybe_finished_meal(FALSE);
+
 	if (!invent || (inv_cnt() == 1 && uskin)) {
 nothing_to_steal:
 	    /* Not even a thousand men in armor can strip a naked man. */
@@ -159,7 +174,10 @@
 	    return(1);	/* let her flee */
 	}
 
-	if (Adornment & LEFT_RING) {
+	monkey_business = is_animal(mtmp->data);
+	if (monkey_business) {
+	    ;	/* skip ring special cases */
+	} else if (Adornment & LEFT_RING) {
 	    otmp = uleft;
 	    goto gotobj;
 	} else if (Adornment & RIGHT_RING) {
@@ -183,7 +201,7 @@
 #ifdef INVISIBLE_OBJECTS
 				&& (!otmp->oinvis || perceives(mtmp->data))
 #endif
-	    		)
+			)
 		if((tmp -= ((otmp->owornmask &
 			(W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1)) < 0)
 			break;
@@ -203,7 +221,39 @@
 gotobj:
 	if(otmp->o_id == stealoid) return(0);
 
-	if(otmp->otyp == LEASH && otmp->leashmon) o_unleash(otmp);
+	/* animals can't overcome curse stickiness nor unlock chains */
+	if (monkey_business) {
+	    boolean ostuck;
+	    /* is the player prevented from voluntarily giving up this item?
+	       (ignores loadstones; the !can_carry() check will catch those) */
+	    if (otmp == uball)
+		ostuck = TRUE;	/* effectively worn; curse is implicit */
+	    else if (otmp == uquiver || (otmp == uswapwep && !u.twoweap))
+		ostuck = FALSE;	/* not really worn; curse doesn't matter */
+	    else
+		ostuck = (otmp->cursed && otmp->owornmask);
+
+	    if (ostuck || !can_carry(mtmp, otmp)) {
+		static const char *how[] = { "steal","snatch","grab","take" };
+ cant_take:
+		pline("%s tries to %s your %s but gives up.",
+		      Monnam(mtmp), how[rn2(SIZE(how))],
+		      (otmp->owornmask & W_ARMOR) ? equipname(otmp) :
+		       cxname(otmp));
+		/* the fewer items you have, the less likely the thief
+		   is going to stick around to try again (0) instead of
+		   running away (1) */
+		return !rn2(inv_cnt() / 5 + 2);
+	    }
+	}
+
+	if (otmp->otyp == LEASH && otmp->leashmon) {
+	    if (monkey_business && otmp->cursed) goto cant_take;
+	    o_unleash(otmp);
+	}
+
+	/* you're going to notice the theft... */
+	stop_occupation();
 
 	if((otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))){
 		switch(otmp->oclass) {
@@ -211,33 +261,43 @@
 		case AMULET_CLASS:
 		case RING_CLASS:
 		case FOOD_CLASS: /* meat ring */
+		    remove_worn_item(otmp);
+		    break;
+		case ARMOR_CLASS:
+		    armordelay = objects[otmp->otyp].oc_delay;
+		    /* Stop putting on armor which has been stolen. */
+		    if (donning(otmp)) {
 			remove_worn_item(otmp);
 			break;
-		case ARMOR_CLASS:
-			/* Stop putting on armor which has been stolen. */
-			if (donning(otmp) || is_animal(mtmp->data)) {
-			    remove_worn_item(otmp);
-			    break;
-			} else {
+		    } else if (monkey_business) {
+			/* animals usually don't have enough patience
+			   to take off items which require extra time */
+			if (armordelay >= 1 && rn2(10)) goto cant_take;
+			remove_worn_item(otmp);
+			break;
+		    } else {
 			int curssv = otmp->cursed;
+			int slowly;
 
 			otmp->cursed = 0;
-			stop_occupation();
+			/* can't charm you without first waking you */
+			if (multi < 0 && is_fainted()) unmul((char *)0);
+			slowly = (armordelay >= 1 || multi < 0);
 			if(flags.female)
 			    pline("%s charms you.  You gladly %s your %s.",
 				  Blind ? "She" : Monnam(mtmp),
 				  curssv ? "let her take" :
-	(objects[otmp->otyp].oc_delay > 1) ? "start removing" : "hand over",
+				  slowly ? "start removing" : "hand over",
 				  equipname(otmp));
 			else
 			    pline("%s seduces you and %s off your %s.",
 				  Blind ? "It" : Adjmonnam(mtmp, "beautiful"),
 				  curssv ? "helps you to take" :
-	(objects[otmp->otyp].oc_delay > 1) ? "you start taking" : "you take",
+				  slowly ? "you start taking" : "you take",
 				  equipname(otmp));
 			named++;
 			/* the following is to set multi for later on */
-			nomul(-objects[otmp->otyp].oc_delay);
+			nomul(-armordelay);
 			remove_worn_item(otmp);
 			otmp->cursed = curssv;
 			if(multi < 0){
@@ -252,20 +312,22 @@
 				return(0);
 			}
 		    }
-			break;
+		    break;
 		default:
-			impossible("Tried to steal a strange worn thing.");
+		    impossible("Tried to steal a strange worn thing. [%d]",
+			       otmp->oclass);
 		}
 	}
-	else if (otmp == uwep) uwepgone();
-	else if (otmp == uquiver) uqwepgone();
-	else if (otmp == uswapwep) uswapwepgone();
+	else if (otmp->owornmask)
+	    remove_worn_item(otmp);
 
-	if(otmp == uball) unpunish();
+	/* do this before removing it from inventory */
+	if (objnambuf) Strcpy(objnambuf, yname(otmp));
 
 	freeinv(otmp);
 	pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp));
-	could_petrify = otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]);
+	could_petrify = (otmp->otyp == CORPSE &&
+			 touch_petrifies(&mons[otmp->corpsenm]));
 	(void) mpickobj(mtmp,otmp);	/* may free otmp */
 	if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) {
 	    minstapetrify(mtmp, TRUE);
@@ -290,26 +352,25 @@
 	obfree(otmp, (struct obj *)0);
 	freed_otmp = 1;
     } else {
-	boolean snuff_otmp = FALSE;
-	/* don't want hidden light source inside the monster; assumes that
-	   engulfers won't have external inventories; whirly monsters cause
-	   the light to be extinguished rather than letting it shine thru */
-	if (otmp->lamplit &&  /* hack to avoid function calls for most objs */
-		obj_sheds_light(otmp) &&
-		attacktype(mtmp->data, AT_ENGL)) {
-	    /* this is probably a burning object that you dropped or threw */
-	    if (u.uswallow && mtmp == u.ustuck && !Blind)
-		pline("%s go%s out.", The(xname(otmp)),
-		      otmp->quan == 1L ? "es" : "");
-	    snuff_otmp = TRUE;
-	}
-	/* Must do carrying effects on object prior to add_to_minv() */
-	carry_obj_effects(otmp);
-	/* add_to_minv() might free otmp [if merged with something else],
-	   so we have to call it after doing the object checks */
-	freed_otmp = add_to_minv(mtmp, otmp);
-	/* and we had to defer this until object is in mtmp's inventory */
-	if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my);
+    boolean snuff_otmp = FALSE;
+    /* don't want hidden light source inside the monster; assumes that
+       engulfers won't have external inventories; whirly monsters cause
+       the light to be extinguished rather than letting it shine thru */
+    if (otmp->lamplit &&  /* hack to avoid function calls for most objs */
+      	obj_sheds_light(otmp) &&
+	attacktype(mtmp->data, AT_ENGL)) {
+	/* this is probably a burning object that you dropped or threw */
+	if (u.uswallow && mtmp == u.ustuck && !Blind)
+	    pline("%s out.", Tobjnam(otmp, "go"));
+	snuff_otmp = TRUE;
+    }
+    /* Must do carrying effects on object prior to add_to_minv() */
+    carry_obj_effects(otmp);
+    /* add_to_minv() might free otmp [if merged with something else],
+       so we have to call it after doing the object checks */
+    freed_otmp = add_to_minv(mtmp, otmp);
+    /* and we had to defer this until object is in mtmp's inventory */
+    if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my);
     }
     return freed_otmp;
 }
@@ -399,6 +460,13 @@
 				continue;
 			}
 			mtmp->misc_worn_check &= ~(otmp->owornmask);
+#ifdef STEED
+			/* don't charge for an owned saddle on dead pet */
+			if (mtmp->mtame && mtmp->mhp == 0 &&
+			    (otmp->owornmask & W_SADDLE) && !otmp->unpaid &&
+			    costly_spot(mtmp->mx, mtmp->my))
+				otmp->no_charge = 1;
+#endif
 			otmp->owornmask = 0L;
 		}
 		if (is_pet && cansee(omx, omy) && flags.verbose)
@@ -413,7 +481,6 @@
 	    keepobj = otmp->nobj;
 	    (void) add_to_minv(mtmp, otmp);
 	}
-
 	if (mtmp->mgold) {
 		register long g = mtmp->mgold;
 		(void) mkgold(g, omx, omy);
@@ -422,6 +489,7 @@
 				g, plur(g));
 		mtmp->mgold = 0L;
 	}
+	
 	if (show & cansee(omx, omy))
 		newsym(omx, omy);
 }
diff -Naurd ../nethack-3.3.1/src/steed.c ./src/steed.c
--- ../nethack-3.3.1/src/steed.c Thu Aug 3 21:00:42 2000
+++ ./src/steed.c Fri Mar 22 14:40:55 2002
@@ -12,6 +12,7 @@
 	S_QUADRUPED, S_UNICORN, S_ANGEL, S_CENTAUR, S_DRAGON, S_JABBERWOCK, '\0'
 };
 
+STATIC_DCL boolean FDECL(landing_spot, (coord *, int));
 
 /*** Putting the saddle on ***/
 
@@ -200,6 +201,26 @@
 	    pline("Maybe you should find a designated driver.");
 	    return (FALSE);
 	}
+	/* While riding Wounded_legs refers to the steed's,
+	 * not the hero's legs.
+	 * That opens up a potential abuse where the player
+	 * can mount a steed, then dismount immediately to
+	 * heal leg damage, because leg damage is always
+	 * healed upon dismount (Wounded_legs context switch).
+	 * By preventing a hero with Wounded_legs from
+	 * mounting a steed, the potential for abuse is
+	 * minimized, if not eliminated altogether.
+	 */
+	if (Wounded_legs) {
+	    Your("%s are in no shape for riding.", makeplural(body_part(LEG)));
+#ifdef WIZARD
+	    if (force && wizard && yn("Heal your legs?") == 'y')
+		HWounded_legs = EWounded_legs = 0;
+	    else
+#endif
+	    return (FALSE);
+	}
+	
 	if (Upolyd && (!humanoid(youmonst.data) || verysmall(youmonst.data) ||
 			bigmonst(youmonst.data))) {
 	    if (!force)
@@ -251,7 +272,11 @@
 	    return (FALSE);
 	}
 	if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) {
-	    pline("%s resists!", Monnam(mtmp));
+	    /* no longer tame */
+	    newsym(mtmp->mx, mtmp->my);
+	    pline("%s resists%s!", Monnam(mtmp),
+		  mtmp->mleashed ? " and its leash comes off" : "");
+	    if (mtmp->mleashed) m_unleash(mtmp, FALSE);
 	    return (FALSE);
 	}
 	if (!force && Underwater && !is_swimmer(ptr)) {
@@ -278,8 +303,13 @@
 	    return (FALSE);
 	}
 	if (!force && (Confusion || Fumbling || Glib || Wounded_legs ||
-			otmp->cursed || (u.ulevel+mtmp->mtame < rnd(MAXULEV/2+5)))) {
+		otmp->cursed || (u.ulevel+mtmp->mtame < rnd(MAXULEV/2+5)))) {
+	    if (Levitation) {
+		pline("%s slips away from you.", Monnam(mtmp));
+		return FALSE;
+	    }
 	    You("slip while trying to get on %s.", mon_nam(mtmp));
+
 	    /* Unfortunately we don't have a version of the monster-naming
 	     * function that works well with "a" and "the" but ignores
 	     * hallucination.  Fortunately, we know the monster must be saddled
@@ -327,12 +357,40 @@
 void
 kick_steed()
 {
+	char He[4];
 	if (!u.usteed)
 	    return;
 
+	/* [ALI] Various effects of kicking sleeping/paralyzed steeds */
+	if (u.usteed->msleeping || !u.usteed->mcanmove) {
+	    /* We assume a message has just been output of the form
+	     * "You kick <steed>."
+	     */
+	    Strcpy(He, mhe(u.usteed));
+	    *He = highc(*He);
+	    if ((u.usteed->mcanmove || u.usteed->mfrozen) && !rn2(2)) {
+		if (u.usteed->mcanmove)
+		    u.usteed->msleeping = 0;
+		else if (u.usteed->mfrozen > 2)
+		    u.usteed->mfrozen -= 2;
+		else {
+		    u.usteed->mfrozen = 0;
+		    u.usteed->mcanmove = 1;
+		}
+		if (u.usteed->msleeping || !u.usteed->mcanmove)
+		    pline("%s stirs.", He);
+		else
+		    pline("%s rouses %sself!", He, mhim(u.usteed));
+	    } else
+		pline("%s does not respond.", He);
+	    return;
+	}
+
 	/* Make the steed less tame and check if it resists */
 	if (u.usteed->mtame) u.usteed->mtame--;
+	if (!u.usteed->mtame && u.usteed->mleashed) m_unleash(u.usteed, TRUE);
 	if (!u.usteed->mtame || (u.ulevel+u.usteed->mtame < rnd(MAXULEV/2+5))) {
+	    newsym(u.usteed->mx, u.usteed->my);
 	    dismount_steed(DISMOUNT_THROWN);
 	    return;
 	}
@@ -342,6 +400,45 @@
 	return;
 }
 
+/*
+ * Try to find a dismount point adjacent to the steed's location.
+ * If all else fails, try enexto().  Use enexto() as a last resort because
+ * enexto() chooses its point randomly, possibly even outside the
+ * room's walls, which is not what we want.
+ * Adapted from mail daemon code.
+ */
+STATIC_OVL boolean
+landing_spot(spot, forceit)
+coord *spot;	/* landing position (we fill it in) */
+int forceit;
+{
+    int x, y, distance, min_distance = -1;
+    boolean found = FALSE;
+    
+    for (x = u.ux-1; x <= u.ux+1; x++)
+  	for (y = u.uy-1; y <= u.uy+1; y++) {
+	    if (!isok(x, y) || (x == u.ux && y == u.uy)) continue;
+
+	    if (ACCESSIBLE(levl[x][y].typ) &&
+			!MON_AT(x,y) && !closed_door(x,y)) {
+		distance = distu(x,y);
+		if (min_distance < 0 || distance < min_distance ||
+			(distance == min_distance && rn2(2))) {
+		    spot->x = x;
+		    spot->y = y;
+		    min_distance = distance;
+		    found = TRUE;
+		}
+	    }
+	}
+    
+    /* If we didn't find a good spot and forceit is on, try enexto(). */
+    if (forceit && min_distance < 0 &&
+		!enexto(spot, u.ux, u.uy, youmonst.data))
+	return FALSE;
+
+    return found;
+}
 
 /* Stop riding the current steed */
 void
@@ -354,10 +451,11 @@
 	const char *verb = "fall";
 	boolean repair_leg_damage = TRUE;
 	unsigned save_utrap = u.utrap;
+	boolean have_spot = landing_spot(&cc,0);
 	
-	/* Sanity checks */
-	if (!(mtmp = u.usteed))
-	    /* Just return silently */
+	mtmp = u.usteed;		/* make a copy of steed pointer */
+	/* Sanity check */
+	if (!mtmp)		/* Just return silently */
 	    return;
 
 	/* Check the reason for dismounting */
@@ -367,17 +465,21 @@
 		verb = "are thrown";
 	    case DISMOUNT_FELL:
 		You("%s off of %s!", verb, mon_nam(mtmp));
+		if (!have_spot) have_spot = landing_spot(&cc,1);
 		losehp(rn1(10,10), "riding accident", KILLED_BY_AN);
-		HWounded_legs += rn1(5, 5);
-		EWounded_legs |= BOTH_SIDES;
+		set_wounded_legs(BOTH_SIDES, (int)HWounded_legs + rn1(5,5));
 		repair_leg_damage = FALSE;
 		break;
 	    case DISMOUNT_POLY:
 		You("can no longer ride %s.", mon_nam(u.usteed));
+		if (!have_spot) have_spot = landing_spot(&cc,1);
 		break;
 	    case DISMOUNT_ENGULFED:
 		/* caller displays message */
 		break;
+	    case DISMOUNT_BONES:
+		/* hero has just died... */
+		break;
 	    case DISMOUNT_GENERIC:
 		/* no messages, just make it so */
 		break;
@@ -388,15 +490,19 @@
 		    otmp->bknown = TRUE;
 		    return;
 		}
+		if (!have_spot) {
+		    You("can't. There isn't anywhere for you to stand.");
+		    return;
+		}
 		if (!mtmp->mnamelth) {
 			pline("You've been through the dungeon on %s with no name.",
-	    			an(mtmp->data->mname));
+				an(mtmp->data->mname));
 			if (Hallucination)
 				pline("It felt good to get out of the rain.");
 		} else
 			You("dismount %s.", mon_nam(mtmp));
 	}
- 	/* While riding these refer to the steed's legs
+	/* While riding these refer to the steed's legs
 	 * so after dismounting they refer to the player's
 	 * legs once again.
 	 */
@@ -406,10 +512,19 @@
 	u.usteed = 0;
 	u.ugallop = 0L;
 
-	/* Set player and steed's position.  Try moving the player first */
+	/* Set player and steed's position.  Try moving the player first
+	   unless we're in the midst of creating a bones file. */
+	if (reason == DISMOUNT_BONES) {
+	    /* move the steed to an adjacent square */
+	    if (enexto(&cc, u.ux, u.uy, mtmp->data))
+		rloc_to(mtmp, cc.x, cc.y);
+	    else	/* evidently no room nearby; move steed elsewhere */
+		rloc(mtmp);
+	    return;
+	}
 	if (!DEADMONSTER(mtmp)) {
 	    place_monster(mtmp, u.ux, u.uy);
-	    if (!u.uswallow && !u.ustuck && enexto(&cc, u.ux, u.uy, youmonst.data)) {
+	    if (!u.uswallow && !u.ustuck && have_spot) {
 		struct permonst *mdat = mtmp->data;
 
 		/* The steed may drop into water/lava */
@@ -447,15 +562,19 @@
 	     * able to walk onto a square with a hole, and autopickup before
 	     * falling into the hole).
 	     */
-		/* Keep steed here, move the player to cc; teleds() clears u.utrap */
-		in_steed_dismounting = TRUE;
-		teleds(cc.x, cc.y);
-		in_steed_dismounting = FALSE;
-
-		/* Put your steed in your trap */
-		if (save_utrap)
-		    (void) mintrap(mtmp);
+		/* [ALI] No need to move the player if the steed died. */
+		if (!DEADMONSTER(mtmp)) {
+		    /* Keep steed here, move the player to cc;
+		     * teleds() clears u.utrap
+		     */
+		    in_steed_dismounting = TRUE;
+		    teleds(cc.x, cc.y);
+		    in_steed_dismounting = FALSE;
 
+		    /* Put your steed in your trap */
+		    if (save_utrap)
+			(void) mintrap(mtmp);
+		}
 	    /* Couldn't... try placing the steed */
 	    } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) {
 		/* Keep player here, move the steed to cc */
@@ -469,7 +588,9 @@
 	}
 
 	/* Return the player to the floor */
+	in_steed_dismounting = TRUE;
 	(void) float_down(0L, W_SADDLE);
+	in_steed_dismounting = FALSE;
 	flags.botl = 1;
 	if (reason != DISMOUNT_ENGULFED) {
 		(void)encumber_msg();
diff -Naurd ../nethack-3.3.1/src/teleport.c ./src/teleport.c
--- ../nethack-3.3.1/src/teleport.c Sun Jul 16 16:11:06 2000
+++ ./src/teleport.c Fri Mar 22 14:40:55 2002
@@ -204,7 +204,19 @@
 teleds(nux, nuy)
 register int nux,nuy;
 {
-	if (Punished) unplacebc();
+	boolean dont_teleport_ball = FALSE;
+
+	if (Punished) {
+	    /* If they're teleporting to a position where the ball doesn't need
+	     * to be moved, don't place the ball.  Especially useful when this
+	     * function is being called for crawling out of water instead of
+	     * real teleportation.
+	     */
+	    if (!carried(uball) && distmin(nux, nuy, uball->ox, uball->oy) <= 2)
+		dont_teleport_ball = TRUE;
+	    else
+		unplacebc();
+	}
 	u.utrap = 0;
 	u.ustuck = 0;
 	u.ux0 = u.ux;
@@ -228,7 +240,20 @@
 		u.uswldtim = u.uswallow = 0;
 		docrt();
 	}
-	if (Punished) placebc();
+	if (Punished) {
+	    if (dont_teleport_ball) {
+		int bc_control;
+		xchar ballx, bally, chainx, chainy;
+		boolean cause_delay;
+
+		/* this should only drag the chain (and never give a near-
+		   capacity message) since we already checked ball distance */
+		(void) drag_ball(u.ux, u.uy, &bc_control, &ballx, &bally,
+					&chainx, &chainy, &cause_delay);
+		move_bc(0, bc_control, ballx, bally, chainx, chainy);
+	    } else
+		 placebc();
+	}
 	initrack(); /* teleports mess up tracking monsters without this */
 	update_player_regions();
 #ifdef STEED
@@ -248,6 +273,7 @@
 	see_monsters();
 	vision_full_recalc = 1;
 	nomul(0);
+	vision_recalc(0);	/* vision before effects */
 	spoteffects(TRUE);
 	invocation_message();
 }
@@ -306,7 +332,7 @@
 	    } else {
 		Your("leash goes slack.");
  release_it:
-		m_unleash(mtmp);
+		m_unleash(mtmp, FALSE);
 		return TRUE;
 	    }
 	}
@@ -485,6 +511,7 @@
 	register int newlev;
 	d_level newlevel;
 	const char *escape_by_flying = 0;	/* when surviving dest of -N */
+	char buf[BUFSZ];
 
 	if ((u.uhave.amulet || In_endgame(&u.uz) || In_sokoban(&u.uz))
 #ifdef WIZARD
@@ -499,7 +526,7 @@
 	   || wizard
 #endif
 		) {
-	    char buf[BUFSZ], qbuf[BUFSZ];
+	    char qbuf[BUFSZ];
 	    int trycnt = 0;
 
 	    Strcpy(qbuf, "To what level do you want to teleport?");
@@ -520,7 +547,8 @@
 		if (trycnt >= 10)
 		    goto random_levtport;
 		if (ynq("Go to Nowhere.  Are you sure?") != 'y') return;
-		You("scream in agony as your body begins to warp...");
+		You("%s in agony as your body begins to warp...",
+		    is_silent(youmonst.data) ? "writhe" : "scream");
 		display_nhwindow(WIN_MESSAGE, FALSE);
 		You("cease to exist.");
 		killer_format = NO_KILLER_PREFIX;
@@ -598,9 +626,11 @@
 		} else {
 		    pline("Unfortunately, you don't know how to fly.");
 		    You("plummet a few thousand feet to your death.");
+		    Sprintf(buf,
+				"teleported out of the dungeon and fell to %s death",
+				uhis());
+		    killer = buf;
 		    killer_format = NO_KILLER_PREFIX;
-		    killer =
-    self_pronoun("teleported out of the dungeon and fell to %s death","his");
 		}
 	}
 
@@ -791,6 +821,7 @@
 register int x, y;
 {
 	register int oldx = mtmp->mx, oldy = mtmp->my;
+	boolean resident_shk = mtmp->isshk && inhishop(mtmp);
 
 	if (x == mtmp->mx && y == mtmp->my)	/* that was easy */
 		return;
@@ -820,6 +851,11 @@
 
 	newsym(x, y);				/* update new location */
 	set_apparxy(mtmp);			/* orient monster */
+
+	/* shopkeepers will only teleport if you zap them with a wand of
+	   teleportation or if they've been transformed into a jumpy monster;
+	   the latter only happens if you've attacked them with polymorph */
+	if (resident_shk && !inhishop(mtmp)) make_angry_shk(mtmp, oldx, oldy);
 }
 
 /* place a monster at a random location, typically due to teleport */
@@ -828,7 +864,6 @@
 struct monst *mtmp;	/* mx==0 implies migrating monster arrival */
 {
 	register int x, y, trycount;
-	xchar omx = mtmp->mx, omy = mtmp->my;
 
 #ifdef STEED
 	if (mtmp == u.usteed) {
@@ -837,7 +872,7 @@
 	}
 #endif
 
-	if (mtmp->iswiz && omx) {	/* Wizard, not just arriving */
+	if (mtmp->iswiz && mtmp->mx) {	/* Wizard, not just arriving */
 	    if (!In_W_tower(u.ux, u.uy, &u.uz))
 		x = xupstair,  y = yupstair;
 	    else if (!xdnladder)	/* bottom level of tower */
@@ -872,10 +907,6 @@
 
  found_xy:
 	rloc_to(mtmp, x, y);
-	/* shopkeepers will only teleport if you zap them with a wand of
-	   teleportation or if they've been transformed into a jumpy monster;
-	   the latter only happens if you've attacked them with polymorph */
-	if (mtmp->isshk && !inhishop(mtmp)) make_angry_shk(mtmp, omx, omy);
 }
 
 STATIC_OVL void
@@ -1118,14 +1149,17 @@
 	    if (give_feedback)
 		pline("%s resists your magic!", Monnam(mtmp));
 	    return FALSE;
-	} else {
-	    if (is_rider(mtmp->data) && rn2(13) &&
-		    enexto(&cc, u.ux, u.uy, mtmp->data))
-		rloc_to(mtmp, cc.x, cc.y);
-	    else
-		rloc(mtmp);
-	    return TRUE;
-	}
+	} else if (level.flags.noteleport && u.uswallow && mtmp == u.ustuck) {
+	    if (give_feedback)
+		You("are no longer inside %s!", mon_nam(mtmp));
+	    unstuck(mtmp);
+	    rloc(mtmp);
+	} else if (is_rider(mtmp->data) && rn2(13) &&
+		   enexto(&cc, u.ux, u.uy, mtmp->data))
+	    rloc_to(mtmp, cc.x, cc.y);
+	else
+	    rloc(mtmp);
+	return TRUE;
 }
 
 /*teleport.c*/
diff -Naurd ../nethack-3.3.1/src/timeout.c ./src/timeout.c
--- ../nethack-3.3.1/src/timeout.c Sun Jul 16 02:15:53 2000
+++ ./src/timeout.c Fri Mar 22 14:40:55 2002
@@ -12,6 +12,7 @@
 STATIC_DCL void NDECL(slip_or_trip);
 STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *));
 STATIC_DCL void FDECL(lantern_message, (struct obj *));
+STATIC_DCL void FDECL(cleanup_burn, (genericptr_t,long));
 
 #ifdef OVLB
 
@@ -29,11 +30,11 @@
 {
 	register long i = (Stoned & TIMEOUT);
 
-	if(i > 0 && i <= SIZE(stoned_texts))
+	if (i > 0L && i <= SIZE(stoned_texts))
 		pline(stoned_texts[SIZE(stoned_texts) - i]);
-	if(i == 5)
+	if (i == 5L)
 		HFast = 0L;
-	if(i == 3)
+	if (i == 3L)
 		nomul(-3);
 	exercise(A_DEX, FALSE);
 }
@@ -120,21 +121,24 @@
 {
 	register long i = (Slimed & TIMEOUT) / 2L;
 
-	if (((Slimed & TIMEOUT) % 2L) && i >= 0
+	if (((Slimed & TIMEOUT) % 2L) && i >= 0L
 		&& i < SIZE(slime_texts)) {
-	    const char *str = slime_texts[SIZE(slime_texts)-i-1];
+	    const char *str = slime_texts[SIZE(slime_texts) - i - 1L];
 
 	    if (index(str, '%')) {
-		if (i == 4) {
-		    if (!Blind)
+		if (i == 4L) {	/* "you are turning green" */
+		    if (!Blind)	/* [what if you're already green?] */
 			pline(str, hcolor(green));
 		} else
 		    pline(str, an(Hallucination ? rndmonnam() : "green slime"));
 	    } else
 		pline(str);
 	}
-	if(i == 4)
-	    HFast = 0;
+	if (i == 3L) {	/* limbs becoming oozy */
+	    HFast = 0L;	/* lose intrinsic speed */
+	    stop_occupation();
+	    if (multi > 0) nomul(0);
+	}
 	exercise(A_DEX, FALSE);
 }
 
@@ -144,6 +148,7 @@
 	if (Slimed) {
 	    pline_The("slime that covers you is burned away!");
 	    Slimed = 0L;
+	    flags.botl = 1;
 	}
 	return;
 }
@@ -489,7 +494,8 @@
 	    if (cansee_hatchspot) {
 		Sprintf(monnambuf, "%s%s",
 			siblings ? "some " : "",
-			siblings ? makeplural(m_monnam(mon)) : a_monnam(mon));
+			siblings ?
+			makeplural(m_monnam(mon)) : an(m_monnam(mon)));
 		/* we don't learn the egg type here because learning
 		   an egg type requires either seeing the egg hatch
 		   or being familiar with the egg already,
@@ -610,8 +616,12 @@
 	struct obj *otmp = vobj_at(u.ux, u.uy);
 	const char *what, *pronoun;
 	char buf[BUFSZ];
+	boolean on_foot = TRUE;
+#ifdef STEED
+	if (u.usteed) on_foot = FALSE;
+#endif
 
-	if (otmp) {		/* trip over something in particular */
+	if (otmp && on_foot) {		/* trip over something in particular */
 	    /*
 		If there is only one item, it will have just been named
 		during the move, so refer to by via pronoun; otherwise,
@@ -634,26 +644,52 @@
 		You("trip over %s.", what);
 	    }
 	} else if (rn2(3) && is_ice(u.ux, u.uy)) {
-	    You("%s on the ice.", rn2(2) ? "slip" : "slide");
-	} else switch (rn2(4)) {
-	    case 1:
-		You("trip over your own %s.", Hallucination ?
-			"elbow" : makeplural(body_part(FOOT)));
-		break;
-	    case 2:
-		You("slip %s.", Hallucination ?
-			"on a banana peel" : "and nearly fall");
-		break;
-	    case 3:
-		You("flounder.");
-		break;
-	    default:
-		You("stumble.");
-		break;
-	}
+	    pline("%s %s%s on the ice.",
 #ifdef STEED
-	if (u.usteed) dismount_steed(DISMOUNT_FELL);
+		u.usteed ? upstart(x_monnam(u.usteed,
+				u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				(char *)0, SUPPRESS_SADDLE, FALSE)) :
 #endif
+		"You", rn2(2) ? "slip" : "slide", on_foot ? "" : "s");
+	} else {
+	    if (on_foot) {
+		switch (rn2(4)) {
+		  case 1:
+			You("trip over your own %s.", Hallucination ?
+				"elbow" : makeplural(body_part(FOOT)));
+			break;
+		  case 2:
+			You("slip %s.", Hallucination ?
+				"on a banana peel" : "and nearly fall");
+			break;
+		  case 3:
+			You("flounder.");
+			break;
+		  default:
+			You("stumble.");
+			break;
+		}
+	    }
+#ifdef STEED
+	    else {
+		switch (rn2(4)) {
+		  case 1:
+			Your("%s slip out of the stirrups.", makeplural(body_part(FOOT)));
+			break;
+		  case 2:
+			You("let go of the reins.");
+			break;
+		  case 3:
+			You("bang into the saddle-horn.");
+			break;
+		  default:
+			You("slide to one side of the saddle.");
+			break;
+		}
+		dismount_steed(DISMOUNT_FELL);
+	    }
+#endif
+	}
 }
 
 /* Print a lamp flicker message with tailer. */
@@ -1007,7 +1043,8 @@
 	long turns = 0;
 	boolean do_timer = TRUE;
 
-	if (obj->age == 0 && obj->otyp != MAGIC_LAMP) return;
+	if (obj->age == 0 && obj->otyp != MAGIC_LAMP && !artifact_light(obj))
+	    return;
 
 	switch (obj->otyp) {
 	    case MAGIC_LAMP:
@@ -1049,8 +1086,15 @@
 		break;
 
 	    default:
-		impossible("begin burn: unexpected %s", xname(obj));
-		turns = obj->age;
+                /* [ALI] Support artifact light sources */
+                if (artifact_light(obj)) {
+		    obj->lamplit = 1;
+		    do_timer = FALSE;
+		    radius = 2;
+		} else {
+		    impossible("begin burn: unexpected %s", xname(obj));
+		    turns = obj->age;
+		}
 		break;
 	}
 
@@ -1059,11 +1103,14 @@
 					BURN_OBJECT, (genericptr_t)obj)) {
 		obj->lamplit = 1;
 		obj->age -= turns;
-		if (obj->where == OBJ_INVENT && !already_lit)
+		if (carried(obj) && !already_lit)
 		    update_inventory();
 	    } else {
 		obj->lamplit = 0;
 	    }
+	} else {
+	    if (carried(obj) && !already_lit)
+		update_inventory();
 	}
 
 	if (obj->lamplit && !already_lit) {
@@ -1085,30 +1132,55 @@
 	struct obj *obj;
 	boolean timer_attached;
 {
-	long expire_time;
-
 	if (!obj->lamplit) {
 	    impossible("end_burn: obj %s not lit", xname(obj));
 	    return;
 	}
 
-	del_light_source(LS_OBJECT, (genericptr_t) obj);
+	if (obj->otyp == MAGIC_LAMP || artifact_light(obj))
+	    timer_attached = FALSE;
 
-	if (obj->otyp == MAGIC_LAMP) timer_attached = FALSE;
-	if (timer_attached) {
-	    expire_time = stop_timer(BURN_OBJECT, (genericptr_t) obj);
-	    if (expire_time)
-		/* restore unused time */
-		obj->age += expire_time - monstermoves;
-	    else
-		impossible("end_burn: obj %s not timed!", xname(obj));
-	}
-	obj->lamplit = 0;
+	if (!timer_attached) {
+	    /* [DS] Cleanup explicitly, since timer cleanup won't happen */
+	    del_light_source(LS_OBJECT, (genericptr_t)obj);
+	    obj->lamplit = 0;
+	    if (obj->where == OBJ_INVENT)
+		update_inventory();
+	} else if (!stop_timer(BURN_OBJECT, (genericptr_t) obj))
+	    impossible("end_burn: obj %s not timed!", xname(obj));
+}
 
-	if (obj->where == OBJ_INVENT)
-	    update_inventory();
+#endif /* OVL1 */
+#ifdef OVL0
+
+/*
+ * Cleanup a burning object if timer stopped.
+ */
+static void
+cleanup_burn(arg, expire_time)
+    genericptr_t arg;
+    long expire_time;
+{
+    struct obj *obj = (struct obj *)arg;
+    if (!obj->lamplit) {
+	impossible("cleanup_burn: obj %s not lit", xname(obj));
+	return;
+    }
+
+    del_light_source(LS_OBJECT, arg);
+
+    /* restore unused time */
+    obj->age += expire_time - monstermoves;
+
+    obj->lamplit = 0;
+
+    if (obj->where == OBJ_INVENT)
+	update_inventory();
 }
 
+#endif /* OVL0 */
+#ifdef OVL1
+
 void
 do_storms()
 {
@@ -1205,17 +1277,6 @@
  *		Stop all timers attached to obj.
  */
 
-
-typedef struct fe {
-    struct fe *next;		/* next item in chain */
-    long timeout;		/* when we time out */
-    unsigned long tid;		/* timer ID */
-    short kind;			/* kind of use */
-    short func_index;		/* what to call when we time out */
-    genericptr_t arg;		/* pointer to timeout argument */
-    Bitfield (needs_fixup,1);	/* does arg need to be patched? */
-} timer_element;
-
 #ifdef WIZARD
 STATIC_DCL const char *FDECL(kind_name, (SHORT_P));
 STATIC_DCL void FDECL(print_queue, (winid, timer_element *));
@@ -1223,6 +1284,7 @@
 STATIC_DCL void FDECL(insert_timer, (timer_element *));
 STATIC_DCL timer_element *FDECL(remove_timer, (timer_element **, SHORT_P,
 								genericptr_t));
+STATIC_DCL void FDECL(write_timer, (int, timer_element *));
 STATIC_DCL boolean FDECL(mon_is_local, (struct monst *));
 STATIC_DCL boolean FDECL(timer_is_local, (timer_element *));
 STATIC_DCL int FDECL(maybe_write_timer, (int, int, BOOLEAN_P));
@@ -1235,23 +1297,23 @@
 #define VERBOSE_TIMER
 
 typedef struct {
-    timeout_proc f;
+    timeout_proc f, cleanup;
 #ifdef VERBOSE_TIMER
     const char *name;
-# define TTAB(a, b) {a,b}
+# define TTAB(a, b, c) {a,b,c}
 #else
-# define TTAB(a, b) {a}
+# define TTAB(a, b, c) {a,b}
 #endif
 } ttable;
 
 /* table of timeout functions */
 static ttable timeout_funcs[NUM_TIME_FUNCS] = {
-    TTAB(rot_organic,	"rot_organic"),
-    TTAB(rot_corpse,	"rot_corpse"),
-    TTAB(revive_mon,	"revive_mon"),
-    TTAB(burn_object,	"burn_object"),
-    TTAB(hatch_egg,	"hatch_egg"),
-    TTAB(fig_transform,	"fig_transform")
+    TTAB(rot_organic,	(timeout_proc)0,	"rot_organic"),
+    TTAB(rot_corpse,	(timeout_proc)0,	"rot_corpse"),
+    TTAB(revive_mon,	(timeout_proc)0,	"revive_mon"),
+    TTAB(burn_object,	cleanup_burn,		"burn_object"),
+    TTAB(hatch_egg,	(timeout_proc)0,	"hatch_egg"),
+    TTAB(fig_transform,	(timeout_proc)0,	"fig_transform")
 };
 #undef TTAB
 
@@ -1418,6 +1480,8 @@
 	timeout = doomed->timeout;
 	if (doomed->kind == TIMER_OBJECT)
 	    ((struct obj *)arg)->timed--;
+	if (timeout_funcs[doomed->func_index].cleanup)
+	    (*timeout_funcs[doomed->func_index].cleanup)(arg, timeout);
 	free((genericptr_t) doomed);
 	return timeout;
     }
@@ -1483,6 +1547,9 @@
 		prev->next = curr->next;
 	    else
 		timer_base = curr->next;
+	    if (timeout_funcs[curr->func_index].cleanup)
+		(*timeout_funcs[curr->func_index].cleanup)(curr->arg,
+			curr->timeout);
 	    free((genericptr_t) curr);
 	} else {
 	    prev = curr;
diff -Naurd ../nethack-3.3.1/src/topten.c ./src/topten.c
--- ../nethack-3.3.1/src/topten.c Tue May 23 22:44:55 2000
+++ ./src/topten.c Fri Mar 22 14:40:55 2002
@@ -31,7 +31,7 @@
 #define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry))
 #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
 #define NAMSZ	10
-#define DTHSZ	60
+#define DTHSZ	100
 #define ROLESZ   3
 #define PERSMAX	 3		/* entries per name/uid per char. allowed */
 #define POINTSMIN	1	/* must be > 0 */
@@ -79,7 +79,7 @@
 NEARDATA const char *killed_by_prefix[] = {
 	"killed by ", "choked on ", "poisoned by ", "", "drowned in ",
 	"", "dissolved in ", "crushed to death by ", "petrified by ",
-	"turned to slime by ", "", "", "", "", "", ""
+	"turned to slime by ", "killed by ", "", "", "", "", ""
 };
 
 static winid toptenwin = WIN_ERR;
@@ -264,6 +264,13 @@
 	return;
 #endif
 
+/* If we are in the midst of a panic, cut out topten entirely.
+ * topten uses alloc() several times, which will lead to
+ * problems if the panic was the result of an alloc() failure.
+ */
+	if (program_state.panicking)
+		return;
+
 	if (flags.toptenwin) {
 	    toptenwin = create_nhwindow(NHW_TEXT);
 	}
@@ -707,7 +714,16 @@
 #endif
 
 	for (i = 0; i < playerct; i++) {
-		if (strcmp(players[i], "all") == 0 ||
+	    if (players[i][0] == '-' && index("pr", players[i][1]) &&
+                players[i][2] == 0 && i + 1 < playerct) {
+		char *arg = (char *)players[i + 1];
+		if ((players[i][1] == 'p' &&
+		     str2role(arg) == str2role(t1->plrole)) ||
+		    (players[i][1] == 'r' &&
+		     str2race(arg) == str2race(t1->plrace)))
+		    return 1;
+		i++;
+	    } else if (strcmp(players[i], "all") == 0 ||
 		    strncmp(t1->name, players[i], NAMSZ) == 0 ||
 		    (players[i][0] == '-' &&
 		     players[i][1] == t1->plrole[0] &&
@@ -841,18 +857,28 @@
 		if (playerct > 1) Strcat(pbuf, "any of ");
 		for (i = 0; i < playerct; i++) {
 		    Strcat(pbuf, players[i]);
-		    if (i < playerct-1) Strcat(pbuf, ":");
+		    if (i < playerct-1) {
+			if (players[i][0] == '-' &&
+			    index("pr", players[i][1]) && players[i][2] == 0)
+			    Strcat(pbuf, " ");
+			else Strcat(pbuf, ":");
+		    }
 		}
 	    }
 	    raw_print(pbuf);
-	    raw_printf("Call is: %s -s [-v] [-role] [maxrank] [playernames]",
+	    raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
+
 			 hname);
+	    raw_printf("Player types are: [-p role] [-r race]");
 	}
 	free_ttlist(tt_head);
 #ifdef	AMIGA
-	display_nhwindow(amii_rawprwin, 1);
-	destroy_nhwindow(amii_rawprwin);
-	amii_rawprwin = WIN_ERR;
+	{
+	    extern winid amii_rawprwin;
+	    display_nhwindow(amii_rawprwin, 1);
+	    destroy_nhwindow(amii_rawprwin);
+	    amii_rawprwin = WIN_ERR;
+	}
 #endif
 }
 
diff -Naurd ../nethack-3.3.1/src/trap.c ./src/trap.c
--- ../nethack-3.3.1/src/trap.c Sat Aug 5 00:43:52 2000
+++ ./src/trap.c Fri Mar 22 14:41:08 2002
@@ -1,9 +1,11 @@
-/*	SCCS Id: @(#)trap.c	3.3	2000/07/07	*/
+/*	SCCS Id: @(#)trap.c	3.4	2001/09/06	*/
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
 
+extern const char *destroy_strings[];
+
 STATIC_DCL void FDECL(dofiretrap, (struct obj *));
 STATIC_DCL void NDECL(domagictrap);
 STATIC_DCL boolean FDECL(emergency_disrobe,(boolean *));
@@ -12,7 +14,7 @@
 STATIC_DCL void FDECL(move_into_trap, (struct trap *));
 STATIC_DCL int FDECL(try_disarm, (struct trap *,BOOLEAN_P));
 STATIC_DCL void FDECL(reward_untrap, (struct trap *, struct monst *));
-STATIC_DCL int FDECL(disarm_beartrap, (struct trap *));
+STATIC_DCL int FDECL(disarm_holdingtrap, (struct trap *));
 STATIC_DCL int FDECL(disarm_landmine, (struct trap *));
 STATIC_DCL int FDECL(disarm_squeaky_board, (struct trap *));
 STATIC_DCL int FDECL(disarm_shooting_trap, (struct trap *, int));
@@ -22,7 +24,9 @@
 STATIC_DCL int FDECL(mkroll_launch,
 			(struct trap *,XCHAR_P,XCHAR_P,SHORT_P,long));
 STATIC_DCL boolean FDECL(isclearpath,(coord *, int, SCHAR_P, SCHAR_P));
-STATIC_DCL void FDECL(blow_up_landmine, (struct trap *));
+#ifdef STEED
+STATIC_OVL int FDECL(steedintrap, (struct trap *, struct obj *));
+#endif
 
 #ifndef OVLB
 STATIC_VAR const char *a_your[2];
@@ -67,7 +71,7 @@
 	case 1:
 	    item = (victim == &youmonst) ? uarmc : which_armor(victim, W_ARMC);
 	    if (item) {
-		(void) burn_dmg(item, "cloak");
+		(void) burn_dmg(item, cloak_simple_name(item));
 		return TRUE;
 	    }
 	    item = (victim == &youmonst) ? uarm : which_armor(victim, W_ARM);
@@ -116,7 +120,6 @@
 	static NEARDATA const char *action[] = { "smoulder", "rust", "rot", "corrode" };
 	static NEARDATA const char *msg[] =  { "burnt", "rusted", "rotten", "corroded" };
 	boolean vulnerable = FALSE;
-	boolean plural;
 	boolean grprot = FALSE;
 	boolean is_primary = TRUE;
 	boolean vismon = (victim != &youmonst) && canseemon(victim);
@@ -142,76 +145,73 @@
 	if (!print && (!vulnerable || otmp->oerodeproof || erosion == MAX_ERODE))
 		return FALSE;
 
-	plural = (is_gloves(otmp) || is_boots(otmp)) &&
-		!strstri(ostr, "pair of ");	/* "pair of *s" is singular */
-
 	if (!vulnerable) {
 	    if (flags.verbose) {
 		if (victim == &youmonst)
-		    Your("%s %s not affected.", ostr, plural ? "are" : "is");
+		    Your("%s %s not affected.", ostr, vtense(ostr, "are"));
 		else if (vismon)
 		    pline("%s's %s %s not affected.", Monnam(victim), ostr,
-			plural ? "are" : "is");
+			  vtense(ostr, "are"));
 	    }
 	} else if (erosion < MAX_ERODE) {
 	    if (grprot && otmp->greased) {
-		grease_protect(otmp,ostr,plural,victim);
+		grease_protect(otmp,ostr,victim);
 	    } else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) {
 		if (flags.verbose) {
 		    if (victim == &youmonst)
 			pline("Somehow, your %s %s not affected.",
-				ostr, plural ? "are" : "is");
+			      ostr, vtense(ostr, "are"));
 		    else if (vismon)
 			pline("Somehow, %s's %s %s not affected.",
-				mon_nam(victim), ostr, plural ? "are" : "is");
+			      mon_nam(victim), ostr, vtense(ostr, "are"));
 		}
 	    } else {
 		if (victim == &youmonst)
-		    Your("%s %s%s%s!", ostr, action[type],
-			plural ? "" : "s",
-			erosion+1 == MAX_ERODE ? " completely" :
-			erosion ? " further" : "");
+		    Your("%s %s%s!", ostr,
+			 vtense(ostr, action[type]),
+			 erosion+1 == MAX_ERODE ? " completely" :
+			    erosion ? " further" : "");
 		else if (vismon)
-		    pline("%s's %s %s%s%s!", Monnam(victim), ostr, action[type],
-			plural ? "" : "s",
+		    pline("%s's %s %s%s!", Monnam(victim), ostr,
+			vtense(ostr, action[type]),
 			erosion+1 == MAX_ERODE ? " completely" :
-			erosion ? " further" : "");
+			  erosion ? " further" : "");
 		if (is_primary)
 		    otmp->oeroded++;
 		else
 		    otmp->oeroded2++;
+		update_inventory();
 	    }
 	} else {
 	    if (flags.verbose) {
 		if (victim == &youmonst)
-		    Your("%s %s%s completely %s.", ostr,
-			Blind ? "feel" : "look",
-			plural ? "" : "s", msg[type]);
+		    Your("%s %s completely %s.", ostr,
+			 vtense(ostr, Blind ? "feel" : "look"),
+			 msg[type]);
 		else if (vismon)
-		    pline("%s's %s look%s completely %s.",
-			Monnam(victim), ostr,
-			plural ? "" : "s", msg[type]);
+		    pline("%s's %s %s completely %s.",
+			  Monnam(victim), ostr,
+			  vtense(ostr, "look"), msg[type]);
 	    }
 	}
 	return(TRUE);
 }
 
 void
-grease_protect(otmp,ostr,plu,victim)
+grease_protect(otmp,ostr,victim)
 register struct obj *otmp;
 register const char *ostr;
-register boolean plu;
 struct monst *victim;
 {
 	static const char txt[] = "protected by the layer of grease!";
-	boolean vismon = (victim != &youmonst) && canseemon(victim);
+	boolean vismon = victim && (victim != &youmonst) && canseemon(victim);
 
 	if (ostr) {
 	    if (victim == &youmonst)
-		Your("%s %s %s",ostr,plu ? "are" : "is", txt);
+		Your("%s %s %s", ostr, vtense(ostr, "are"), txt);
 	    else if (vismon)
 		pline("%s's %s %s %s", Monnam(victim),
-		    ostr, plu ? "are" : "is", txt);
+		    ostr, vtense(ostr, "are"), txt);
 	} else {
 	    if (victim == &youmonst)
 		Your("%s %s",aobjnam(otmp,"are"), txt);
@@ -219,9 +219,11 @@
 		pline("%s's %s %s", Monnam(victim), aobjnam(otmp,"are"), txt);
 	}
 	if (!rn2(2)) {
-	    pline_The("grease dissolves.");
 	    otmp->greased = 0;
-	    update_inventory();
+	    if (carried(otmp)) {
+		pline_The("grease dissolves.");
+		update_inventory();
+	    }
 	}
 }
 
@@ -263,8 +265,9 @@
 		    otmp = mtmp->minvent;
 		    otmp->owornmask = 0;
 		    obj_extract_self(otmp);
-		    add_to_container(statue, otmp);
+		    (void) add_to_container(statue, otmp);
 		}
+		statue->owt = weight(statue);
 		mongone(mtmp);
 		break;
 	      }
@@ -490,12 +493,14 @@
 }
 
 void
-dotrap(trap)
+dotrap(trap, trflags)
 register struct trap *trap;
+unsigned trflags;
 {
 	register int ttype = trap->ttyp;
 	register struct obj *otmp;
 	boolean already_seen = trap->tseen;
+	boolean webmsgok = (!(trflags & NOWEBMSG));
 	
 	nomul(0);
 
@@ -540,6 +545,11 @@
 		otmp = mksobj(ARROW, TRUE, FALSE);
 		otmp->quan = 1L;
 		otmp->owt = weight(otmp);
+		otmp->opoisoned = 0;
+#ifdef STEED
+		if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) /* nothing */;
+		else
+#endif
 		if (thitu(8, dmgval(otmp, &youmonst), otmp, "arrow")) {
 		    obfree(otmp, (struct obj *)0);
 		} else {
@@ -556,6 +566,10 @@
 		otmp->quan = 1L;
 		otmp->owt = weight(otmp);
 		if (!rn2(6)) otmp->opoisoned = 1;
+#ifdef STEED
+		if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) /* nothing */;
+		else
+#endif
 		if (thitu(7, dmgval(otmp, &youmonst), otmp, "little dart")) {
 		    if (otmp->opoisoned)
 			poisoned("dart",A_CON,"poison dart",10);
@@ -572,7 +586,7 @@
 		    int dmg = d(2,6); /* should be std ROCK dmg? */
 
 		    seetrap(trap);
-		    otmp = mksobj_at(ROCK, u.ux, u.uy, TRUE);
+		    otmp = mksobj_at(ROCK, u.ux, u.uy, TRUE, FALSE);
 		    otmp->quan = 1L;
 		    otmp->owt = weight(otmp);
 
@@ -623,7 +637,11 @@
 			    A_Your[trap->madeby_u]);
 		    break;
 		}
-		if(youmonst.data->msize <= MZ_SMALL) {
+		if(
+#ifdef STEED
+		   !u.usteed &&
+#endif
+		   youmonst.data->msize <= MZ_SMALL) {
 		    pline("%s bear trap closes harmlessly over you.",
 			    A_Your[trap->madeby_u]);
 		    break;
@@ -634,7 +652,7 @@
 		if (u.usteed) {
 		    pline("%s bear trap closes on %s %s!",
 			A_Your[trap->madeby_u], s_suffix(mon_nam(u.usteed)),
-			body_part(FOOT));
+			mbodypart(u.usteed, FOOT));
 		} else
 #endif
 		{
@@ -648,7 +666,7 @@
 
 	    case SLP_GAS_TRAP:
 		seetrap(trap);
-		if(Sleep_resistance) {
+		if(Sleep_resistance || breathless(youmonst.data)) {
 		    You("are enveloped in a cloud of gas!");
 		    break;
 		}
@@ -656,6 +674,9 @@
 		flags.soundok = 0;
 		fall_asleep(-rnd(25), TRUE);
 		afternmv = Hear_again;
+#ifdef STEED
+		(void) steedintrap(trap, (struct obj *)0);
+#endif
 		break;
 
 	    case RUST_TRAP:
@@ -688,7 +709,7 @@
 			if (rust_dmg(uarms, "shield", 1, TRUE, &youmonst))
 			    break;
 			if (u.twoweap || (uwep && bimanual(uwep)))
-			    erode_weapon(u.twoweap ? uswapwep : uwep, FALSE);
+			    erode_obj(u.twoweap ? uswapwep : uwep, FALSE, TRUE);
 glovecheck:		(void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
 			/* Not "metal gauntlets" since it gets called
 			 * even if it's leather for the message
@@ -697,14 +718,15 @@
 		    case 2:
 			pline("%s your right %s!", A_gush_of_water_hits,
 				    body_part(ARM));
-			erode_weapon(uwep, FALSE);
+			erode_obj(uwep, FALSE, TRUE);
 			goto glovecheck;
 		    default:
 			pline("%s you!", A_gush_of_water_hits);
 			for (otmp=invent; otmp; otmp = otmp->nobj)
 				    (void) snuff_lit(otmp);
 			if (uarmc)
-			    (void) rust_dmg(uarmc, "cloak", 1, TRUE, &youmonst);
+			    (void) rust_dmg(uarmc, cloak_simple_name(uarmc),
+						1, TRUE, &youmonst);
 			else if (uarm)
 			    (void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst);
 #ifdef TOURIST
@@ -712,6 +734,7 @@
 			    (void) rust_dmg(uarmu, "shirt", 1, TRUE, &youmonst);
 #endif
 		}
+		update_inventory();
 		break;
 
 	    case FIRE_TRAP:
@@ -724,7 +747,7 @@
 		/* KMH -- You can't escape the Sokoban level traps */
 		if (!In_sokoban(&u.uz) && (Levitation || Flying)) break;
 		seetrap(trap);
-		if (is_clinger(youmonst.data)) {
+		if (!In_sokoban(&u.uz) && is_clinger(youmonst.data)) {
 		    if(trap->tseen) {
 			You("see %s %spit below you.", a_your[trap->madeby_u],
 			    ttype == SPIKED_PIT ? "spiked " : "");
@@ -736,8 +759,19 @@
 		    }
 		    break;
 		}
-		if (!In_sokoban(&u.uz))
-		    You("fall into %s pit!", a_your[trap->madeby_u]);
+		if (!In_sokoban(&u.uz)) {
+		   char verbbuf[BUFSZ];
+#ifdef STEED
+		   if (u.usteed)
+		   	Sprintf(verbbuf,"lead %s",
+				x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 "poor", SUPPRESS_SADDLE, FALSE));
+		   else
+#endif
+		    Strcpy(verbbuf,"fall");
+		   You("%s into %s pit!", verbbuf, a_your[trap->madeby_u]);
+		}
 		/* wumpus reference */
 		if (Role_if(PM_RANGER) && !trap->madeby_u && !trap->once &&
 			In_quest(&u.uz) && Is_qlocate(&u.uz)) {
@@ -746,11 +780,24 @@
 		} else if (u.umonnum == PM_PIT_VIPER ||
 			u.umonnum == PM_PIT_FIEND)
 		    pline("How pitiful.  Isn't that the pits?");
-		if (ttype == SPIKED_PIT)
-		    You("land on a set of sharp iron spikes!");
+		if (ttype == SPIKED_PIT) {
+		    char *predicament = "on a set of sharp iron spikes";
+#ifdef STEED
+		    if (u.usteed) {
+			pline("%s lands %s!",
+				upstart(x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 "poor", SUPPRESS_SADDLE, FALSE)), predicament);
+		    } else
+#endif
+		    You("land %s!", predicament);
+		}
 		if (!Passes_walls)
 		    u.utrap = rn1(6,2);
 		u.utraptype = TT_PIT;
+#ifdef STEED
+		if (!steedintrap(trap, (struct obj *)0)) {
+#endif
 		if (ttype == SPIKED_PIT) {
 		    losehp(rnd(10),"fell into a pit of iron spikes",
 			NO_KILLER_PREFIX);
@@ -767,6 +814,9 @@
 		vision_full_recalc = 1;	/* vision limits change */
 		exercise(A_STR, FALSE);
 		exercise(A_DEX, FALSE);
+#ifdef STEED
+		}
+#endif
 		break;
 	    case HOLE:
 	    case TRAPDOOR:
@@ -794,26 +844,40 @@
 						    unsolid(youmonst.data)) {
 		    if (acidic(youmonst.data) || u.umonnum == PM_GELATINOUS_CUBE ||
 			u.umonnum == PM_FIRE_ELEMENTAL) {
-			You("%s %s spider web!",
-			    (u.umonnum == PM_FIRE_ELEMENTAL) ? "burn" : "dissolve",
-			    a_your[trap->madeby_u]);
+			if (webmsgok)
+			    You("%s %s spider web!",
+				(u.umonnum == PM_FIRE_ELEMENTAL) ? "burn" : "dissolve",
+				a_your[trap->madeby_u]);
 			deltrap(trap);
 			newsym(u.ux,u.uy);
 			break;
 		    }
-		    You("flow through %s spider web.",
+		    if (webmsgok) You("flow through %s spider web.",
 			    a_your[trap->madeby_u]);
 		    break;
 		}
 		if (webmaker(youmonst.data)) {
-		    pline(trap->madeby_u ? "You take a walk on your web."
+		    if (webmsgok)
+		    	pline(trap->madeby_u ? "You take a walk on your web."
 					 : "There is a spider web here.");
 		    break;
 		}
-		You("%s into %s spider web!",
-		      Levitation ? (const char *)"float" :
-		      locomotion(youmonst.data, "stumble"),
-		      a_your[trap->madeby_u]);
+		if (webmsgok) {
+		   char verbbuf[BUFSZ];
+		   verbbuf[0] = '\0';
+#ifdef STEED
+		   if (u.usteed)
+		   	Sprintf(verbbuf,"lead %s",
+				x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 "poor", SUPPRESS_SADDLE, FALSE));
+		   else
+#endif
+			
+		    Sprintf(verbbuf, "%s", Levitation ? (const char *)"float" :
+		      		locomotion(youmonst.data, "stumble"));
+		   You("%s into %s spider web!", verbbuf, a_your[trap->madeby_u]);
+		}
 		u.utraptype = TT_WEB;
 
 		/* Time stuck in the web depends on your strength. */
@@ -829,7 +893,8 @@
 		    else if (str < 69) u.utrap = 1;
 		    else {
 			u.utrap = 0;
-			You("tear through %s web!", a_your[trap->madeby_u]);
+			if (webmsgok)
+				You("tear through %s web!", a_your[trap->madeby_u]);
 			deltrap(trap);
 			newsym(u.ux,u.uy);	/* get rid of trap symbol */
 		    }
@@ -850,6 +915,9 @@
 		    Your("body absorbs some of the magical energy!");
 		    u.uen = (u.uenmax += 2);
 		} else domagictrap();
+#ifdef STEED
+		(void) steedintrap(trap, (struct obj *)0);
+#endif
 		break;
 
 	    case ANTI_MAGIC:
@@ -860,23 +928,36 @@
 		} else drain_en(rnd(u.ulevel) + 1);
 		break;
 
-	    case POLY_TRAP:
+	    case POLY_TRAP: {
+	        char verbbuf[BUFSZ];
 		seetrap(trap);
-		You("%s onto a polymorph trap!",
+#ifdef STEED
+		if (u.usteed)
+			Sprintf(verbbuf, "lead %s",
+				x_monnam(u.usteed,
+					 u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE));
+		else
+#endif
+		 Sprintf(verbbuf,"%s",
 		    Levitation ? (const char *)"float" :
 		    locomotion(youmonst.data, "step"));
+		You("%s onto a polymorph trap!", verbbuf);
 		if(Antimagic || Unchanging) {
 		    shieldeff(u.ux, u.uy);
 		    You_feel("momentarily different.");
 		    /* Trap did nothing; don't remove it --KAA */
 		} else {
+#ifdef STEED
+		    (void) steedintrap(trap, (struct obj *)0);
+#endif
 		    deltrap(trap);	/* delete trap before polymorph */
 		    newsym(u.ux,u.uy);	/* get rid of trap symbol */
 		    You_feel("a change coming over you.");
-		    polyself();
+		    polyself(FALSE);
 		}
 		break;
-
+	    }
 	    case LANDMINE:
 		if (Levitation || Flying) {
 		    if (!already_seen && rn2(3)) break;
@@ -890,9 +971,23 @@
 			    already_seen ? a_your[trap->madeby_u] : "",
 			    already_seen ? " land mine" : "it");
 		} else {
+#ifdef STEED
+		    /* prevent landmine from killing steed, throwing you to
+		     * the ground, and you being affected again by the same
+		     * mine because it hasn't been deleted yet
+		     */
+		    static boolean recursive_mine = FALSE;
+
+		    if (recursive_mine) break;
+#endif
 		    seetrap(trap);
 		    pline("KAABLAMM!!!  You triggered %s land mine!",
 					    a_your[trap->madeby_u]);
+#ifdef STEED
+		    recursive_mine = TRUE;
+		    (void) steedintrap(trap, (struct obj *)0);
+		    recursive_mine = FALSE;
+#endif
 		    set_wounded_legs(LEFT_SIDE, rn1(35, 41));
 		    set_wounded_legs(RIGHT_SIDE, rn1(35, 41));
 		    exercise(A_DEX, FALSE);
@@ -901,7 +996,8 @@
 		newsym(u.ux,u.uy);		/* update trap symbol */
 		losehp(rnd(16), "land mine", KILLED_BY_AN);
 		/* fall recursively into the pit... */
-		if ((trap = t_at(u.ux, u.uy)) != 0) dotrap(trap);
+		if ((trap = t_at(u.ux, u.uy)) != 0) dotrap(trap, 0);
+		fill_pit(u.ux, u.uy);
 		break;
 
 	    case ROLLING_BOULDER_TRAP:
@@ -926,8 +1022,99 @@
 	}
 }
 
+#ifdef STEED
+STATIC_OVL int
+steedintrap(trap, otmp)
+struct trap *trap;
+struct obj *otmp;
+{
+	struct monst *mtmp = u.usteed;
+	struct permonst *mptr;
+	int tt;
+	boolean in_sight;
+	boolean trapkilled = FALSE;
+	boolean steedhit = FALSE;
+
+	if (!u.usteed || !trap) return 0;
+	mptr = mtmp->data;
+	tt = trap->ttyp;
+	mtmp->mx = u.ux;
+	mtmp->my = u.uy;
+
+	in_sight = !Blind;
+	switch (tt) {
+		case ARROW_TRAP:
+			if(!otmp) {
+				impossible("steed hit by non-existant arrow?");
+				return 0;
+			}
+			if(thitm(8, mtmp, otmp, 0)) trapkilled = TRUE;
+			steedhit = TRUE;
+			break;
+		case DART_TRAP:
+			if(!otmp) {
+				impossible("steed hit by non-existant dart?");
+				return 0;
+			}
+			if(thitm(7, mtmp, otmp, 0)) trapkilled = TRUE;
+			steedhit = TRUE;
+			break;
+		case SLP_GAS_TRAP:
+		    if (!resists_sleep(mtmp) && !breathless(mptr) &&
+				!mtmp->msleeping && mtmp->mcanmove) {
+			    mtmp->mcanmove = 0;
+			    mtmp->mfrozen = rnd(25);
+			    if (in_sight) {
+				pline("%s suddenly falls asleep!",
+				      Monnam(mtmp));
+			    }
+			}
+			steedhit = TRUE;
+			break;
+		case LANDMINE:
+			if (thitm(0, mtmp, (struct obj *)0, rnd(16)))
+			    trapkilled = TRUE;
+			steedhit = TRUE;
+			break;
+		case PIT:
+		case SPIKED_PIT:
+			if (mtmp->mhp <= 0 ||
+				thitm(0, mtmp, (struct obj *)0,
+				      rnd((tt == PIT) ? 6 : 10)))
+			    trapkilled = TRUE;
+			steedhit = TRUE;
+			break;
+		case POLY_TRAP: 
+		    if (!resists_magm(mtmp)) {
+			if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) {
+			(void) newcham(mtmp, (struct permonst *)0, FALSE);
+			if (!can_saddle(mtmp) || !can_ride(mtmp)) {
+				dismount_steed(DISMOUNT_POLY);
+			} else {
+				You("have to adjust yourself in the saddle on %s.",
+					x_monnam(mtmp,
+					 mtmp->mnamelth ? ARTICLE_NONE : ARTICLE_A,
+				 	 (char *)0, SUPPRESS_SADDLE, FALSE));
+			}
+				
+		    }
+		    steedhit = TRUE;
+		    break;
+		default:
+			return 0;
+	    }
+	}
+	if(trapkilled) {
+		dismount_steed(DISMOUNT_POLY);
+		return 2;
+	}
+	else if(steedhit) return 1;
+	else return 0;
+}
+#endif /*STEED*/
+
 /* some actions common to both player and monsters for triggered landmine */
-STATIC_OVL void
+void
 blow_up_landmine(trap)
 struct trap *trap;
 {
@@ -938,8 +1125,8 @@
 	wake_nearto(trap->tx, trap->ty, 400);
 	if (IS_DOOR(levl[trap->tx][trap->ty].typ))
 	    levl[trap->tx][trap->ty].doormask = D_BROKEN;
-	/* TODO: destroy drawbridge if present;
-		 sometimes delete trap instead of always leaving a pit */
+	/* TODO: destroy drawbridge if present */
+	/* caller may subsequently fill pit, e.g. with a boulder */
 	trap->ttyp = PIT;		/* explosion creates a pit */
 	trap->madeby_u = FALSE;		/* resulting pit isn't yours */
 }
@@ -961,7 +1148,7 @@
 int style;
 {
 	register struct monst *mtmp;
-	register struct obj *otmp;
+	register struct obj *otmp, *otmp2;
 	register int dx,dy;
 	struct obj *singleobj;
 	boolean used_up = FALSE;
@@ -990,7 +1177,7 @@
 	    singleobj = otmp;
 	    otmp = (struct obj *) 0;
 	} else {
-	    singleobj = splitobj(otmp, otmp->quan - 1L);
+	    singleobj = splitobj(otmp, 1L);
 	    obj_extract_self(singleobj);
 	}
 	newsym(x1,y1);
@@ -1019,6 +1206,7 @@
 
 	/* Set the object in motion */
 	while(dist-- > 0 && !used_up) {
+		struct trap *t;
 		tmp_at(bhitpos.x, bhitpos.y);
 		tmp = delaycnt;
 
@@ -1028,7 +1216,8 @@
 
 		bhitpos.x += dx;
 		bhitpos.y += dy;
-
+		t = t_at(bhitpos.x, bhitpos.y);
+		
 		if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
 			if (otyp == BOULDER && throws_rocks(mtmp->data)) {
 			    if (rn2(3)) {
@@ -1058,15 +1247,74 @@
 				break;
 			}
 		    }
+		    if (t && otyp == BOULDER) {
+			switch(t->ttyp) {
+			case LANDMINE:
+			    if (rn2(10) > 2) {
+			  	pline(
+				  "KAABLAMM!!!%s",
+				  cansee(bhitpos.x, bhitpos.y) ?
+					" The rolling boulder triggers a land mine." : "");
+				deltrap(t);
+				del_engr_at(bhitpos.x,bhitpos.y);
+				place_object(singleobj, bhitpos.x, bhitpos.y);
+				fracture_rock(singleobj);
+				scatter(bhitpos.x,bhitpos.y, 4,
+					MAY_DESTROY|MAY_HIT|MAY_FRACTURE|VIS_EFFECTS,
+					(struct obj *)0);
+				if (cansee(bhitpos.x,bhitpos.y))
+					newsym(bhitpos.x,bhitpos.y);
+			        used_up = TRUE;
+			    }
+			    break;		
+			case LEVEL_TELEP:
+			case TELEP_TRAP:
+			    if (cansee(bhitpos.x, bhitpos.y))
+			    	pline("Suddenly the rolling boulder disappears!");
+			    else
+			    	You_hear("a rumbling stop abruptly.");
+			    if (t->ttyp == TELEP_TRAP)
+				rloco(singleobj);
+			    else {
+				int newlev = random_teleport_level();
+				d_level dest;
+
+				if (newlev == depth(&u.uz) || In_endgame(&u.uz))
+				    continue;
+				add_to_migration(singleobj);
+				get_level(&dest, newlev);
+				singleobj->ox = dest.dnum;
+				singleobj->oy = dest.dlevel;
+				singleobj->owornmask = (long)MIGR_RANDOM;
+			    }
+		    	    seetrap(t);
+			    used_up = TRUE;
+			    break;
+			}
+			if (used_up) break;
+		    }
 		    if (flooreffects(singleobj, bhitpos.x, bhitpos.y, "fall")) {
 			used_up = TRUE;
 			break;
 		    }
+		    if (otyp == BOULDER &&
+		       (otmp2 = sobj_at(BOULDER, bhitpos.x, bhitpos.y)) != 0) {
+			char *bmsg =
+				" as one boulder sets another in motion";
+		    	You_hear("a loud crash%s!",
+				cansee(bhitpos.x, bhitpos.y) ? bmsg : "");
+			obj_extract_self(otmp2);
+			place_object(singleobj, bhitpos.x, bhitpos.y);
+			singleobj = otmp2;
+			otmp2 = (struct obj *)0;
+			wake_nearto(bhitpos.x, bhitpos.y, 10*10);
+		    }
 		}
 		if (otyp == BOULDER && closed_door(bhitpos.x,bhitpos.y)) {
 			if (cansee(bhitpos.x, bhitpos.y))
 				pline_The("boulder crashes through a door.");
 			levl[bhitpos.x][bhitpos.y].doormask = D_BROKEN;
+			if (dist) unblock_point(bhitpos.x, bhitpos.y);
 		}
 	}
 	tmp_at(DISP_END, 0);
@@ -1192,6 +1440,17 @@
 	if (!trap) {
 	    mtmp->mtrapped = 0;	/* perhaps teleported? */
 	} else if (mtmp->mtrapped) {	/* is currently in the trap */
+	    if (!trap->tseen &&
+		cansee(mtmp->mx, mtmp->my) && canseemon(mtmp) &&
+		(trap->ttyp == SPIKED_PIT || trap->ttyp == BEAR_TRAP ||
+		 trap->ttyp == HOLE || trap->ttyp == PIT ||
+		 trap->ttyp == WEB)) {
+		/* If you come upon an obviously trapped monster, then
+		 * you must be able to see the trap it's in too.
+		 */
+		seetrap(trap);
+	    }
+		
 	    if (!rn2(40)) {
 		if (sobj_at(BOULDER, mtmp->mx, mtmp->my) &&
 			(trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)) {
@@ -1246,6 +1505,7 @@
 			otmp = mksobj(ARROW, TRUE, FALSE);
 			otmp->quan = 1L;
 			otmp->owt = weight(otmp);
+			otmp->opoisoned = 0;
 			if (in_sight) seetrap(trap);
 			if(thitm(8, mtmp, otmp, 0)) trapkilled = TRUE;
 			break;
@@ -1297,7 +1557,7 @@
 			break;
 
 		case SLP_GAS_TRAP:
-			if (!resists_sleep(mtmp) &&
+		    if (!resists_sleep(mtmp) && !breathless(mptr) &&
 				!mtmp->msleeping && mtmp->mcanmove) {
 			    mtmp->mcanmove = 0;
 			    mtmp->mfrozen = rnd(25);
@@ -1332,7 +1592,7 @@
 				break;
 			    target = MON_WEP(mtmp);
 			    if (target && bimanual(target))
-				erode_weapon(target, FALSE);
+				erode_obj(target, FALSE, TRUE);
 glovecheck:		    target = which_armor(mtmp, W_ARMG);
 			    (void) rust_dmg(target, "gauntlets", 1, TRUE, mtmp);
 			    break;
@@ -1340,7 +1600,7 @@
 			    if (in_sight)
 				pline("%s %s's right %s!", A_gush_of_water_hits,
 				    mon_nam(mtmp), mbodypart(mtmp, ARM));
-			    erode_weapon(MON_WEP(mtmp), FALSE);
+			    erode_obj(MON_WEP(mtmp), FALSE, TRUE);
 			    goto glovecheck;
 			default:
 			    if (in_sight)
@@ -1350,7 +1610,8 @@
 				(void) snuff_lit(otmp);
 			    target = which_armor(mtmp, W_ARMC);
 			    if (target)
-				(void) rust_dmg(target, "cloak", 1, TRUE, mtmp);
+				(void) rust_dmg(target, cloak_simple_name(target),
+						 1, TRUE, mtmp);
 			    else {
 				target = which_armor(mtmp, W_ARM);
 				if (target)
@@ -1408,7 +1669,7 @@
 			    (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
 			    (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
 			}
-			if (burn_floor_paper(mtmp->mx, mtmp->my, see_it) &&
+			if (burn_floor_paper(mtmp->mx, mtmp->my, see_it, FALSE) &&
 				!see_it && distu(mtmp->mx, mtmp->my) <= 3*3)
 			    You("smell smoke.");
 			if (is_ice(mtmp->mx,mtmp->my))
@@ -1431,10 +1692,10 @@
 			    pline("%s %s into %s pit!",
 				  Monnam(mtmp), fallverb,
 				  a_your[trap->madeby_u]);
+			    if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND])
+				pline("How pitiful.  Isn't that the pits?");
 			    seetrap(trap);
 			}
-			if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND])
-			    pline("How pitiful.  Isn't that the pits?");
 			mselftouch(mtmp, "Falling, ", FALSE);
 			if (mtmp->mhp <= 0 ||
 				thitm(0, mtmp, (struct obj *)0,
@@ -1536,6 +1797,7 @@
 			    case PM_IRON_GOLEM:
 			    case PM_BALROG:
 			    case PM_KRAKEN:
+			    case PM_MASTODON:
 				tear_web = TRUE;
 				break;
 			}
@@ -1587,6 +1849,9 @@
 				/* monsters recursively fall into new pit */
 				if (mintrap(mtmp) == 2) trapkilled=TRUE;
 			}
+			/* a boulder may fill the new pit, crushing monster */
+			fill_pit(trap->tx, trap->ty);
+			if (mtmp->mhp <= 0) trapkilled = TRUE;
 			if (unconscious()) {
 				multi = -1;
 				nomovemsg="The explosion awakens you!";
@@ -1597,7 +1862,7 @@
 		    if (resists_magm(mtmp)) {
 			shieldeff(mtmp->mx, mtmp->my);
 		    } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) {
-			(void) newcham(mtmp, (struct permonst *)0);
+			(void) newcham(mtmp, (struct permonst *)0, FALSE);
 			if (in_sight) seetrap(trap);
 		    }
 		    break;
@@ -1815,45 +2080,45 @@
 		}
 	}
 	if (!trap) {
-		trap = t_at(u.ux,u.uy);
-		if(Is_airlevel(&u.uz))
-			You("begin to tumble in place.");
-		else if (Is_waterlevel(&u.uz) && !no_msg)
-			You_feel("heavier.");
-		/* u.uinwater msgs already in spoteffects()/drown() */
-		else if (!u.uinwater && !no_msg) {
+	    trap = t_at(u.ux,u.uy);
+	    if(Is_airlevel(&u.uz))
+		You("begin to tumble in place.");
+	    else if (Is_waterlevel(&u.uz) && !no_msg)
+		You_feel("heavier.");
+	    /* u.uinwater msgs already in spoteffects()/drown() */
+	    else if (!u.uinwater && !no_msg) {
 #ifdef STEED
-		    if (!(emask & W_SADDLE))
+		if (!(emask & W_SADDLE))
 #endif
-		    {
-			boolean sokoban_trap = (In_sokoban(&u.uz) && trap);
-			if (Hallucination)
-				pline("Bummer!  You've %s.",
-				      is_pool(u.ux,u.uy) ?
-					"splashed down" : sokoban_trap ? "crashed" :
-					"hit the ground");
+		{
+		    boolean sokoban_trap = (In_sokoban(&u.uz) && trap);
+		    if (Hallucination)
+			pline("Bummer!  You've %s.",
+			      is_pool(u.ux,u.uy) ?
+			      "splashed down" : sokoban_trap ? "crashed" :
+			      "hit the ground");
+		    else {
+			if (!sokoban_trap)
+			    You("float gently to the %s.",
+				surface(u.ux, u.uy));
 			else {
-				if (!sokoban_trap)
-					You("float gently to the %s.",
-					    surface(u.ux, u.uy));
-				else {
-					/* Justification elsewhere for Sokoban traps
-					 * is based on air currents. This is
-					 * consistent with that.
-					 * The unexpected additional force of the
-					 * air currents once leviation
-					 * ceases knocks you off your feet.
-					 */
-					You("fall over.");
-		    			losehp(rnd(2), "wind swept", KILLED_BY);
+			    /* Justification elsewhere for Sokoban traps
+			     * is based on air currents. This is
+			     * consistent with that.
+			     * The unexpected additional force of the
+			     * air currents once leviation
+			     * ceases knocks you off your feet.
+			     */
+			    You("fall over.");
+			    losehp(rnd(2), "dangerous winds", KILLED_BY);
 #ifdef STEED
-		    			if (u.usteed) dismount_steed(DISMOUNT_FELL);
+			    if (u.usteed) dismount_steed(DISMOUNT_FELL);
 #endif
-					selftouch("As you fall, you");
-				}
+			    selftouch("As you fall, you");
 			}
 		    }
 		}
+	    }
 	}
 
 	/* can't rely on u.uz0 for detecting trap door-induced level change;
@@ -1870,7 +2135,8 @@
 				break;
 			/* fall into next case */
 		default:
-			dotrap(trap);
+			if (!u.utrap) /* not already in the trap */
+				dotrap(trap, 0);
 	}
 
 	if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !u.uswallow &&
@@ -1921,7 +2187,7 @@
 	    destroy_item(SPBOOK_CLASS, AD_FIRE);
 	    destroy_item(POTION_CLASS, AD_FIRE);
 	}
-	if (!box && burn_floor_paper(u.ux, u.uy, see_it) && !see_it)
+	if (!box && burn_floor_paper(u.ux, u.uy, see_it, TRUE) && !see_it)
 	    You("smell paper burning.");
 	if (is_ice(u.ux, u.uy))
 	    melt_ice(u.ux, u.uy);
@@ -1941,6 +2207,7 @@
 	  if (!resists_blnd(&youmonst)) {
 		You("are momentarily blinded by a flash of light!");
 		make_blinded((long)rn1(5,10),FALSE);
+		if (!Blind) Your(vision_clears);
 	  } else if (!Blind) {
 		You("see a flash of light!");
 	  }  else
@@ -2006,22 +2273,120 @@
 
 	     case 20:
 		    /* uncurse stuff */
-		   {  register struct obj *obj;
+		   {	struct obj pseudo;
+			long save_conf = HConfusion;
 
-			/* below plines added by GAN 10/30/86 */
-			You_feel(Hallucination ?
-				"in touch with the Universal Oneness." :
-				"like someone is helping you.");
-			for(obj = invent; obj ; obj = obj->nobj)
-			       if(obj->owornmask || obj->otyp == LOADSTONE)
-					uncurse(obj);
-		       if(Punished) unpunish();
-		       break;
+			pseudo = zeroobj;   /* neither cursed nor blessed */
+			pseudo.otyp = SCR_REMOVE_CURSE;
+			HConfusion = 0L;
+			(void) seffects(&pseudo);
+			HConfusion = save_conf;
+			break;
 		   }
 	     default: break;
 	  }
 }
 
+/*
+ * Scrolls, spellbooks, potions, and flammable items
+ * may get affected by the fire.
+ *
+ * Return number of objects destroyed. --ALI
+ */
+int
+fire_damage(chain, force, here, x, y)
+struct obj *chain;
+boolean force, here;
+xchar x, y;
+{
+    int chance;
+    struct obj *obj, *otmp, *nobj, *ncobj;
+    int retval = 0;
+    int in_sight = !Blind && couldsee(x, y);	/* Don't care if it's lit */
+    int dindx;
+
+    for (obj = chain; obj; obj = nobj) {
+	nobj = here ? obj->nexthere : obj->nobj;
+
+	/* object might light in a controlled manner */
+	if (catch_lit(obj))
+	    continue;
+
+	if (Is_container(obj)) {
+	    switch (obj->otyp) {
+	    case ICE_BOX:
+		continue;		/* Immune */
+		/*NOTREACHED*/
+		break;
+	    case CHEST:
+		chance = 40;
+		break;
+	    case LARGE_BOX:
+		chance = 30;
+		break;
+	    default:
+		chance = 20;
+		break;
+	    }
+	    if (!force && (Luck + 5) > rn2(chance))
+		continue;
+	    /* Container is burnt up - dump contents out */
+	    if (in_sight) pline("%s catches fire and burns.", Yname2(obj));
+	    if (Has_contents(obj)) {
+		if (in_sight) pline("Its contents fall out.");
+		for (otmp = obj->cobj; otmp; otmp = ncobj) {
+		    ncobj = otmp->nobj;
+		    obj_extract_self(otmp);
+		    if (!flooreffects(otmp, x, y, ""))
+			place_object(otmp, x, y);
+		}
+	    }
+	    delobj(obj);
+	    retval++;
+	} else if (!force && (Luck + 5) > rn2(20)) {
+	    /*  chance per item of sustaining damage:
+	     *	max luck (full moon):	 5%
+	     *	max luck (elsewhen):	10%
+	     *	avg luck (Luck==0):	75%
+	     *	awful luck (Luck<-4):  100%
+	     */
+	    continue;
+	} else if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) {
+	    if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
+		continue;
+	    if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
+		if (in_sight) pline("Smoke rises from %s.", the(xname(obj)));
+		continue;
+	    }
+	    dindx = (obj->oclass == SCROLL_CLASS) ? 2 : 3;
+	    if (in_sight)
+		pline("%s %s.", Yname2(obj), (obj->quan > 1) ?
+		      destroy_strings[dindx*3 + 1] : destroy_strings[dindx*3]);
+	    delobj(obj);
+	    retval++;
+	} else if (obj->oclass == POTION_CLASS) {
+	    dindx = 1;
+	    if (in_sight)
+		pline("%s %s.", Yname2(obj), (obj->quan > 1) ?
+		      destroy_strings[dindx*3 + 1] : destroy_strings[dindx*3]);
+	    delobj(obj);
+	    retval++;
+	} else if (is_flammable(obj) && obj->oeroded < MAX_ERODE &&
+		   !(obj->oerodeproof || (obj->blessed && !rnl(4)))) {
+	    if (in_sight) {
+		pline("%s %s%s.", Yname2(obj), otense(obj, "burn"),
+		      obj->oeroded+1 == MAX_ERODE ? " completely" :
+		      obj->oeroded ? " further" : "");
+	    }
+	    obj->oeroded++;
+	}
+    }
+
+    if (retval && !in_sight)
+	You("smell smoke.");
+    return retval;
+}
+
 void
 water_damage(obj, force, here)
 register struct obj *obj;
@@ -2122,7 +2487,6 @@
 			/* else continue */
 		}
 		}
-
 		if (!otmp) {
 			/* Nothing available left to drop; try gold */
 			if (u.ugold) {
@@ -2135,16 +2499,14 @@
 				assigninvlet(obj);           /* might end up as NOINVSYM */
 				obj->nobj = invent;
 				invent = obj;
+				*lostsome = TRUE;
 				dropx(obj);
 				continue;                    /* Try again */
 			}
 			/* We can't even drop gold! */
 			return (FALSE);
 		}
-
-		if (otmp == uarmh) (void) Helmet_off();
-		else if (otmp == uarms) (void) Shield_off();
-		else if (otmp == uwep) setuwep((struct obj *)0);
+		if (otmp->owornmask && otmp != uball) remove_worn_item(otmp);
 		*lostsome = TRUE;
 		dropx(otmp);
 		invc--;
@@ -2306,7 +2668,7 @@
 	    pline("You're too strained to do that.");
 	    return 0;
 	}
-	if (nohands(youmonst.data) || !youmonst.data->mmove) {
+	if ((nohands(youmonst.data) && !webmaker(youmonst.data)) || !youmonst.data->mmove) {
 	    pline("And just how do you expect to do that?");
 	    return 0;
 	} else if (u.ustuck && sticks(youmonst.data)) {
@@ -2330,6 +2692,9 @@
 {
 	int chance = 3;
 
+	/* Only spiders know how to deal with webs reliably */
+	if (ttmp->ttyp == WEB && !webmaker(youmonst.data))
+	 	chance = 30;
 	if (Confusion || Hallucination) chance++;
 	if (Blind) chance++;
 	if (Stunned) chance += 2;
@@ -2339,7 +2704,7 @@
 	if (Role_if(PM_ROGUE)) {
 	    if (rn2(2 * MAXULEV) < u.ulevel) chance--;
 	    if (u.uhave.questart && chance > 1) chance--;
-	} else if (Role_if(PM_RANGER)) chance--;
+	} else if (Role_if(PM_RANGER) && chance > 1) chance--;
 	return rn2(chance);
 }
 
@@ -2353,6 +2718,9 @@
 	struct obj *otmp = mksobj(otyp, TRUE, FALSE);
 	otmp->quan=cnt;
 	otmp->owt = weight(otmp);
+	/* Only dart traps are capable of being poisonous */
+	if (otyp != DART)
+	    otmp->opoisoned = 0;
 	place_object(otmp, ttmp->tx, ttmp->ty);
 	/* Sell your own traps only... */
 	if (ttmp->madeby_u) sellobj(otmp, ttmp->tx, ttmp->ty);
@@ -2396,9 +2764,10 @@
 	struct monst *mtmp = m_at(ttmp->tx,ttmp->ty);
 	int ttype = ttmp->ttyp;
 	boolean under_u = (!u.dx && !u.dy);
-
+	boolean holdingtrap = (ttype == BEAR_TRAP || ttype == WEB);
+	
 	/* Test for monster first, monsters are displayed instead of trap. */
-	if (mtmp && (!mtmp->mtrapped || ttype != BEAR_TRAP)) {
+	if (mtmp && (!mtmp->mtrapped || !holdingtrap)) {
 		pline("%s is in the way.", Monnam(mtmp));
 		return 0;
 	}
@@ -2408,6 +2777,18 @@
 		There("is a boulder in your way.");
 		return 0;
 	}
+	/* duplicate tight-space checks from test_move */
+	if (u.dx && u.dy &&
+	    bad_rock(youmonst.data,u.ux,ttmp->ty) &&
+	    bad_rock(youmonst.data,ttmp->tx,u.uy)) {
+	    if ((invent && (inv_weight() + weight_cap() > 600)) ||
+		bigmonst(youmonst.data)) {
+		/* don't allow untrap if they can't get thru to it */
+		You("are unable to reach the %s!",
+		    defsyms[trap_to_defsym(ttype)].explanation);
+		return 0;
+	    }
+	}
 	/* untrappable traps are located on the ground. */
 	if (!can_reach_floor()) {
 #ifdef STEED
@@ -2425,18 +2806,30 @@
 	if (force_failure || untrap_prob(ttmp)) {
 		if (rnl(5)) {
 		    pline("Whoops...");
-		    if (mtmp) {		/* must be a bear trap */
-			if (mtmp->mtame) abuse_dog(mtmp);
-			if ((mtmp->mhp -= rnd(4)) <= 0) killed(mtmp);
+		    if (mtmp) {		/* must be a trap that holds monsters */
+			if (ttype == BEAR_TRAP) {
+			    if (mtmp->mtame) abuse_dog(mtmp);
+			    if ((mtmp->mhp -= rnd(4)) <= 0) killed(mtmp);
+			} else if (ttype == WEB) {
+			    if (!webmaker(youmonst.data)) {
+				struct trap *ttmp2 = maketrap(u.ux, u.uy, WEB);
+				if (ttmp2) {
+				    pline_The("webbing sticks to you. You're caught too!");
+				    dotrap(ttmp2, NOWEBMSG);
+				}
+			    } else
+				pline("%s remains entangled.", Monnam(mtmp));
+			}
 		    } else if (under_u) {
-			dotrap(ttmp);
+			dotrap(ttmp, 0);
 		    } else {
 			move_into_trap(ttmp);
 		    }
 		} else {
-		    pline("%s %s is difficult to disarm.",
+		    pline("%s %s is difficult to %s.",
 			  ttmp->madeby_u ? "Your" : under_u ? "This" : "That",
-			  defsyms[trap_to_defsym(ttype)].explanation);
+			  defsyms[trap_to_defsym(ttype)].explanation,
+			  (ttype == WEB) ? "remove" : "disarm");
 		}
 		return 1;
 	}
@@ -2449,23 +2842,24 @@
 struct monst *mtmp;
 {
 	if (!ttmp->madeby_u) {
-		if (rnl(10)<8 && !mtmp->mpeaceful &&
-						mtmp->data->mlet != S_HUMAN) {
-			mtmp->mpeaceful = 1;
-			set_malign(mtmp);	/* reset alignment */
-			pline("%s is grateful.", Monnam(mtmp));
-		}
-		/* Helping someone out of a trap is a nice thing to do,
-		 * A lawful may be rewarded, but not too often.  */
-		if (!rn2(3) && !rnl(8) && u.ualign.type == A_LAWFUL) {
-			adjalign(1);
-			You_feel("that you did the right thing.");
-		}
+	    if (rnl(10) < 8 && !mtmp->mpeaceful &&
+		    !mindless(mtmp->data) &&
+		    mtmp->data->mlet != S_HUMAN) {
+		mtmp->mpeaceful = 1;
+		set_malign(mtmp);	/* reset alignment */
+		pline("%s is grateful.", Monnam(mtmp));
+	    }
+	    /* Helping someone out of a trap is a nice thing to do,
+	     * A lawful may be rewarded, but not too often.  */
+	    if (!rn2(3) && !rnl(8) && u.ualign.type == A_LAWFUL) {
+		adjalign(1);
+		You_feel("that you did the right thing.");
+	    }
 	}
 }
 
 STATIC_OVL int
-disarm_beartrap(ttmp) /* Helge Hafting */
+disarm_holdingtrap(ttmp) /* Helge Hafting */
 struct trap *ttmp;
 {
 	struct monst *mtmp;
@@ -2479,11 +2873,20 @@
 	   There's no need for a cockatrice test, only the trap is touched */
 	if ((mtmp = m_at(ttmp->tx,ttmp->ty)) != 0) {
 		mtmp->mtrapped = 0;
-		You("remove %s bear trap from %s.", the_your[ttmp->madeby_u],
+		You("remove %s %s from %s.", the_your[ttmp->madeby_u],
+			(ttmp->ttyp == BEAR_TRAP) ? "bear trap" : "webbing",
 			mon_nam(mtmp));
 		reward_untrap(ttmp, mtmp);
-	} else You("disarm %s bear trap.", the_your[ttmp->madeby_u]);
-	cnv_trap_obj(BEARTRAP, 1, ttmp);
+	} else {
+		if (ttmp->ttyp == BEAR_TRAP) {
+			You("disarm %s bear trap.", the_your[ttmp->madeby_u]);
+			cnv_trap_obj(BEARTRAP, 1, ttmp);
+		} else /* if (ttmp->ttyp == WEB) */ {
+			You("succeed in removing %s web.", the_your[ttmp->madeby_u]);
+			deltrap(ttmp);
+		}
+	}
+	newsym(u.ux + u.dx, u.uy + u.dy);
 	return 1;
 }
 
@@ -2533,6 +2936,7 @@
 	deltrap(ttmp);
 	newsym(u.ux + u.dx, u.uy + u.dy);
 	more_experienced(1, 5);
+	newexplevel();
 	return 1;
 }
 
@@ -2561,16 +2965,17 @@
 {
 	int wc = weight_cap();
 
-	if ((((wt<<1) / wc)+1) >= EXT_ENCUMBER) {
-		pline("%s is %s for you to lift.", Monnam(mtmp),
-			stuff ? "carrying too much" : "too heavy");
-		if (!ttmp->madeby_u && !mtmp->mpeaceful
-			&& mtmp->data->mlet != S_HUMAN && rnl(10) < 3) {
-		    mtmp->mpeaceful = 1;
-		    set_malign(mtmp);		/* reset alignment */
-		    pline("%s thinks it was nice of you to try.", Monnam(mtmp));
-		}
-		return 0;
+	if (((wt * 2) / wc) >= HVY_ENCUMBER) {
+	    pline("%s is %s for you to lift.", Monnam(mtmp),
+		  stuff ? "carrying too much" : "too heavy");
+	    if (!ttmp->madeby_u && !mtmp->mpeaceful && mtmp->mcanmove &&
+		    !mindless(mtmp->data) &&
+		    mtmp->data->mlet != S_HUMAN && rnl(10) < 3) {
+		mtmp->mpeaceful = 1;
+		set_malign(mtmp);		/* reset alignment */
+		pline("%s thinks it was nice of you to try.", Monnam(mtmp));
+	    }
+	    return 0;
 	}
 	return 1;
 }
@@ -2583,6 +2988,7 @@
 {
 	int wt;
 	struct obj *otmp;
+	boolean uprob;
 
 	/*
 	 * This works when levitating too -- consistent with the ability
@@ -2601,7 +3007,7 @@
 	if (check_capacity((char *)0)) return 1;
 
 	/* Will our hero succeed? */
-	if (untrap_prob(ttmp)) {
+	if ((uprob = untrap_prob(ttmp)) && !mtmp->msleeping && mtmp->mcanmove) {
 		You("try to reach out your %s, but %s backs away skeptically.",
 			makeplural(body_part(ARM)),
 			mon_nam(mtmp));
@@ -2625,9 +3031,30 @@
 			return 1;
 		}
 	}
+	/* need to do cockatrice check first if sleeping or paralyzed */
+	if (uprob) {
+	    You("try to grab %s, but cannot get a firm grasp.",
+		mon_nam(mtmp));
+	    if (mtmp->msleeping) {
+		mtmp->msleeping = 0;
+		pline("%s awakens.", Monnam(mtmp));
+	    }
+	    return 1;
+	}
+
 	You("reach out your %s and grab %s.",
 	    makeplural(body_part(ARM)), mon_nam(mtmp));
 
+	if (mtmp->msleeping) {
+	    mtmp->msleeping = 0;
+	    pline("%s awakens.", Monnam(mtmp));
+	} else if (mtmp->mfrozen && !rn2(mtmp->mfrozen)) {
+	    /* After such manhandling, perhaps the effect wears off */
+	    mtmp->mcanmove = 1;
+	    mtmp->mfrozen = 0;
+	    pline("%s stirs.", Monnam(mtmp));
+	}
+
 	/* is the monster too heavy? */
 	wt = inv_weight() + mtmp->data->cwt;
 	if (!try_lift(mtmp, ttmp, wt, FALSE)) return 1;
@@ -2667,7 +3094,8 @@
 		}
 		switch(ttmp->ttyp) {
 			case BEAR_TRAP:
-				return disarm_beartrap(ttmp);
+			case WEB:
+				return disarm_holdingtrap(ttmp);
 			case LANDMINE:
 				return disarm_landmine(ttmp);
 			case SQKY_BOARD:
@@ -2859,7 +3287,7 @@
 			  insider = (*u.ushops && inside_shop(u.ux, u.uy) &&
 				    *in_rooms(ox, oy, SHOPBASE) == *u.ushops);
 
-			  pline("%s explodes!", The(xname(obj)));
+			  pline("%s!", Tobjnam(obj, "explode"));
 			  Sprintf(buf, "exploding %s", xname(obj));
 
 			  if(costly)
@@ -2880,11 +3308,11 @@
 			  exercise(A_STR, FALSE);
 			  if(costly && loss) {
 			      if(insider)
-			      You("owe %ld zorkmids for objects destroyed.",
-							loss);
+			      You("owe %ld %s for objects destroyed.",
+							loss, currency(loss));
 			      else {
-				  You("caused %ld zorkmids worth of damage!",
-							loss);
+				  You("caused %ld %s worth of damage!",
+							loss, currency(loss));
 				  make_angry_shk(shkp, ox, oy);
 			      }
 			  }
@@ -2950,9 +3378,11 @@
 			    if (Hallucination)
 				pline("What a groovy feeling!");
 			    else if (Blind)
-				You("stagger and get dizzy...");
+				You("%s and get dizzy...",
+				    stagger(youmonst.data, "stagger"));
 			    else
-				You("stagger and your vision blurs...");
+				You("%s and your vision blurs...",
+				    stagger(youmonst.data, "stagger"));
 			}
 			make_stunned(HStun + rn1(7, 16),FALSE);
 			make_hallucinated(HHallucination + rn1(5, 16),FALSE,0L);
@@ -3068,9 +3498,8 @@
 	 * obj->spe into account.
 	 */
 	if(!strike) {
-		if (cansee(mon->mx, mon->my))
-			pline("%s is almost hit by %s!", Monnam(mon),
-								doname(obj));
+		if (obj && cansee(mon->mx, mon->my))
+		    pline("%s is almost hit by %s!", Monnam(mon), doname(obj));
 	} else {
 		int dam = 1;
 
@@ -3105,8 +3534,8 @@
 {
 	return((boolean)(multi < 0 && (!nomovemsg ||
 		u.usleep ||
-		!strncmp(nomovemsg,"You regain con", 15) ||
-		!strncmp(nomovemsg,"You are consci", 15))));
+		!strncmp(nomovemsg,"You regain con", 14) ||
+		!strncmp(nomovemsg,"You are consci", 14))));
 }
 
 static char lava_killer[] = "molten lava";
diff -Naurd ../nethack-3.3.1/src/u_init.c ./src/u_init.c
--- ../nethack-3.3.1/src/u_init.c Sat Jul 22 19:39:06 2000
+++ ./src/u_init.c Fri Mar 22 14:41:08 2002
@@ -12,10 +12,10 @@
 	Bitfield(trbless,2);
 };
 
-static void FDECL(ini_inv, (struct trobj *));
-static void FDECL(knows_object,(int));
-static void FDECL(knows_class,(CHAR_P));
-static boolean FDECL(restricted_spell_discipline, (int));
+STATIC_DCL void FDECL(ini_inv, (struct trobj *));
+STATIC_DCL void FDECL(knows_object,(int));
+STATIC_DCL void FDECL(knows_class,(CHAR_P));
+STATIC_DCL boolean FDECL(restricted_spell_discipline, (int));
 
 #define UNDEF_TYP	0
 #define UNDEF_SPE	'\177'
@@ -33,6 +33,7 @@
 	{ FOOD_RATION, 0, FOOD_CLASS, 3, 0 },
 	{ PICK_AXE, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS },
 	{ TINNING_KIT, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS },
+	{ TOUCHSTONE, 0, GEM_CLASS, 1, 0 },
 	{ SACK, 0, TOOL_CLASS, 1, 0 },
 	{ 0, 0, 0, 0, 0 }
 };
@@ -69,8 +70,8 @@
 	{ 0, 0, 0, 0, 0 }
 };
 static struct trobj Knight[] = {
-	{ LONG_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS },
-	{ SPEAR, 2, WEAPON_CLASS, 1, UNDEF_BLESS },
+	{ LONG_SWORD, 1, WEAPON_CLASS, 1, UNDEF_BLESS },
+	{ LANCE, 1, WEAPON_CLASS, 1, UNDEF_BLESS },
 	{ RING_MAIL, 1, ARMOR_CLASS, 1, UNDEF_BLESS },
 	{ HELMET, 0, ARMOR_CLASS, 1, UNDEF_BLESS },
 	{ SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS },
@@ -80,7 +81,7 @@
 	{ 0, 0, 0, 0, 0 }
 };
 static struct trobj Monk[] = {
-#define M_BOOK          2
+#define M_BOOK		2
 	{ LEATHER_GLOVES, 2, ARMOR_CLASS, 1, UNDEF_BLESS },
 	{ ROBE, 1, ARMOR_CLASS, 1, UNDEF_BLESS },
 	{ UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, 1 },
@@ -269,7 +270,7 @@
     { P_TWO_HANDED_SWORD, P_EXPERT },	{ P_SCIMITAR, P_SKILLED },
     { P_SABER, P_BASIC },		{ P_CLUB, P_SKILLED },
     { P_MACE, P_SKILLED },		{ P_MORNING_STAR, P_SKILLED },
-    { P_FLAIL, P_BASIC },               { P_HAMMER, P_EXPERT },
+    { P_FLAIL, P_BASIC },		{ P_HAMMER, P_EXPERT },
     { P_QUARTERSTAFF, P_BASIC },	{ P_SPEAR, P_SKILLED },
     { P_TRIDENT, P_SKILLED },		{ P_BOW, P_BASIC },
     { P_ATTACK_SPELL, P_SKILLED },
@@ -382,15 +383,15 @@
 
 static struct def_skill Skill_Ran[] = {
     { P_DAGGER, P_EXPERT },		 { P_KNIFE,  P_SKILLED },
-    { P_AXE, P_SKILLED },        { P_PICK_AXE, P_BASIC },
-    { P_SHORT_SWORD, P_BASIC },  { P_MORNING_STAR, P_BASIC },
-    { P_FLAIL, P_SKILLED },      { P_HAMMER, P_BASIC },
+    { P_AXE, P_SKILLED },	 { P_PICK_AXE, P_BASIC },
+    { P_SHORT_SWORD, P_BASIC },	 { P_MORNING_STAR, P_BASIC },
+    { P_FLAIL, P_SKILLED },	 { P_HAMMER, P_BASIC },
     { P_QUARTERSTAFF, P_BASIC }, { P_POLEARMS, P_SKILLED },
-    { P_SPEAR, P_SKILLED },      { P_JAVELIN, P_EXPERT },
-    { P_TRIDENT, P_BASIC },      { P_BOW, P_EXPERT },
-    { P_SLING, P_EXPERT },       { P_CROSSBOW, P_EXPERT },
-    { P_DART, P_EXPERT },        { P_SHURIKEN, P_SKILLED },
-    { P_BOOMERANG, P_EXPERT },   { P_WHIP, P_BASIC },
+    { P_SPEAR, P_SKILLED },	 { P_JAVELIN, P_EXPERT },
+    { P_TRIDENT, P_BASIC },	 { P_BOW, P_EXPERT },
+    { P_SLING, P_EXPERT },	 { P_CROSSBOW, P_EXPERT },
+    { P_DART, P_EXPERT },	 { P_SHURIKEN, P_SKILLED },
+    { P_BOOMERANG, P_EXPERT },	 { P_WHIP, P_BASIC },
     { P_HEALING_SPELL, P_BASIC },
     { P_DIVINATION_SPELL, P_EXPERT },
     { P_ESCAPE_SPELL, P_BASIC },
@@ -485,7 +486,7 @@
 };
 
 
-static void
+STATIC_OVL void
 knows_object(obj)
 register int obj;
 {
@@ -496,7 +497,7 @@
 /* Know ordinary (non-magical) objects of a certain class,
  * like all gems except the loadstone and luckstone.
  */
-static void
+STATIC_OVL void
 knows_class(sym)
 register char sym;
 {
@@ -601,8 +602,8 @@
 		if(!rn2(10)) ini_inv(Tinopener);
 		else if(!rn2(4)) ini_inv(Lamp);
 		else if(!rn2(10)) ini_inv(Magicmarker);
-		knows_class(GEM_CLASS);
 		knows_object(SACK);
+		knows_object(TOUCHSTONE);
 		skill_init(Skill_A);
 		break;
 	case PM_BARBARIAN:
@@ -664,38 +665,6 @@
 		 */
 		break;
 	case PM_RANGER:
-#if 0		/* superseded by inv_subs[] */
-		switch (rn2(100) / 20) {
-		case 0:	/* Special racial bow */
-		case 1:
-		case 2:
-		    switch (Race_switch) {
-		    case PM_ELF:
-			Ranger[RAN_BOW].trotyp = ELVEN_BOW;
-			Ranger[RAN_TWO_ARROWS].trotyp =
-			Ranger[RAN_ZERO_ARROWS].trotyp = ELVEN_ARROW;
-			break;
-		    case PM_GNOME:
-			Ranger[RAN_BOW].trotyp = CROSSBOW;
-			Ranger[RAN_TWO_ARROWS].trotyp =
-			Ranger[RAN_ZERO_ARROWS].trotyp = CROSSBOW_BOLT;
-			break;
-		    case PM_ORC:
-			Ranger[RAN_BOW].trotyp = ORCISH_BOW;
-			Ranger[RAN_TWO_ARROWS].trotyp =
-			Ranger[RAN_ZERO_ARROWS].trotyp = ORCISH_ARROW;
-			break;
-		    default: break;	/* Use default bow + arrow */
-		    }
-		    break;
-		case 3:	/* Missiles */
-		    Ranger[RAN_BOW].trotyp = BOOMERANG;
-		    Ranger[RAN_TWO_ARROWS].trotyp =
-		    Ranger[RAN_ZERO_ARROWS].trotyp = DART;
-		    break;
-		default: break;	/* Use default bow + arrow */
-		}
-#endif	/*0*/
 		Ranger[RAN_TWO_ARROWS].trquan = rn1(10, 50);
 		Ranger[RAN_ZERO_ARROWS].trquan = rn1(10, 30);
 		ini_inv(Ranger);
@@ -730,7 +699,6 @@
 		break;
 #endif
 	case PM_VALKYRIE:
-		flags.female = TRUE;
 		ini_inv(Valkyrie);
 		if(!rn2(6)) ini_inv(Lamp);
 		knows_class(WEAPON_CLASS);
@@ -819,10 +787,15 @@
 	default:	/* impossible */
 		break;
 	}
-		
+
 	if (discover)
 		ini_inv(Wishing);
 
+#ifdef WIZARD
+	if (wizard)
+		read_wizkit();
+#endif
+
 	u.ugold0 += hidden_gold();	/* in case sack has gold in it */
 
 	find_ac();			/* get initial ac value */
@@ -850,7 +823,7 @@
 }
 
 /* skills aren't initialized, so we use the role-specific skill lists */
-static boolean
+STATIC_OVL boolean
 restricted_spell_discipline(otyp)
 int otyp;
 {
@@ -883,7 +856,7 @@
     return TRUE;
 }
 
-static void
+STATIC_OVL void
 ini_inv(trop)
 register struct trobj *trop;
 {
@@ -933,7 +906,6 @@
 				|| otyp == POT_ACID
 				|| otyp == SCR_AMNESIA
 				|| otyp == SCR_FIRE
-				|| otyp == SCR_STINKING_CLOUD
 				|| otyp == SCR_BLANK_PAPER
 				|| otyp == SPE_BLANK_PAPER
 				|| otyp == RIN_AGGRAVATE_MONSTER
@@ -984,20 +956,23 @@
 				nocreate4 = otyp;
 		}
 
-		obj->dknown = obj->bknown = obj->rknown = 1;
-		if (objects[otyp].oc_uses_known) obj->known = 1;
-		obj->cursed = 0;
-		if (obj->opoisoned && u.ualign.type != A_CHAOTIC)
-		    obj->opoisoned = 0;
-		if(obj->oclass == WEAPON_CLASS || obj->oclass == TOOL_CLASS) {
-			obj->quan = (long) trop->trquan;
-			trop->trquan = 1;
-		}
-		if(trop->trspe != UNDEF_SPE)
-			obj->spe = trop->trspe;
-		if(trop->trbless != UNDEF_BLESS)
-			obj->blessed = trop->trbless;
-
+			obj->dknown = obj->bknown = obj->rknown = 1;
+			if (objects[otyp].oc_uses_known) obj->known = 1;
+			obj->cursed = 0;
+			if (obj->opoisoned && u.ualign.type != A_CHAOTIC)
+			    obj->opoisoned = 0;
+			if (obj->oclass == WEAPON_CLASS ||
+				obj->oclass == TOOL_CLASS) {
+			    obj->quan = (long) trop->trquan;
+			    trop->trquan = 1;
+			} else if (obj->oclass == GEM_CLASS &&
+				is_graystone(obj) && obj->otyp != FLINT) {
+			    obj->quan = 1L;
+			}
+			if (trop->trspe != UNDEF_SPE)
+			    obj->spe = trop->trspe;
+			if (trop->trbless != UNDEF_BLESS)
+			    obj->blessed = trop->trbless;
 		/* defined after setting otyp+quan + blessedness */
 		obj->owt = weight(obj);
 		obj = addinv(obj);
@@ -1009,9 +984,10 @@
 			discover_object(POT_OIL, TRUE, FALSE);
 
 		if(obj->oclass == ARMOR_CLASS){
-			if (is_shield(obj) && !uarms)
+			if (is_shield(obj) && !uarms) {
 				setworn(obj, W_ARMS);
-			else if (is_helmet(obj) && !uarmh)
+				if (uswapwep) setuswapwep((struct obj *) 0);
+			} else if (is_helmet(obj) && !uarmh)
 				setworn(obj, W_ARMH);
 			else if (is_gloves(obj) && !uarmg)
 				setworn(obj, W_ARMG);
diff -Naurd ../nethack-3.3.1/src/uhitm.c ./src/uhitm.c
--- ../nethack-3.3.1/src/uhitm.c Sat Aug 5 01:21:06 2000
+++ ./src/uhitm.c Fri Mar 22 14:41:08 2002
@@ -37,7 +37,7 @@
 	switch(attk) {
 	    /* 0 is burning, which we should never be called with */
 	    case AD_RUST: hurt = 1; break;
-	    case AD_CORRODE: hurt = 3; break;
+	    case AD_CORR: hurt = 3; break;
 	    default: hurt = 2; break;
 	}
 	/* What the following code does: it keeps looping until it
@@ -87,6 +87,7 @@
 	}
 }
 
+/* FALSE means it's OK to attack */
 boolean
 attack_checks(mtmp, wep)
 register struct monst *mtmp;
@@ -114,13 +115,16 @@
 		return FALSE;
 	}
 
-	/* Put up an invisible monster marker, but one exception is for
-	 * monsters that hide.  That already prints a warning message and
+	/* Put up an invisible monster marker, but with exceptions for
+	 * monsters that hide and monsters you've been warned about.
+	 * The former already prints a warning message and
 	 * prevents you from hitting the monster just via the hidden monster
 	 * code below; if we also did that here, similar behavior would be
-	 * happening two turns in a row.
+	 * happening two turns in a row.  The latter shows a glyph on
+	 * the screen, so you know something is there.
 	 */
 	if (!canspotmon(mtmp) &&
+		    !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) &&
 		    !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) &&
 		    !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) {
 		pline("Wait!  There's %s there you can't see!",
@@ -137,8 +141,9 @@
 		return TRUE;
 	}
 
-	if(mtmp->m_ap_type && !Protection_from_shape_changers
-						&& !sensemon(mtmp)) {
+	if (mtmp->m_ap_type && !Protection_from_shape_changers &&
+	   !sensemon(mtmp) &&
+	   !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy))) {
 		/* If a hidden mimic was in a square where a player remembers
 		 * some (probably different) unseen monster, the player is in
 		 * luck--he attacks it even though it's hidden.
@@ -152,6 +157,7 @@
 	}
 
 	if (mtmp->mundetected && !canseemon(mtmp) &&
+		!glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) &&
 		(hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) {
 	    mtmp->mundetected = mtmp->msleeping = 0;
 	    newsym(mtmp->mx, mtmp->my);
@@ -171,6 +177,15 @@
 	    }
 	}
 
+	/*
+	 * make sure to wake up a monster from the above cases if the
+	 * hero can sense that the monster is there.
+	 */
+	if ((mtmp->mundetected || mtmp->m_ap_type) && sensemon(mtmp)) {
+	    mtmp->mundetected = 0;
+	    wakeup(mtmp);
+	}
+
 	if (flags.confirm && mtmp->mpeaceful
 	    && !Confusion && !Hallucination && !Stunned) {
 		/* Intelligent chaotic weapons (Stormbringer) want blood */
@@ -202,7 +217,8 @@
 
 /*	it is unchivalrous to attack the defenseless or from behind */
 	if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL &&
-	    (!mtmp->mcanmove || mtmp->msleeping || mtmp->mflee) &&
+	    (!mtmp->mcanmove || mtmp->msleeping ||
+	    (mtmp->mflee && !mtmp->mavenge)) &&
 	    u.ualign.record > -10) {
 	    You("caitiff!");
 	    adjalign(-1);
@@ -284,16 +300,22 @@
 		 * there's also a chance of displacing a "frozen" monster.
 		 * sleeping monsters might magically walk in their sleep.
 		 */
-		unsigned int foo = (Punished ||
-				    !rn2(7) || is_longworm(mtmp->data));
+		boolean foo = (Punished || !rn2(7) || is_longworm(mtmp->data)),
+			inshop = FALSE;
+		char *p;
 
-		if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE) || foo
-			|| (IS_ROCK(levl[u.ux][u.uy].typ) &&
+		for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++)
+		    if (tended_shop(&rooms[*p - ROOMOFFSET])) {
+			inshop = TRUE;
+			break;
+		    }
+
+		if (inshop || foo ||
+			(IS_ROCK(levl[u.ux][u.uy].typ) &&
 					!passes_walls(mtmp->data))) {
 		    char buf[BUFSZ];
 
-		    mtmp->mflee = 1;
-		    mtmp->mfleetim = rnd(6);
+		    monflee(mtmp, rnd(6), FALSE, FALSE);
 		    Strcpy(buf, y_monnam(mtmp));
 		    buf[0] = highc(buf[0]);
 		    You("stop.  %s is in the way!", buf);
@@ -397,24 +419,30 @@
 	    /* we hit the monster; be careful: it might die! */
 	    notonhead = (mon->mx != u.ux+u.dx || mon->my != u.uy+u.dy);
 	    malive = hmon(mon, uwep, 0);
-	    if (malive && u.twoweap) malive = hmon(mon, uswapwep, 0);
+	    /*	This assumes that Stormbringer was uwep not uswapwep */ 
+	    if (malive && u.twoweap && !override_confirmation)
+		malive = hmon(mon, uswapwep, 0);
 	    if (malive) {
 		/* monster still alive */
 		if(!rn2(25) && mon->mhp < mon->mhpmax/2
 			    && !(u.uswallow && mon == u.ustuck)) {
 		    /* maybe should regurgitate if swallowed? */
-		    mon->mflee = 1;
 		    if(!rn2(3)) {
-			mon->mfleetim = rnd(100);
-			if (!Blind) pline("%s turns to flee!", (Monnam(mon)));
-		    }
+			monflee(mon, rnd(100), FALSE, TRUE);
+		    } else monflee(mon, 0, FALSE, TRUE);
+
 		    if(u.ustuck == mon && !u.uswallow && !sticks(youmonst.data))
 			u.ustuck = 0;
 		}
 		/* Vorpal Blade hit converted to miss */
 		/* could be headless monster or worm tail */
-		if (mon->mhp == oldhp)
-			*mhit = 0;
+		if (mon->mhp == oldhp) {
+		    *mhit = 0;
+		    /* a miss does not break conduct */
+		    if (uwep &&
+			(uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))
+			--u.uconduct.weaphit;
+		}
 		if (mon->wormno && *mhit)
 			cutworm(mon, u.ux+u.dx, u.uy+u.dy, uwep);
 	    }
@@ -475,6 +503,9 @@
 	boolean get_dmg_bonus = TRUE;
 	boolean ispoisoned = FALSE, needpoismsg = FALSE, poiskilled = FALSE;
 	boolean silvermsg = FALSE;
+#ifdef STEED
+	boolean jousting = FALSE;
+#endif
 	boolean valid_weapon_attack = FALSE;
 	int wtype;
 	struct obj *monwep;
@@ -544,9 +575,9 @@
 			  ((wtype = uwep_skill_type()) != P_NONE &&
 			    P_SKILL(wtype) >= P_SKILLED) &&
 			  ((monwep = MON_WEP(mon)) != 0 &&
-			    weapon_type(monwep) != P_WHIP &&
-			    !obj_resists(monwep,
-				 50 + 15 * greatest_erosion(monwep), 100))) {
+			   !is_flimsy(monwep) &&
+			   !obj_resists(monwep,
+				 50 + 15 * greatest_erosion(obj), 100))) {
 			/*
 			 * 2.5% chance of shattering defender's weapon when
 			 * using a two-handed weapon; less if uwep is rusted.
@@ -554,17 +585,16 @@
 			 * -bisecting hit, in case of special artifact damage;
 			 * the percentage chance is (1/20)*(50/100).]
 			 */
-			monwep->owornmask &= ~W_WEP;
+			setmnotwielded(mon,monwep);
 			MON_NOWEP(mon);
 			mon->weapon_check = NEED_WEAPON;
-			pline("%s %s shatter%s from the force of your blow!",
+			pline("%s %s %s from the force of your blow!",
 			      s_suffix(Monnam(mon)), xname(monwep),
-			      (monwep->quan) == 1L ? "s" : "");
+			      otense(monwep, "shatter"));
 			m_useup(mon, monwep);
 			/* If someone just shattered MY weapon, I'd flee! */
-			if (rn2(4) && !mon->mflee) {
-			    mon->mflee = 1;
-			    mon->mfleetim = d(2,3);
+			if (rn2(4)) {
+			    monflee(mon, d(2,3), TRUE, TRUE);
 			}
 			hittxt = TRUE;
 		    }
@@ -579,6 +609,11 @@
 		    if (objects[obj->otyp].oc_material == SILVER
 				&& hates_silver(mdat))
 			silvermsg = TRUE;
+#ifdef STEED
+		    if (u.usteed && !thrown &&
+				weapon_type(obj) == P_LANCE && mon != u.ustuck)
+			jousting = TRUE;
+#endif
 		    if(!thrown && obj == uwep && obj->otyp == BOOMERANG &&
 		       !rnl(3)) {
 			pline("As you hit %s, %s breaks into splinters.",
@@ -607,7 +642,7 @@
 		}
 	    } else if(obj->oclass == POTION_CLASS) {
 		if (obj->quan > 1L)
-		    setworn(splitobj(obj, 1L), W_WEP);
+		    obj = splitobj(obj, 1L);
 		else
 		    setuwep((struct obj *)0);
 		freeinv(obj);
@@ -705,7 +740,7 @@
 					      (cnt > 1L) ? "some" : "an";
 			    You("hit %s with %s egg%s.",
 				mon_nam(mon), eggp, plur(cnt));
-			    if (touch_petrifies(mdat)) {
+			    if (touch_petrifies(mdat) && !stale_egg(obj)) {
 				pline_The("egg%s %s alive any more...",
 				      plur(cnt),
 				      (cnt == 1L) ? "isn't" : "aren't");
@@ -728,9 +763,7 @@
 		      }
 		    case CLOVE_OF_GARLIC:	/* no effect against demons */
 			if (is_undead(mdat)) {
-			    mon->mflee = 1;
-			    mon->mfleetim += d(2,4);
-			    pline("%s turns to flee!", Monnam(mon));
+			    monflee(mon, d(2, 4), FALSE, TRUE);
 			}
 			tmp = 1;
 			break;
@@ -835,8 +868,8 @@
 	    }
 	    if (obj && !rn2(nopoison)) {
 		obj->opoisoned = FALSE;
-		Your("%s%s no longer poisoned.", xname(obj),
-		     (obj->quan == 1L) ? " is" : "s are");	/**FIXME**/
+		Your("%s %s no longer poisoned.", xname(obj),
+		     otense(obj, "are"));
 	    }
 	    if (resists_poison(mon))
 		needpoismsg = TRUE;
@@ -859,29 +892,24 @@
 	    }
 	}
 
+#ifdef STEED
+	if (jousting) {
+	    You("joust %s%s",
+			 mon_nam(mon), canseemon(mon) ? exclam(tmp) : ".");
+	    mhurtle(mon, u.dx, u.dy, 1);
+	    hittxt = TRUE;
+	} else
+#endif
+
 	/* VERY small chance of stunning opponent if unarmed. */
 	if (tmp > 1 && !thrown && !obj && !uwep && !uarm && !uarms && !Upolyd) {
 	    if (rnd(100) < P_SKILL(P_BARE_HANDED_COMBAT) &&
 			!bigmonst(mdat) && !thick_skinned(mdat)) {
 		if (canspotmon(mon))
-		    pline("%s staggers from your powerful strike!",
-			  Monnam(mon));
-		mon->mstun = 1;
+		    pline("%s %s from your powerful strike!", Monnam(mon),
+			  makeplural(stagger(mon->data, "stagger")));
+		mhurtle(mon, u.dx, u.dy, 1);
 		hittxt = TRUE;
-		if (mon->mcanmove && mon != u.ustuck) {
-		    xchar mdx, mdy;
-
-		    /* see if the monster has a place to move into */
-		    mdx = mon->mx + u.dx;
-		    mdy = mon->my + u.dy;
-		    if (goodpos(mdx, mdy, mon)) {
-			remove_monster(mon->mx, mon->my);
-			newsym(mon->mx, mon->my);
-			place_monster(mon, mdx, mdy);
-			newsym(mon->mx, mon->my);
-			set_apparxy(mon);
-		    }
-		}
 	    }
 	}
 
@@ -889,12 +917,8 @@
 	if(mon->mhp < 1)
 		destroyed = TRUE;
 	if (mon->mtame && (!mon->mflee || mon->mfleetim) && tmp > 0) {
-		unsigned fleetim;
-
 		abuse_dog(mon);
-		mon->mflee = TRUE;		/* Rick Richardson */
-		fleetim = mon->mfleetim + (unsigned)(10 * rnd(tmp));
-		mon->mfleetim = min(fleetim,127);
+		monflee(mon, 10 * rnd(tmp), FALSE, FALSE);
 	}
 	if((mdat == &mons[PM_BLACK_PUDDING] || mdat == &mons[PM_BROWN_PUDDING])
 		   && obj && obj == uwep
@@ -907,11 +931,10 @@
 		}
 	}
 
-	if(!hittxt && !destroyed) {
-		if(thrown)
-		    /* thrown => obj exists */
-		    hit(xname(obj), mon, exclam(tmp) );
-		else if(!flags.verbose) You("hit it.");
+	if (!hittxt &&			/*( thrown => obj exists )*/
+	  (!destroyed || (thrown && m_shot.n > 1 && m_shot.o == obj->otyp))) {
+		if (thrown) hit(mshot_xname(obj), mon, exclam(tmp));
+		else if (!flags.verbose) You("hit it.");
 		else You("%s %s%s", Role_if(PM_BARBARIAN) ? "smite" : "hit",
 			 mon_nam(mon), canseemon(mon) ? exclam(tmp) : ".");
 	}
@@ -991,7 +1014,7 @@
 		/* avoid "slippery slippery cloak"
 		   for undiscovered oilskin cloak */
 		(obj->greased || objects[obj->otyp].oc_name_known) ?
-			xname(obj) : "cloak");
+			xname(obj) : cloak_simple_name(obj));
 
 	    if (obj->greased && !rn2(2)) {
 		pline_The("grease wears off.");
@@ -1013,11 +1036,13 @@
 STATIC_OVL void
 demonpet()
 {
+	int i;
 	struct permonst *pm;
 	struct monst *dtmp;
 
 	pline("Some hell-p has arrived!");
-	pm = !rn2(6) ? &mons[ndemon(u.ualign.type)] : youmonst.data;
+	i = !rn2(6) ? ndemon(u.ualign.type) : NON_PM;
+	pm = i != NON_PM ? &mons[i] : youmonst.data;
 	if ((dtmp = makemon(pm, u.ux, u.uy, NO_MM_FLAGS)) != 0)
 	    (void)tamedog(dtmp, (struct obj *)0);
 	exercise(A_WIS, TRUE);
@@ -1069,8 +1094,7 @@
 		    mon_nam(mdef));
 	    else
 		You("seduce %s and %s starts to take off %s clothes.",
-		    mon_nam(mdef), he[pronoun_gender(mdef)],
-		    his[pronoun_gender(mdef)]);
+		    mon_nam(mdef), mhe(mdef), mhis(mdef));
 	}
 
 	while ((otmp = mdef->minvent) != 0) {
@@ -1078,12 +1102,14 @@
 	    obj_extract_self(otmp);
 	    if ((unwornmask = otmp->owornmask) != 0L) {
 		mdef->misc_worn_check &= ~unwornmask;
+		if (otmp->owornmask & W_WEP)
+		    setmnotwielded(mdef,otmp);
 		otmp->owornmask = 0L;
 		update_mon_intrinsics(mdef, otmp, FALSE);
 
 		if (otmp == stealoid)	/* special message for final item */
 		    pline("%s finishes taking off %s suit.",
-			  Monnam(mdef), his[pronoun_gender(mdef)]);
+			  Monnam(mdef), mhis(mdef));
 	    }
 	    /* give the object to the character */
 	    otmp = hold_another_object(otmp, "You steal %s.",
@@ -1126,7 +1152,8 @@
 	switch(mattk->adtyp) {
 	    case AD_STUN:
 		if(!Blind)
-		    pline("%s staggers for a moment.", Monnam(mdef));
+		    pline("%s %s for a moment.", Monnam(mdef),
+			  makeplural(stagger(mdef->data, "stagger")));
 		mdef->mstun = 1;
 		/* fall through to next case */
 	    case AD_WERE:	    /* no effect on monsters */
@@ -1149,6 +1176,7 @@
 	    case AD_FIRE:
 		if (!Blind)
 		    pline("%s is %s!", Monnam(mdef),
+			  mdef->data == &mons[PM_WATER_ELEMENTAL] ? "boiling" :
 			  mattk->aatyp == AT_HUGS ?
 				"being roasted" : "on fire");
 		if (pd == &mons[PM_STRAW_GOLEM] ||
@@ -1277,8 +1305,8 @@
 		hurtmarmor(mdef, AD_RUST);
 		tmp = 0;
 		break;
-	    case AD_CORRODE:
-		hurtmarmor(mdef, AD_CORRODE);
+	    case AD_CORR:
+		hurtmarmor(mdef, AD_CORR);
 		tmp = 0;
 		break;
 	    case AD_DCAY:
@@ -1310,18 +1338,29 @@
 		if (notonhead || !has_head(mdef->data)) {
 		    pline("%s doesn't seem harmed.", Monnam(mdef));
 		    tmp = 0;
+		    if (!Unchanging && mdef->data == &mons[PM_GREEN_SLIME]) {
+			if (!Slimed) {
+			    You("suck in some slime and don't feel very well.");
+			    Slimed = 10L;
+			}
+		    }
 		    break;
 		}
 		if (m_slips_free(mdef, mattk)) break;
 
 		if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) {
 		    pline("%s helmet blocks your attack to %s head.",
-			  s_suffix(Monnam(mdef)), his[pronoun_gender(mdef)]);
+			  s_suffix(Monnam(mdef)), mhis(mdef));
 		    break;
 		}
 
 		You("eat %s brain!", s_suffix(mon_nam(mdef)));
 		u.uconduct.food++;
+		if (touch_petrifies(mdef->data) && !Stone_resistance && !Stoned) {
+		    Stoned = 5;
+		    killer_format = KILLED_BY_AN;
+		    delayed_killer = mdef->data->mname;
+		}
 		if (!vegan(mdef->data))
 		    u.uconduct.unvegan++;
 		if (!vegetarian(mdef->data))
@@ -1387,9 +1426,10 @@
 	    case AD_SLIM:
 	    	if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
 	    			mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
+	    			mdef->data != &mons[PM_SALAMANDER] &&
 	    			mdef->data != &mons[PM_GREEN_SLIME]) {
 	    	    You("turn %s into slime.", mon_nam(mdef));
-	    	    (void) newcham(mdef, &mons[PM_GREEN_SLIME]);
+	    	    (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE);
 	    	    tmp = 0;
 	    	}
 	    	break;
@@ -1515,7 +1555,8 @@
 	    for (otmp = mdef->minvent; otmp; otmp = otmp->nobj)
 		(void) snuff_lit(otmp);
 
-	    if(!touch_petrifies(mdef->data) || Stone_resistance) {
+	    if((!touch_petrifies(mdef->data) || Stone_resistance) &&
+		    (Unchanging || mdef->data != &mons[PM_GREEN_SLIME])) {
 #ifdef LINT	/* static char msgbuf[BUFSZ]; */
 		char msgbuf[BUFSZ];
 #else
@@ -1554,12 +1595,19 @@
 			newuhs(FALSE);
 			xkilled(mdef,2);
 			if (mdef->mhp > 0) { /* monster lifesaved */
-			    You("hurriedly regurgitate the sizzling in your stomach.");
+			    You("hurriedly regurgitate the sizzling in your %s.",
+				body_part(STOMACH));
 			} else {
-			    u.uhunger += mdef->data->cnutrit;
+			    tmp = 1 + (mdef->data->cwt >> 8);
+			    if (corpse_chance(mdef, &youmonst, TRUE) &&
+				!(mvitals[monsndx(mdef->data)].mvflags &
+				  G_NOCORPSE)) {
+				/* nutrition only if there can be a corpse */
+				u.uhunger += (mdef->data->cnutrit+1) / 2;
+			    } else tmp = 0;
 			    Sprintf(msgbuf, "You totally digest %s.",
 					    mon_nam(mdef));
-			    if ((tmp = 3 + (mdef->data->cwt >> 6)) != 0) {
+			    if (tmp != 0) {
 				/* setting afternmv = end_engulf is tempting,
 				 * but will cause problems if the player is
 				 * attacked (which uses his real location) or
@@ -1669,7 +1717,7 @@
 register struct monst *mon;
 register int tmp;
 {
-	register struct attack *mattk;
+	struct attack *mattk, alt_attk;
 	int	i, sum[NATTK], hittmp = 0;
 	int	nsum = 0;
 	int	dhit = 0;
@@ -1677,7 +1725,7 @@
 	for(i = 0; i < NATTK; i++) {
 
 	    sum[i] = 0;
-	    mattk = &(youmonst.data->mattk[i]);
+	    mattk = getmattk(youmonst.data, i, sum, &alt_attk);
 	    switch(mattk->aatyp) {
 		case AT_WEAP:
 use_weapon:
@@ -1702,7 +1750,7 @@
 			if (!known_hitum(mon,&dhit,mattk)) {
 			    sum[i] = 2;
 			    break;
-			} else sum[i] = 1;
+			} else sum[i] = dhit;
 			/* might be a worm that gets cut in half */
 			if (m_at(u.ux+u.dx, u.uy+u.dy) != mon) return((boolean)(nsum != 0));
 			/* Do not print "You hit" message, since known_hitum
@@ -1804,8 +1852,18 @@
 				if (mon->data == &mons[PM_SHADE])
 				    Your("attempt to surround %s is harmless.",
 					mon_nam(mon));
-				else
+				else {
 				    sum[i]= gulpum(mon,mattk);
+				    if (sum[i] == 2 &&
+					    (mon->data->mlet == S_ZOMBIE ||
+						mon->data->mlet == S_MUMMY) &&
+					    rn2(5) &&
+					    !Sick_resistance) {
+					You_feel("%ssick.",
+					    (Sick) ? "very " : "");
+					mdamageu(mon, rnd(8));
+				    }
+				}
 			} else
 				missum(mon, mattk);
 			break;
@@ -1889,12 +1947,13 @@
 			mdamageu(mon, tmp);
 		if(!rn2(30)) erode_armor(&youmonst, TRUE);
 	    }
-	    if(mhit && !rn2(6)) {
+	    if (mhit) {
 		if (aatyp == AT_KICK) {
-		    if (uarmf)
-			(void) rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst);
-		} else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH)
-		    erode_weapon(uwep, TRUE);
+		    if (uarmf && !rn2(6))
+			(void)rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst);
+		} else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
+			   aatyp == AT_MAGC || aatyp == AT_TUCH)
+		    passive_obj(mon, (struct obj*)0, &(ptr->mattk[i]));
 	    }
 	    exercise(A_STR, FALSE);
 	    break;
@@ -1918,22 +1977,22 @@
 	    break;
 	  case AD_RUST:
 	    if(mhit && !mon->mcan) {
-	      if (aatyp == AT_KICK) {
-		if (uarmf)
-		    (void) rust_dmg(uarmf, xname(uarmf), 1, TRUE, &youmonst);
-	      } else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
-			aatyp == AT_MAGC || aatyp == AT_TUCH)
-		erode_weapon(uwep, FALSE);
+		if (aatyp == AT_KICK) {
+		    if (uarmf)
+			(void)rust_dmg(uarmf, xname(uarmf), 1, TRUE, &youmonst);
+		} else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
+			   aatyp == AT_MAGC || aatyp == AT_TUCH)
+		    passive_obj(mon, (struct obj*)0, &(ptr->mattk[i]));
 	    }
 	    break;
-	  case AD_CORRODE:
+	  case AD_CORR:
 	    if(mhit && !mon->mcan) {
-	      if (aatyp == AT_KICK) {
-		if (uarmf)
-		    (void) rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst);
-	      } else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
-			aatyp == AT_MAGC || aatyp == AT_TUCH)
-		erode_weapon(uwep, TRUE);
+		if (aatyp == AT_KICK) {
+		    if (uarmf)
+			(void)rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst);
+		} else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
+			   aatyp == AT_MAGC || aatyp == AT_TUCH)
+		    passive_obj(mon, (struct obj*)0, &(ptr->mattk[i]));
 	    }
 	    break;
 	  case AD_MAGM:
@@ -1947,17 +2006,16 @@
 	    }
 	    break;
 	  case AD_ENCH:	/* KMH -- remove enchantment (disenchanter) */
-	    {
-	  		struct obj *obj = (aatyp == AT_KICK) ? uarmf :
-	  				uwep ? uwep : uarmg;
+	    if (mhit) {
+		struct obj *obj = (struct obj *)0;
 
-	    	if (mhit && !mon->mcan && obj) {
-	    	    if (drain_item(obj) && (obj->known ||
-	    	    		obj->oclass == ARMOR_CLASS))
-	    	    	Your("%s less effective.", aobjnam(obj, "seem"));
-	    	}
-	    	break;
+		if (aatyp == AT_KICK) {
+		    obj = uarmf;
+		    if (!obj) break;
+		}
+		passive_obj(mon, obj, &(ptr->mattk[i]));
 	    }
+	    break;
 	  default:
 	    break;
 	}
@@ -1994,6 +2052,7 @@
 		    You("momentarily stiffen.");
 		} else { /* gelatinous cube */
 		    You("are frozen by %s!", mon_nam(mon));
+	    	    nomovemsg = 0;	/* default: "you can move again" */
 		    nomul(-tmp);
 		    exercise(A_DEX, FALSE);
 		}
@@ -2049,6 +2108,68 @@
 	return(malive | mhit);
 }
 
+/*
+ * Special (passive) attacks on an attacking object by monsters done here.
+ * Assumes the attack was successful.
+ */
+void
+passive_obj(mon, obj, mattk)
+register struct monst *mon;
+register struct obj *obj;	/* null means pick uwep, uswapwep or uarmg */
+struct attack *mattk;		/* null means we find one internally */
+{
+	register struct permonst *ptr = mon->data;
+	register int i;
+
+	/* if caller hasn't specified an object, use uwep, uswapwep or uarmg */
+	if (!obj) {
+	    obj = (u.twoweap && uswapwep && !rn2(2)) ? uswapwep : uwep;
+	    if (!obj && mattk->adtyp == AD_ENCH)
+		obj = uarmg;		/* no weapon? then must be gloves */
+	    if (!obj) return;		/* no object to affect */
+	}
+
+	/* if caller hasn't specified an attack, find one */
+	if (!mattk) {
+	    for(i = 0; ; i++) {
+		if(i >= NATTK) return;	/* no passive attacks */
+		if(ptr->mattk[i].aatyp == AT_NONE) break; /* try this one */
+	    }
+	    mattk = &(ptr->mattk[i]);
+	}
+
+	switch(mattk->adtyp) {
+
+	case AD_ACID:
+	    if(!rn2(6)) {
+		erode_obj(obj, TRUE, FALSE);
+	    }
+	    break;
+	case AD_RUST:
+	    if(!mon->mcan) {
+		erode_obj(obj, FALSE, FALSE);
+	    }
+	    break;
+	case AD_CORR:
+	    if(!mon->mcan) {
+		erode_obj(obj, TRUE, FALSE);
+	    }
+	    break;
+	case AD_ENCH:
+	    if (!mon->mcan) {
+		if (drain_item(obj) && carried(obj) &&
+		    (obj->known || obj->oclass == ARMOR_CLASS)) {
+		    Your("%s less effective.", aobjnam(obj, "seem"));
+	    	}
+	    	break;
+	    }
+	  default:
+	    break;
+	}
+
+	if (carried(obj)) update_inventory();
+}
+
 /* Note: caller must ascertain mtmp is mimicking... */
 void
 stumble_onto_mimic(mtmp)
@@ -2149,8 +2270,10 @@
 		if (mtmp->mhp > 0) {
 		    if (!flags.mon_moving) setmangry(mtmp);
 		    if (tmp < 9 && !mtmp->isshk && rn2(4)) {
-			mtmp->mflee = 1;
-			if (rn2(4)) mtmp->mfleetim = rnd(100);
+			if (rn2(4))
+			    monflee(mtmp, rnd(100), FALSE, TRUE);
+			else
+			    monflee(mtmp, 0, FALSE, TRUE);
 		    }
 		    mtmp->mcansee = 0;
 		    mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50/tmp);
diff -Naurd ../nethack-3.3.1/src/vault.c ./src/vault.c
--- ../nethack-3.3.1/src/vault.c Sat Aug 5 01:21:29 2000
+++ ./src/vault.c Fri Mar 22 14:41:08 2002
@@ -7,6 +7,9 @@
 
 STATIC_DCL struct monst *NDECL(findgd);
 
+#define g_monnam(mtmp) \
+	x_monnam(mtmp, ARTICLE_NONE, (char *)0, SUPPRESS_IT, FALSE)
+
 #ifdef OVLB
 
 STATIC_DCL boolean FDECL(clear_fcorr, (struct monst *,BOOLEAN_P));
@@ -23,6 +26,8 @@
 	register int fcx, fcy, fcbeg;
 	register struct monst *mtmp;
 
+	if (!on_level(&(EGD(grd)->gdlevel), &u.uz)) return TRUE;
+
 	while((fcbeg = EGD(grd)->fcbeg) < EGD(grd)->fcend) {
 		fcx = EGD(grd)->fakecorr[fcbeg].fx;
 		fcy = EGD(grd)->fakecorr[fcbeg].fy;
@@ -142,7 +147,6 @@
 	char buf[BUFSZ];
 	register int x, y, dd, gx, gy;
 	int lx = 0, ly = 0;
-
 	/* first find the goal for the guard */
 	for(dd = 2; (dd < ROWNO || dd < COLNO); dd++) {
 	  for(y = u.uy-dd; y <= u.uy+dd; ly = y, y++) {
@@ -233,9 +237,17 @@
 	}
 
 	reset_faint();			/* if fainted - wake up */
-	pline("Suddenly one of the Vault's guards enters!");
+	pline("Suddenly one of the Vault's %s enters!",
+	      makeplural(g_monnam(guard)));
 	newsym(guard->mx,guard->my);
-	if (Strangled || youmonst.data->msound == MS_SILENT) {
+	if ((youmonst.m_ap_type == M_AP_OBJECT &&
+		youmonst.mappearance == GOLD_PIECE) || u.uundetected) {
+	    /* You're mimicking a pile of gold or you're hidden. */
+	    pline("Puzzled, %s turns around and leaves.", mhe(guard));
+	    mongone(guard);
+	    return;
+	}
+	if (Strangled || is_silent(youmonst.data)) {
 	    verbalize("I'll be back when you're ready to speak to me!");
 	    mongone(guard);
 	    return;
@@ -404,7 +416,7 @@
 
 	if(movedgold || fixed) {
 	    if(in_fcorridor(grd, grd->mx, grd->my) || cansee(grd->mx, grd->my))
-		pline_The("guard whispers an incantation.");
+		pline_The("%s whispers an incantation.", g_monnam(grd));
 	    else You_hear("a distant chant.");
 	    if(movedgold)
 		pline("A mysterious force moves the gold into the vault.");
@@ -432,7 +444,6 @@
 					TRUE : FALSE;
 	boolean disappear_msg_seen = FALSE, semi_dead = (grd->mhp <= 0);
 	register boolean u_carry_gold = ((u.ugold + hidden_gold()) > 0L);
-
 	if(!on_level(&(egrd->gdlevel), &u.uz)) return(-1);
 	nx = ny = m = n = 0;
 	if(!u_in_vault && !grd_in_vault)
@@ -463,7 +474,8 @@
 			(u_carry_gold || um_dist(grd->mx, grd->my, 1))) {
 		if(egrd->warncnt == 3)
 			verbalize("I repeat, %sfollow me!",
-				u_carry_gold ? (!u.ugold ?
+				u_carry_gold ? (
+					  !u.ugold ?
 					  "drop that hidden gold and " :
 					  "drop that gold and ") : "");
 		if(egrd->warncnt == 7) {
@@ -490,13 +502,13 @@
 		    newsym(m,n);
 		    grd->mpeaceful = 0;
 letknow:
-		    if(!cansee(grd->mx, grd->my))
+		    if (!cansee(grd->mx, grd->my) || !mon_visible(grd))
 			You_hear("the shrill sound of a guard's whistle.");
 		    else
 			You(um_dist(grd->mx, grd->my, 2) ?
 			    "see an angry %s approaching." :
 			    "are confronted by an angry %s.",
-			    l_monnam(grd));
+			    g_monnam(grd));
 		    return(-1);
 		} else {
 		    verbalize("Well, begone.");
@@ -512,7 +524,7 @@
 		  !egrd->gddone && !in_fcorridor(grd, u.ux, u.uy) &&
 		  levl[egrd->fakecorr[0].fx][egrd->fakecorr[0].fy].typ
 				 == egrd->fakecorr[0].ftyp) {
-		pline_The("guard, confused, disappears.");
+		pline_The("%s, confused, disappears.", g_monnam(grd));
 		disappear_msg_seen = TRUE;
 		goto cleanup;
 	    }
@@ -687,7 +699,7 @@
 		if(!semi_dead && (in_fcorridor(grd, u.ux, u.uy) ||
 				     cansee(x, y))) {
 		    if (!disappear_msg_seen)
-			pline("Suddenly, the guard disappears.");
+			pline("Suddenly, the %s disappears.", g_monnam(grd));
 		    return(1);
 		}
 		return(-2);
@@ -713,8 +725,9 @@
 	if (!u.ugold || !grd) return;
 
 	if (u.uinvault) {
-	    Your("%ld zorkmid%s goes into the Magic Memory Vault.",
-		u.ugold, plur(u.ugold));
+	    Your("%ld %s goes into the Magic Memory Vault.",
+		u.ugold,
+		currency(u.ugold));
 	    gx = u.ux;
 	    gy = u.uy;
 	} else {
diff -Naurd ../nethack-3.3.1/src/version.c ./src/version.c
--- ../nethack-3.3.1/src/version.c Sun Dec 5 03:12:18 1999
+++ ./src/version.c Fri Mar 22 14:40:55 2002
@@ -68,7 +68,7 @@
 		   version_data->entity_count != VERSION_SANITY1 ||
 		   version_data->struct_sizes != VERSION_SANITY2) {
 	    if (complain)
-		pline("Configuration incompatability for file \"%s\".",
+		pline("Configuration incompatibility for file \"%s\".",
 		      filename);
 	    return FALSE;
 	}
diff -Naurd ../nethack-3.3.1/src/vision.c ./src/vision.c
--- ../nethack-3.3.1/src/vision.c Sun Jan 30 19:01:24 2000
+++ ./src/vision.c Fri Mar 22 14:40:55 2002
@@ -242,6 +242,7 @@
 	}
     }
 
+    iflags.vision_inited = 1;	/* vision is ready */
     vision_full_recalc = 1;	/* we want to run vision_recalc() */
 }
 
@@ -510,7 +511,7 @@
     int oldseenv;				/* previous seenv value */
 
     vision_full_recalc = 0;			/* reset flag */
-    if (in_mklev) return;
+    if (in_mklev || !iflags.vision_inited) return;
 
 #ifdef GCC_WARN
     row = 0;
@@ -725,12 +726,13 @@
 		/*
 		 * We see this position because it is lit.
 		 */
-		if (IS_DOOR(lev->typ) && !viz_clear[row][col]) {
+		if ((IS_DOOR(lev->typ) || lev->typ == SDOOR ||
+		     IS_WALL(lev->typ)) && !viz_clear[row][col]) {
 		    /*
-		     * Make sure doors, boulders or mimics don't show up
+		     * Make sure doors, walls, boulders or mimics don't show up
 		     * at the end of dark hallways.  We do this by checking
 		     * the adjacent position.  If it is lit, then we can see
-		     * the door, otherwise we can't.
+		     * the door or wall, otherwise we can't.
 		     */
 		    dx = u.ux - col;	dx = sign(dx);
 		    flev = &(levl[col+dx][row+dy]);
@@ -793,7 +795,13 @@
     colbump[u.ux] = colbump[u.ux+1] = 0;
 
 skip:
-    newsym(u.ux,u.uy);		/* Make sure the hero shows up! */
+    /* This newsym() caused a crash delivering msg about failure to open
+     * dungeon file init_dungeons() -> panic() -> done(11) ->
+     * vision_recalc(2) -> newsym() -> crash!  u.ux and u.uy are 0 and
+     * program_state.panicking == 1 under those circumstances
+     */
+    if (!program_state.panicking)
+	newsym(u.ux, u.uy);		/* Make sure the hero shows up! */
 
     /* Set the new min and max pointers. */
     viz_rmin  = next_rmin;
diff -Naurd ../nethack-3.3.1/src/weapon.c ./src/weapon.c
--- ../nethack-3.3.1/src/weapon.c Sun May 28 16:08:42 2000
+++ ./src/weapon.c Fri Mar 22 14:40:55 2002
@@ -26,6 +26,8 @@
 #define PN_ESCAPE_SPELL			(-13)
 #define PN_MATTER_SPELL			(-14)
 
+STATIC_DCL void FDECL(give_may_advance_msg, (int));
+
 #ifndef OVLB
 
 STATIC_DCL NEARDATA const short skill_names_indices[];
@@ -93,6 +95,8 @@
 #endif	/* OVLB */
 
 STATIC_DCL boolean FDECL(can_advance, (int, BOOLEAN_P));
+STATIC_DCL boolean FDECL(could_advance, (int));
+STATIC_DCL boolean FDECL(peaked_skill, (int));
 STATIC_DCL int FDECL(slots_required, (int));
 
 #ifdef OVL1
@@ -181,6 +185,7 @@
  * Second edition AD&D came out a few years later; luckily it used the same
  * table.  As of this writing (1999), third edition is in progress but not
  * released.  Let's see if the weapon table stays the same.  --KAA
+ * October 2000: It didn't.  Oh, well.
  */
 
 /*
@@ -507,12 +512,15 @@
 	/* only strong monsters can wield big (esp. long) weapons */
 	/* big weapon is basically the same as bimanual */
 	/* all monsters can wield the remaining weapons */
-	for (i = 0; i < SIZE(hwep); i++)
+	for (i = 0; i < SIZE(hwep); i++) {
+	    if (hwep[i] == CORPSE && !(mtmp->misc_worn_check & W_ARMG))
+		continue;
 	    if (((strong && !wearing_shield)
 			|| !objects[hwep[i]].oc_bimanual) &&
 		    (objects[hwep[i]].oc_material != SILVER
 			|| !hates_silver(mtmp->data)))
 		Oselect(hwep[i]);
+	}
 
 	/* failure */
 	return (struct obj *)0;
@@ -538,7 +546,7 @@
 		return;
 	}
 	if (!attacktype(mon->data, AT_WEAP)) {
-		mw_tmp->owornmask &= ~W_WEP;
+		setmnotwielded(mon, mw_tmp);
 		MON_NOWEP(mon);
 		mon->weapon_check = NO_WEAPON_WANTED;
 		obj_extract_self(obj);
@@ -565,7 +573,8 @@
 	 * polymorphed into little monster.  But it's not quite clear how to
 	 * handle this anyway....
 	 */
-	mon->weapon_check = NEED_WEAPON;
+	if (!(mw_tmp->cursed && mon->weapon_check == NO_WEAPON_WANTED))
+	    mon->weapon_check = NEED_WEAPON;
 }
 
 /* Let a monster try to wield a weapon, based on mon->weapon_check.
@@ -615,8 +624,8 @@
 
 			if (bimanual(mw_tmp)) mon_hand = makeplural(mon_hand);
 			Sprintf(welded_buf, "%s welded to %s %s",
-				(mw_tmp->quan == 1L) ? "is" : "are",
-				his[pronoun_gender(mon)], mon_hand);
+				otense(mw_tmp, "are"),
+				mhis(mon), mon_hand);
 
 			if (obj->otyp == PICK_AXE) {
 			    pline("Since %s weapon%s %s,",
@@ -637,18 +646,24 @@
 		    return 1;
 		}
 		mon->mw = obj;		/* wield obj */
-		if (mw_tmp) mw_tmp->owornmask &= ~W_WEP;
+		setmnotwielded(mon, mw_tmp);
 		mon->weapon_check = NEED_WEAPON;
 		if (canseemon(mon)) {
-			pline("%s wields %s!", Monnam(mon), doname(obj));
-			if (obj->cursed && obj->otyp != CORPSE) {
-				pline("%s %s to %s %s!",
-					The(xname(obj)),
-					(obj->quan == 1L) ? "welds itself"
-					    : "weld themselves",
-					s_suffix(mon_nam(mon)), mbodypart(mon,HAND));
-				obj->bknown = 1;
-			}
+		    pline("%s wields %s!", Monnam(mon), doname(obj));
+		    if (obj->cursed && obj->otyp != CORPSE) {
+			pline("%s %s to %s %s!",
+			    Tobjnam(obj, "weld"),
+			    is_plural(obj) ? "themselves" : "itself",
+			    s_suffix(mon_nam(mon)), mbodypart(mon,HAND));
+			obj->bknown = 1;
+		    }
+		}
+		if (artifact_light(obj) && !obj->lamplit) {
+		    begin_burn(obj, FALSE);
+		    if (canseemon(mon))
+			pline("%s brilliantly in %s %s!",
+			    Tobjnam(obj, "glow"), 
+			    s_suffix(mon_nam(mon)), mbodypart(mon,HAND));
 		}
 		obj->owornmask = W_WEP;
 		return 1;
@@ -750,6 +765,7 @@
 }
 
 /* return true if this skill can be advanced */
+/*ARGSUSED*/
 STATIC_OVL boolean
 can_advance(skill, speedy)
 int skill;
@@ -766,6 +782,30 @@
 	    && u.weapon_slots >= slots_required(skill)));
 }
 
+/* return true if this skill could be advanced if more slots were available */
+STATIC_OVL boolean
+could_advance(skill)
+int skill;
+{
+    return !P_RESTRICTED(skill)
+	    && P_SKILL(skill) < P_MAX_SKILL(skill) && (
+	    (P_ADVANCE(skill) >=
+		(unsigned) practice_needed_to_advance(P_SKILL(skill))
+	    && u.skills_advanced < P_SKILL_LIMIT));
+}
+
+/* return true if this skill has reached its maximum and there's been enough
+   practice to become eligible for the next step if that had been possible */
+STATIC_OVL boolean
+peaked_skill(skill)
+int skill;
+{
+    return !P_RESTRICTED(skill)
+	    && P_SKILL(skill) >= P_MAX_SKILL(skill) && (
+	    (P_ADVANCE(skill) >=
+		(unsigned) practice_needed_to_advance(P_SKILL(skill))));
+}
+
 STATIC_OVL void
 skill_advance(skill)
 int skill;
@@ -775,8 +815,8 @@
     u.skill_record[u.skills_advanced++] = skill;
     /* subtly change the advance message to indicate no more advancement */
     You("are now %s skilled in %s.",
-    	P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more",
-    	P_NAME(skill));
+	P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more",
+	P_NAME(skill));
 }
 
 static struct skill_range {
@@ -799,14 +839,15 @@
 int
 enhance_weapon_skill()
 {
-    int pass, i, n, len, longest, to_advance;
-    char buf[BUFSZ], buf2[BUFSZ];
+    int pass, i, n, len, longest,
+	to_advance, eventually_advance, maxxed_cnt;
+    char buf[BUFSZ], sklnambuf[BUFSZ];
+    const char *prefix;
     menu_item *selected;
     anything any;
     winid win;
     boolean speedy = FALSE;
 
-
 #ifdef WIZARD
 	if (wizard && yn("Advance skills without practice?") == 'y')
 	    speedy = TRUE;
@@ -814,15 +855,44 @@
 
 	do {
 	    /* find longest available skill name, count those that can advance */
-	    for (longest = 0, to_advance = 0, i = 0; i < P_NUM_SKILLS; i++) {
-		if (!P_RESTRICTED(i) && (len = strlen(P_NAME(i))) > longest)
+	    to_advance = eventually_advance = maxxed_cnt = 0;
+	    for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) {
+		if (P_RESTRICTED(i)) continue;
+		if ((len = strlen(P_NAME(i))) > longest)
 		    longest = len;
 		if (can_advance(i, speedy)) to_advance++;
+		else if (could_advance(i)) eventually_advance++;
+		else if (peaked_skill(i)) maxxed_cnt++;
 	    }
 
 	    win = create_nhwindow(NHW_MENU);
 	    start_menu(win);
 
+	    /* start with a legend if any entries will be annotated
+	       with "*" or "#" below */
+	    if (eventually_advance > 0 || maxxed_cnt > 0) {
+		any.a_int = 0;
+		if (eventually_advance > 0) {
+		    Sprintf(buf,
+			    "(Skill%s flagged by \"*\" may be enhanced %s.)",
+			    plur(eventually_advance),
+			    (u.ulevel < MAXULEV) ?
+				"when you're more experienced" :
+				"if skill slots become available");
+		    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
+			     buf, MENU_UNSELECTED);
+		}
+		if (maxxed_cnt > 0) {
+		    Sprintf(buf,
+		  "(Skill%s flagged by \"#\" cannot be enhanced any further.)",
+			    plur(maxxed_cnt));
+		    add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
+			     buf, MENU_UNSELECTED);
+		}
+		add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
+			     "", MENU_UNSELECTED);
+	    }
+
 	    /* List the skills, making ones that could be advanced
 	       selectable.  List the miscellaneous skills first.
 	       Possible future enhancement:  list spell skills before
@@ -838,32 +908,55 @@
 
 		if (P_RESTRICTED(i)) continue;
 		/*
-		 * Sigh, this assumes a monospaced font.
+		 * Sigh, this assumes a monospaced font unless
+		 * iflags.menu_tab_sep is set in which case it puts
+		 * tabs between columns.
 		 * The 12 is the longest skill level name.
 		 * The "    " is room for a selection letter and dash, "a - ".
 		 */
-#ifdef WIZARD
-		if (wizard)
-		    Sprintf(buf2, " %s%-*s %-12s %4d(%4d)",
-			    to_advance == 0 || can_advance(i, speedy) ? "" : "    " ,
-			    longest, P_NAME(i),
-			    skill_level_name(i, buf),
-			    P_ADVANCE(i), practice_needed_to_advance(P_SKILL(i)));
+		if (can_advance(i, speedy))
+		    prefix = "";	/* will be preceded by menu choice */
+		else if (could_advance(i))
+		    prefix = "  * ";
+		else if (peaked_skill(i))
+		    prefix = "  # ";
 		else
+		    prefix = (to_advance + eventually_advance +
+				maxxed_cnt > 0) ? "    " : "";
+		(void) skill_level_name(i, sklnambuf);
+#ifdef WIZARD
+		if (wizard) {
+		    if (!iflags.menu_tab_sep)
+			Sprintf(buf, " %s%-*s %-12s %5d(%4d)",
+			    prefix, longest, P_NAME(i), sklnambuf,
+			    P_ADVANCE(i),
+			    practice_needed_to_advance(P_SKILL(i)));
+		    else
+			Sprintf(buf, " %s%s\t%s\t%5d(%4d)",
+			    prefix, P_NAME(i), sklnambuf,
+			    P_ADVANCE(i),
+			    practice_needed_to_advance(P_SKILL(i)));
+		 } else
 #endif
-		    Sprintf(buf2, " %s %-*s [%s]",
-			    to_advance == 0 || can_advance(i, speedy) ? "" : "    ",
-			    longest, P_NAME(i),
-			    skill_level_name(i, buf));
-
+		{
+		    if (!iflags.menu_tab_sep)
+			Sprintf(buf, " %s %-*s [%s]",
+			    prefix, longest, P_NAME(i), sklnambuf);
+		    else
+			Sprintf(buf, " %s%s\t[%s]",
+			    prefix, P_NAME(i), sklnambuf);
+		}
 		any.a_int = can_advance(i, speedy) ? i+1 : 0;
-		add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf2, MENU_UNSELECTED);
+		add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
+			 buf, MENU_UNSELECTED);
 	    }
 
-	    Strcpy(buf, to_advance ? "Pick a skill to advance:" : "Current skills:");
+	    Strcpy(buf, (to_advance > 0) ? "Pick a skill to advance:" :
+					   "Current skills:");
 #ifdef WIZARD
-	    if (wizard && !speedy) Sprintf(eos(buf), "  (%d slot%s available)",
-				u.weapon_slots, plur(u.weapon_slots));
+	    if (wizard && !speedy)
+		Sprintf(eos(buf), "  (%d slot%s available)",
+			u.weapon_slots, plur(u.weapon_slots));
 #endif
 	    end_menu(win, buf);
 	    n = select_menu(win, to_advance ? PICK_ONE : PICK_NONE, &selected);
@@ -991,10 +1084,13 @@
 weapon_hit_bonus(weapon)
 struct obj *weapon;
 {
-    int type, skill, bonus = 0;
+    int type, wep_type, skill, bonus = 0;
     static const char bad_skill[] = "weapon_hit_bonus: bad skill %d";
 
-    type = u.twoweap ? P_TWO_WEAPON_COMBAT : weapon_type(weapon);
+    wep_type = weapon_type(weapon);
+    /* use two weapon skill only if attacking with one of the wielded weapons */
+    type = (u.twoweap && (weapon == uwep || weapon == uswapwep)) ?
+	    P_TWO_WEAPON_COMBAT : wep_type;
     if (type == P_NONE) {
 	bonus = 0;
     } else if (type <= P_LAST_WEAPON) {
@@ -1008,9 +1104,9 @@
 	}
     } else if (type == P_TWO_WEAPON_COMBAT) {
 	skill = P_SKILL(P_TWO_WEAPON_COMBAT);
-	if (P_SKILL(weapon->otyp) < skill) skill = P_SKILL(weapon->otyp);
+	if (P_SKILL(wep_type) < skill) skill = P_SKILL(wep_type);
 	switch (skill) {
-	    default: impossible(bad_skill, P_SKILL(type)); /* fall through */
+	    default: impossible(bad_skill, skill); /* fall through */
 	    case P_ISRESTRICTED:
 	    case P_UNSKILLED:   bonus = -9; break;
 	    case P_BASIC:	bonus = -7; break;
@@ -1018,8 +1114,18 @@
 	    case P_EXPERT:	bonus = -3; break;
 	}
     } else if (type == P_BARE_HANDED_COMBAT) {
-	/* restricted == 0 */
-	bonus = ((P_SKILL(type) + 1) * (martial_bonus() ? 2 : 1)) / 2;
+	/*
+	 *	       b.h.  m.a.
+	 *	unskl:	+1   n/a
+	 *	basic:	+1    +3
+	 *	skild:	+2    +4
+	 *	exprt:	+2    +5
+	 *	mastr:	+3    +6
+	 *	grand:	+3    +7
+	 */
+	bonus = P_SKILL(type);
+	bonus = max(bonus,P_UNSKILLED) - 1;	/* unskilled => 0 */
+	bonus = ((bonus + 2) * (martial_bonus() ? 2 : 1)) / 2;
     }
 
 #ifdef STEED
@@ -1047,9 +1153,12 @@
 weapon_dam_bonus(weapon)
 struct obj *weapon;
 {
-    int type, skill, bonus = 0;
+    int type, wep_type, skill, bonus = 0;
 
-    type = u.twoweap ? P_TWO_WEAPON_COMBAT : weapon_type(weapon);
+    wep_type = weapon_type(weapon);
+    /* use two weapon skill only if attacking with one of the wielded weapons */
+    type = (u.twoweap && (weapon == uwep || weapon == uswapwep)) ?
+	    P_TWO_WEAPON_COMBAT : wep_type;
     if (type == P_NONE) {
 	bonus = 0;
     } else if (type <= P_LAST_WEAPON) {
@@ -1064,7 +1173,7 @@
 	}
     } else if (type == P_TWO_WEAPON_COMBAT) {
 	skill = P_SKILL(P_TWO_WEAPON_COMBAT);
-	if (P_SKILL(weapon->otyp) < skill) skill = P_SKILL(weapon->otyp);
+	if (P_SKILL(wep_type) < skill) skill = P_SKILL(wep_type);
 	switch (skill) {
 	    default:
 	    case P_ISRESTRICTED:
@@ -1074,7 +1183,18 @@
 	    case P_EXPERT:	bonus = 1; break;
 	}
     } else if (type == P_BARE_HANDED_COMBAT) {
-	bonus = (P_SKILL(type) * (martial_bonus() ? 3 : 1)) / 2;
+	/*
+	 *	       b.h.  m.a.
+	 *	unskl:	 0   n/a
+	 *	basic:	+1    +3
+	 *	skild:	+1    +4
+	 *	exprt:	+2    +6
+	 *	mastr:	+2    +7
+	 *	grand:	+3    +9
+	 */
+	bonus = P_SKILL(type);
+	bonus = max(bonus,P_UNSKILLED) - 1;	/* unskilled => 0 */
+	bonus = ((bonus + 1) * (martial_bonus() ? 3 : 1)) / 2;
     }
 
 #ifdef STEED
@@ -1165,6 +1285,22 @@
 	}
 }
 
+void
+setmnotwielded(mon,obj)
+register struct monst *mon;
+register struct obj *obj;
+{
+    if (!obj) return;
+    if (artifact_light(obj) && obj->lamplit) {
+	end_burn(obj, FALSE);
+	if (canseemon(mon))
+	    pline("%s in %s %s %s glowing.", The(xname(obj)),
+		  s_suffix(mon_nam(mon)), mbodypart(mon,HAND),
+		  otense(obj, "stop"));
+    }
+    obj->owornmask &= ~W_WEP;
+}
+
 #endif /* OVLB */
 
 /*weapon.c*/
diff -Naurd ../nethack-3.3.1/src/were.c ./src/were.c
--- ../nethack-3.3.1/src/were.c Thu Oct 28 02:45:33 1999
+++ ./src/were.c Fri Mar 22 14:40:55 2002
@@ -82,7 +82,7 @@
 	/* regenerate by 1/4 of the lost hit points */
 	mon->mhp += (mon->mhpmax - mon->mhp) / 4;
 	newsym(mon->mx,mon->my);
-	mon_break_armor(mon);
+	mon_break_armor(mon, FALSE);
 	possibly_unwield(mon);
 }
 
diff -Naurd ../nethack-3.3.1/src/wield.c ./src/wield.c
--- ../nethack-3.3.1/src/wield.c Sat Jul 15 18:22:32 2000
+++ ./src/wield.c Fri Mar 22 14:40:55 2002
@@ -50,7 +50,7 @@
  */
 
 
-static int FDECL(ready_weapon, (struct obj *));
+STATIC_DCL int FDECL(ready_weapon, (struct obj *));
 
 /* elven weapons vibrate warningly when enchanted beyond a limit */
 #define is_elven_weapon(optr)	((optr)->otyp == ELVEN_ARROW\
@@ -84,11 +84,6 @@
  * 5.  Emptying the slot, by passing a null object.  NEVER pass
  *     zeroobj!
  *
- * Note: setuwep() with a null obj, and uwepgone(), are NOT the same!
- * Sometimes unwielding a weapon can kill you, and lifesaving will then
- * put it back into your hand.  If lifesaving is permitted to do this,
- * use setwuep((struct obj *)0); otherwise use uwepgone().
- *
  * If the item is being moved from another slot, it is the caller's
  * responsibility to handle that.  It's also the caller's responsibility
  * to print the appropriate messages.
@@ -97,8 +92,17 @@
 setuwep(obj)
 register struct obj *obj;
 {
+	struct obj *olduwep = uwep;
+
 	if (obj == uwep) return; /* necessary to not set unweapon */
+	/* This message isn't printed in the caller because it happens
+	 * *whenever* Sunsword is unwielded, from whatever cause.
+	 */
 	setworn(obj, W_WEP);
+	if (uwep == obj && artifact_light(olduwep) && olduwep->lamplit) {
+	    end_burn(olduwep, FALSE);
+	    if (!Blind) pline("%s glowing.", Tobjnam(olduwep, "stop"));
+	}
 	/* Note: Explicitly wielding a pick-axe will not give a "bashing"
 	 * message.  Wielding one via 'a'pplying it will.
 	 * 3.2.2:  Wielding arbitrary objects will give bashing message too.
@@ -106,14 +110,17 @@
 	if (obj) {
 		unweapon = (obj->oclass == WEAPON_CLASS) ?
 				is_launcher(obj) || is_ammo(obj) ||
-				is_missile(obj) || is_pole(obj) :
-			   !is_weptool(obj);
+				is_missile(obj) || (is_pole(obj)
+#ifdef STEED
+				&& !u.usteed
+#endif
+				) : !is_weptool(obj);
 	} else
 		unweapon = TRUE;	/* for "bare hands" message */
 	update_inventory();
 }
 
-static int
+STATIC_OVL int
 ready_weapon(wep)
 struct obj *wep;
 {
@@ -174,6 +181,12 @@
 	    /* KMH -- Talking artifacts are finally implemented */
 	    arti_speak(wep);
 
+	    if (artifact_light(wep) && !wep->lamplit) {
+		begin_burn(wep, FALSE);
+		if (!Blind)
+		    pline("%s to glow brilliantly!", Tobjnam(wep, "begin"));
+	    }
+
 #if 0
 	    /* we'll get back to this someday, but it's not balanced yet */
 	    if (Race_if(PM_ELF) && !wep->oartifact &&
@@ -227,7 +240,7 @@
 int
 dowield()
 {
-	register struct obj *wep;
+	register struct obj *wep, *oldwep;
 	int result;
 
 	/* May we attempt this? */
@@ -267,9 +280,10 @@
 	}
 
 	/* Set your new primary weapon */
-	if (flags.pushweapon && uwep)
-		setuswapwep(uwep);
+	oldwep = uwep;
 	result = ready_weapon(wep);
+	if (flags.pushweapon && oldwep && uwep != oldwep)
+		setuswapwep(oldwep);
 	untwoweapon();
 
 	return (result);
@@ -355,7 +369,7 @@
 	} else if (newquiver == uwep) {
 		/* Prevent accidentally readying the main weapon */
 		pline("%s already being used as a weapon!",
-		      (uwep->quan == 1L) ? "That is" : "They are");
+		      !is_plural(uwep) ? "That is" : "They are");
 		return(0);
 	} else if (newquiver->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL
 #ifdef STEED
@@ -393,23 +407,23 @@
 	struct obj *otmp;
 
 #define NOT_WEAPON(obj) (!is_weptool(obj) && obj->oclass != WEAPON_CLASS)
-	if (Upolyd)
-		You("can only use two weapons in your normal form.");
+	if (!could_twoweap(youmonst.data))
+		You_cant("use two weapons in your current form.");
 	else if (!uwep || !uswapwep)
 		Your("%s%s%s empty.", uwep ? "left " : uswapwep ? "right " : "",
 			body_part(HAND), (!uwep && !uswapwep) ? "s are" : " is");
 	else if (NOT_WEAPON(uwep) || NOT_WEAPON(uswapwep)) {
 		otmp = NOT_WEAPON(uwep) ? uwep : uswapwep;
 		pline("%s %s.", Yname2(otmp),
-		    (otmp->quan) > 1L ? "aren't weapons" : "isn't a weapon");
+		    is_plural(otmp) ? "aren't weapons" : "isn't a weapon");
 	} else if (bimanual(uwep) || bimanual(uswapwep)) {
 		otmp = bimanual(uwep) ? uwep : uswapwep;
 		pline("%s isn't one-handed.", Yname2(otmp));
 	} else if (uarms)
 		You_cant("use two weapons while wearing a shield.");
 	else if (uswapwep->oartifact)
-		pline("%s resists being held second to another weapon!",
-			Yname2(uswapwep));
+		pline("%s %s being held second to another weapon!",
+			Yname2(uswapwep), otense(uswapwep, "resist"));
 	else if (!uarmg && !Stone_resistance && (uswapwep->otyp == CORPSE &&
 		    touch_petrifies(&mons[uswapwep->corpsenm]))) {
 		char kbuf[BUFSZ];
@@ -419,13 +433,14 @@
 		Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname));
 		instapetrify(kbuf);
 	} else if (Glib || uswapwep->cursed) {
+		char str[BUFSZ];
 		struct obj *obj = uswapwep;
 
-		Your("%s from your %s!",  aobjnam(obj, "slip"),
-				makeplural(body_part(HAND)));
+		/* Avoid trashing makeplural's static buffer */
+		Strcpy(str, makeplural(body_part(HAND)));
+		Your("%s from your %s!",  aobjnam(obj, "slip"), str);
 		if (!Glib)
 			obj->bknown = TRUE;
-		setuswapwep((struct obj *) 0);
 		dropx(obj);
 	} else
 		return (TRUE);
@@ -464,6 +479,10 @@
 uwepgone()
 {
 	if (uwep) {
+		if (artifact_light(uwep) && uwep->lamplit) {
+		    end_burn(uwep, FALSE);
+		    if (!Blind) pline("%s glowing.", Tobjnam(uwep, "stop"));
+		}
 		setworn((struct obj *)0, W_WEP);
 		unweapon = TRUE;
 		update_inventory();
@@ -499,31 +518,35 @@
 	return;
 }
 
-/* Maybe rust weapon, or corrode it if acid damage is called for */
+/* Maybe rust object, or corrode it if acid damage is called for */
 void
-erode_weapon(target, acid_dmg)
-struct obj *target;
+erode_obj(target, acid_dmg, fade_scrolls)
+struct obj *target;		/* object (e.g. weapon or armor) to erode */
 boolean acid_dmg;
+boolean fade_scrolls;
 {
 	int erosion;
 	struct monst *victim;
 	boolean vismon;
+	boolean visobj;
 
 	if (!target)
 	    return;
-	if (!carried(target) && !mcarried(target))
-	    panic("erode whose weapon? (%d)", (int)target->where);
-	victim = carried(target) ? &youmonst : target->ocarry;
-	vismon = (victim != &youmonst) && canseemon(victim);
+	victim = carried(target) ? &youmonst :
+	    mcarried(target) ? target->ocarry : (struct monst *)0;
+	vismon = victim && (victim != &youmonst) && canseemon(victim);
+	visobj = !victim && cansee(bhitpos.x, bhitpos.y); /* assume thrown */
 
 	erosion = acid_dmg ? target->oeroded2 : target->oeroded;
 
 	if (target->greased) {
-	    grease_protect(target,(char *)0,FALSE,victim);
+	    grease_protect(target,(char *)0,victim);
 	} else if (target->oclass == SCROLL_CLASS) {
+	    if(fade_scrolls && target->otyp != SCR_BLANK_PAPER
 #ifdef MAIL
-	    if(target->otyp != SCR_MAIL)
+	    && target->otyp != SCR_MAIL
 #endif
+					)
 	    {
 		if (!Blind) {
 		    if (victim == &youmonst)
@@ -531,6 +554,8 @@
 		    else if (vismon)
 			pline("%s's %s.", Monnam(victim),
 			      aobjnam(target, "fade"));
+		    else if (visobj)
+			pline_The("%s.", aobjnam(target, "fade"));
 		}
 		target->otyp = SCR_BLANK_PAPER;
 		target->spe = 0;
@@ -543,6 +568,7 @@
 		else if (vismon)
 		    pline("%s's %s not affected.", Monnam(victim),
 			aobjnam(target, "are"));
+		/* no message if not carried */
 	    }
 	    if (target->oerodeproof) target->rknown = TRUE;
 	} else if (erosion < MAX_ERODE) {
@@ -555,6 +581,11 @@
 		    aobjnam(target, acid_dmg ? "corrode" : "rust"),
 		    erosion+1 == MAX_ERODE ? " completely" :
 		    erosion ? " further" : "");
+	    else if (visobj)
+		pline_The("%s%s!",
+		    aobjnam(target, acid_dmg ? "corrode" : "rust"),
+		    erosion+1 == MAX_ERODE ? " completely" :
+		    erosion ? " further" : "");
 	    if (acid_dmg)
 		target->oeroded2++;
 	    else
@@ -569,6 +600,10 @@
 		    pline("%s's %s completely %s.", Monnam(victim),
 			aobjnam(target, "look"),
 			acid_dmg ? "corroded" : "rusty");
+		else if (visobj)
+		    pline_The("%s completely %s.",
+			aobjnam(target, "look"),
+			acid_dmg ? "corroded" : "rusty");
 	    }
 	}
 }
@@ -615,9 +650,9 @@
 	if(((uwep->spe > 5 && amount >= 0) || (uwep->spe < -5 && amount < 0))
 								&& rn2(3)) {
 	    if (!Blind)
-	    Your("%s %s for a while and then evaporate%s.",
+	    Your("%s %s for a while and then %s.",
 		 aobjnam(uwep, "violently glow"), color,
-		 uwep->quan == 1L ? "s" : "");
+		 otense(uwep, "evaporate"));
 	    else
 		Your("%s.", aobjnam(uwep, "evaporate"));
 
@@ -674,7 +709,7 @@
 
 	savewornmask = obj->owornmask;
 	Your("%s %s welded to your %s!",
-		xname(obj), (obj->quan == 1L) ? "is" : "are",
+		xname(obj), otense(obj, "are"),
 		bimanual(obj) ? (const char *)makeplural(body_part(HAND))
 				: body_part(HAND));
 	obj->owornmask = savewornmask;
diff -Naurd ../nethack-3.3.1/src/windows.c ./src/windows.c
--- ../nethack-3.3.1/src/windows.c Mon May 8 21:56:52 2000
+++ ./src/windows.c Fri Mar 22 14:40:55 2002
@@ -126,4 +132,15 @@
     return 0;
 }
 
+/*ARGSUSED*/
+void
+genl_preference_update(pref)
+const char *pref;
+{
+	/* window ports are expected to provide
+	   their own preference update routine
+	   for the preference capabilities that
+	   they support.
+	   Just return in this genl one. */
+}
 /*windows.c*/
diff -Naurd ../nethack-3.3.1/src/wizard.c ./src/wizard.c
--- ../nethack-3.3.1/src/wizard.c Sun Jul 16 02:51:14 2000
+++ ./src/wizard.c Fri Mar 22 14:40:55 2002
@@ -64,11 +64,11 @@
 		if(ttmp->ttyp == MAGIC_PORTAL) {
 		    int du = distu(ttmp->tx, ttmp->ty);
 		    if (du <= 9)
-			pline("%s feels hot!", The(xname(amu)));
+			pline("%s hot!", Tobjnam(amu, "feel"));
 		    else if (du <= 64)
-			pline("%s feels very warm.", The(xname(amu)));
+			pline("%s very warm.", Tobjnam(amu, "feel"));
 		    else if (du <= 144)
-			pline("%s feels warm.", The(xname(amu)));
+			pline("%s warm.", Tobjnam(amu, "feel"));
 		    /* else, the amulet feels normal */
 		    break;
 		}
@@ -240,7 +240,12 @@
 {
 	long strat, dstrat;
 
-	if(!is_covetous(mtmp->data)) return(STRAT_NONE);
+	if (!is_covetous(mtmp->data) ||
+		/* perhaps a shopkeeper has been polymorphed into a master
+		   lich; we don't want it teleporting to the stairs to heal
+		   because that will leave its shop untended */
+		(mtmp->isshk && inhishop(mtmp)))
+	    return STRAT_NONE;
 
 	switch((mtmp->mhp*3)/mtmp->mhpmax) {	/* 0-3 */
 
@@ -336,28 +341,32 @@
 		    return(0);
 		}
 		if(where == STRAT_GROUND) {
-		  if(!MON_AT(tx, ty) || (mtmp->mx == tx && mtmp->my == ty)) {
-		    /* teleport to it and pick it up */
-		    rloc_to(mtmp, tx, ty);	/* clean old pos */
+		    if(!MON_AT(tx, ty) || (mtmp->mx == tx && mtmp->my == ty)) {
+			/* teleport to it and pick it up */
+			rloc_to(mtmp, tx, ty);	/* clean old pos */
 
-		    if ((otmp = on_ground(which_arti(targ))) != 0) {
-			if (cansee(mtmp->mx, mtmp->my))
-			    pline("%s picks up %s.",
-				  Monnam(mtmp),
-				  (distu(mtmp->my, mtmp->my) <= 5) ?
-				    doname(otmp) : distant_name(otmp, doname));
-			obj_extract_self(otmp);
-			(void) mpickobj(mtmp, otmp);
-			return(1);
-		    } else return(0);
-		  }
+			if ((otmp = on_ground(which_arti(targ))) != 0) {
+			    if (cansee(mtmp->mx, mtmp->my))
+				pline("%s picks up %s.",
+				    Monnam(mtmp),
+				    (distu(mtmp->my, mtmp->my) <= 5) ?
+				     doname(otmp) : distant_name(otmp, doname));
+			    obj_extract_self(otmp);
+			    (void) mpickobj(mtmp, otmp);
+			    return(1);
+			} else return(0);
+		    } else {
+			/* a monster is standing on it - cause some trouble */
+			if (!rn2(5)) mnexto(mtmp);
+			return(0);
+		    }
 	        } else { /* a monster has it - 'port beside it. */
 		    (void) mnearto(mtmp, tx, ty, TRUE);
 		    return(0);
 		}
 	    }
 	}
-	/* NOTREACHED */
+	/*NOTREACHED*/
 	return(0);
 }
 
@@ -405,33 +414,44 @@
 
 /* create some nasty monsters, aligned or neutral with the caster */
 /* a null caster defaults to a chaotic caster (e.g. the wizard) */
-void
+int
 nasty(mcast)
 	struct monst *mcast;
 {
     register struct monst	*mtmp;
     register int	i, j, tmp;
     int castalign = (mcast ? mcast->data->maligntyp : -1);
+    coord bypos;
+    int count=0;
 
-    if(!rn2(10) && Inhell) msummon(&mons[PM_WIZARD_OF_YENDOR]);
-    else {
+    if(!rn2(10) && Inhell) {
+	msummon(&mons[PM_WIZARD_OF_YENDOR]);
+	count++;
+    } else {
 	tmp = (u.ulevel > 3) ? u.ulevel/3 : 1; /* just in case -- rph */
-
+	/* if we don't have a casting monster, the nasties appear around you */
+	bypos.x = u.ux;
+	bypos.y = u.uy;
 	for(i = rnd(tmp); i > 0; --i)
 	    for(j=0; j<20; j++) {
+		if (mcast &&
+			!enexto(&bypos, mcast->mux, mcast->muy, mcast->data))
+		    continue;
 		if ((mtmp = makemon(&mons[pick_nasty()],
-				    u.ux, u.uy, NO_MM_FLAGS)) != 0) {
+				    bypos.x, bypos.y, NO_MM_FLAGS)) != 0) {
 		    mtmp->msleeping = mtmp->mpeaceful = mtmp->mtame = 0;
 		    set_malign(mtmp);
 		} else /* GENOD? */
 		    mtmp = makemon((struct permonst *)0,
-					u.ux, u.uy, NO_MM_FLAGS);
+					bypos.x, bypos.y, NO_MM_FLAGS);
 		if(mtmp && (mtmp->data->maligntyp == 0 ||
-		            sgn(mtmp->data->maligntyp) == sgn(castalign)) )
+		            sgn(mtmp->data->maligntyp) == sgn(castalign)) ) {
+		    count++;
 		    break;
+		}
 	    }
     }
-    return;
+    return count;
 }
 
 /*	Let's resurrect the wizard, for some unexpected fun.	*/
@@ -501,7 +521,7 @@
 			break;
 	    case 3:	aggravate();
 			break;
-	    case 4:	nasty((struct monst *)0);
+	    case 4:	(void)nasty((struct monst *)0);
 			break;
 	    case 5:	resurrect();
 			break;
diff -Naurd ../nethack-3.3.1/src/worn.c ./src/worn.c
--- ../nethack-3.3.1/src/worn.c Sat Aug 5 01:22:47 2000
+++ ./src/worn.c Fri Mar 22 14:40:55 2002
@@ -6,6 +6,7 @@
 
 STATIC_DCL void FDECL(m_lose_armor, (struct monst *,struct obj *));
 STATIC_DCL void FDECL(m_dowear_type, (struct monst *,long,BOOLEAN_P));
+STATIC_DCL int FDECL(extra_pref, (struct monst *, struct obj *));
 
 const struct worn {
 	long w_mask;
@@ -114,16 +115,16 @@
 	if (!obj) return;
 	if (obj == uwep || obj == uswapwep) u.twoweap = 0;
 	for(wp = worn; wp->w_mask; wp++)
-		if(obj == *(wp->w_obj)) {
-			*(wp->w_obj) = 0;
-			p = objects[obj->otyp].oc_oprop;
-			u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask;
-			obj->owornmask &= ~wp->w_mask;
-			if (obj->oartifact)
-			    set_artifact_intrinsic(obj, 0, wp->w_mask);
-			if ((p = w_blocks(obj,wp->w_mask)) != 0)
-			    u.uprops[p].blocked &= ~wp->w_mask;
-		}
+	    if(obj == *(wp->w_obj)) {
+		*(wp->w_obj) = 0;
+		p = objects[obj->otyp].oc_oprop;
+		u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask;
+		obj->owornmask &= ~wp->w_mask;
+		if (obj->oartifact)
+		    set_artifact_intrinsic(obj, 0, wp->w_mask);
+		if ((p = w_blocks(obj,wp->w_mask)) != 0)
+		    u.uprops[p].blocked &= ~wp->w_mask;
+	    }
 	update_inventory();
 }
 
@@ -140,15 +141,19 @@
 }
 
 void
-mon_adjust_speed(mon, adjust)
+mon_adjust_speed(mon, adjust, obj)
 struct monst *mon;
 int adjust;	/* positive => increase speed, negative => decrease */
+struct obj *obj;	/* item to make known if effect can be seen */
 {
     struct obj *otmp;
+    boolean give_msg = !in_mklev;
+    unsigned int oldspeed = mon->mspeed;
 
     switch (adjust) {
      case  2:
 	mon->permspeed = MFAST;
+	give_msg = FALSE;	/* special case monster creation */
 	break;
      case  1:
 	if (mon->permspeed == MSLOW) mon->permspeed = 0;
@@ -162,6 +167,7 @@
 	break;
      case -2:
 	mon->permspeed = MSLOW;
+	give_msg = FALSE;	/* (not currently used) */
 	break;
     }
 
@@ -172,6 +178,23 @@
 	mon->mspeed = MFAST;
     else
 	mon->mspeed = mon->permspeed;
+
+    if (give_msg && mon->mspeed != oldspeed && canseemon(mon)) {
+	/* fast to slow (skipping intermediate state) or vice versa */
+	const char *howmuch = (mon->mspeed + oldspeed == MFAST + MSLOW) ?
+				"much " : "";
+
+	if (adjust > 0 || mon->mspeed == MFAST)
+	    pline("%s is suddenly moving %sfaster.", Monnam(mon), howmuch);
+	else
+	    pline("%s seems to be moving %sslower.", Monnam(mon), howmuch);
+
+	/* might discover an object if we see the speed change happen, but
+	   avoid making possibly forgotten book known when casting its spell */
+	if (obj != 0 && obj->dknown &&
+		objects[obj->otyp].oc_class != SPBOOK_CLASS)
+	    makeknown(obj->otyp);
+    }
 }
 
 /* armor put on or taken off; might be magical variety */
@@ -195,7 +218,7 @@
 	    mon->minvis = !mon->invis_blkd;
 	    break;
 	 case FAST:
-	    mon_adjust_speed(mon, 0);
+	    mon_adjust_speed(mon, 0, obj);
 	    break;
 	/* properties handled elsewhere */
 	 case ANTIMAGIC:
@@ -230,7 +253,7 @@
 	    mon->minvis = mon->perminvis;
 	    break;
 	 case FAST:
-	    mon_adjust_speed(mon, 0);
+	    mon_adjust_speed(mon, 0, obj);
 	    break;
 	 case FIRE_RES:
 	 case COLD_RES:
@@ -404,7 +427,8 @@
 	     * it would forget spe and once again think the object is better
 	     * than what it already has.
 	     */
-	    if (best && (ARM_BONUS(best) >= ARM_BONUS(obj))) continue;
+	    if (best && (ARM_BONUS(best) + extra_pref(mon,best) >= ARM_BONUS(obj) + extra_pref(mon,obj)))
+		continue;
 	    best = obj;
 	}
 outer_break:
@@ -476,18 +500,65 @@
 }
 
 void
-mon_break_armor(mon)
+clear_bypasses()
+{
+	struct obj *otmp, *nobj;
+
+	for (otmp = fobj; otmp; otmp = nobj) {
+	    nobj = otmp->nobj;
+	    if (otmp->bypass) {
+		otmp->bypass = 0;
+#if 0
+		/*  setting otmp->bypass changes mergability.
+		 *  If monster can ever drop anything that
+                 *  can and should merge, this code block
+		 *  should be enabled.
+		 */
+		{
+		    struct obj *obj;
+		    xchar ox, oy;
+		    (void) get_obj_location(otmp, &ox, &oy, 0);
+		    obj_extract_self(otmp);
+		    obj = merge_choice(fobj, otmp);
+		    /* If it can't merge, then place it */
+		    if (!obj || (obj && !merged(&obj, &otmp)))
+		        place_object(otmp, ox, oy);
+		    newsym(ox, oy);
+		}
+#endif
+	    }
+	}
+	flags.bypasses = FALSE;
+}
+
+void
+bypass_obj(obj)
+struct obj *obj;
+{
+	obj->bypass = 1;
+	flags.bypasses = TRUE;
+}
+
+void
+mon_break_armor(mon, polyspot)
 struct monst *mon;
+boolean polyspot;
 {
 	register struct obj *otmp;
 	struct permonst *mdat = mon->data;
 	boolean vis = cansee(mon->mx, mon->my);
-	const char *pronoun = him[pronoun_gender(mon)],
-			*ppronoun = his[pronoun_gender(mon)];
+	const char *pronoun = mhim(mon),
+			*ppronoun = mhis(mon);
 
 	if (breakarm(mdat)) {
 	    if ((otmp = which_armor(mon, W_ARM)) != 0) {
-		if (vis)
+		if ((Is_dragon_scales(otmp) &&
+			mdat == Dragon_scales_to_pm(otmp)) ||
+		    (Is_dragon_mail(otmp) && mdat == Dragon_mail_to_pm(otmp)))
+		    ;	/* no message here;
+			   "the dragon merges with his scaly armor" is odd
+			   and the monster's previous form is already gone */
+		else if (vis)
 		    pline("%s breaks out of %s armor!", Monnam(mon), ppronoun);
 		else
 		    You_hear("a cracking sound.");
@@ -496,11 +567,14 @@
 	    if ((otmp = which_armor(mon, W_ARMC)) != 0) {
 		if (otmp->oartifact) {
 		    if (vis)
-			pline("%s cloak falls off!", s_suffix(Monnam(mon)));
+			pline("%s %s falls off!", s_suffix(Monnam(mon)),
+				cloak_simple_name(otmp));
+		    if (polyspot) bypass_obj(otmp);
 		    m_lose_armor(mon, otmp);
 		} else {
 		    if (vis)
-			pline("%s cloak tears apart!", s_suffix(Monnam(mon)));
+			pline("%s %s tears apart!", s_suffix(Monnam(mon)),
+				cloak_simple_name(otmp));
 		    else
 			You_hear("a ripping sound.");
 		    m_useup(mon, otmp);
@@ -522,17 +596,19 @@
 				 s_suffix(Monnam(mon)), pronoun);
 		else
 		    You_hear("a thud.");
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 	    if ((otmp = which_armor(mon, W_ARMC)) != 0) {
 		if (vis) {
 		    if (is_whirly(mon->data))
-			pline("%s cloak falls, unsupported!",
-				     s_suffix(Monnam(mon)));
+			pline("%s %s falls, unsupported!",
+				     s_suffix(Monnam(mon)), cloak_simple_name(otmp));
 		    else
-			pline("%s shrinks out of %s cloak!", Monnam(mon),
-								ppronoun);
+			pline("%s shrinks out of %s %s!", Monnam(mon),
+						ppronoun, cloak_simple_name(otmp));
 		}
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 #ifdef TOURIST
@@ -545,6 +621,7 @@
 			pline("%s becomes much too small for %s shirt!",
 					Monnam(mon), ppronoun);
 		}
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 #endif
@@ -555,6 +632,7 @@
 		    pline("%s drops %s gloves%s!", Monnam(mon), ppronoun,
 					MON_WEP(mon) ? " and weapon" : "");
 		possibly_unwield(mon);
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 	    if ((otmp = which_armor(mon, W_ARMS)) != 0) {
@@ -563,6 +641,7 @@
 								ppronoun);
 		else
 		    You_hear("a clank.");
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 	    if ((otmp = which_armor(mon, W_ARMH)) != 0) {
@@ -571,6 +650,7 @@
 			  s_suffix(Monnam(mon)), surface(mon->mx, mon->my));
 		else
 		    You_hear("a clank.");
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 	}
@@ -585,12 +665,14 @@
 			s_suffix(Monnam(mon)),
 			verysmall(mdat) ? "slide" : "are pushed", ppronoun);
 		}
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 	    }
 	}
 #ifdef STEED
 	if (!can_saddle(mon)) {
 	    if ((otmp = which_armor(mon, W_SADDLE)) != 0) {
+		if (polyspot) bypass_obj(otmp);
 		m_lose_armor(mon, otmp);
 		if (vis)
 		    pline("%s saddle falls off.", s_suffix(Monnam(mon)));
@@ -615,4 +697,18 @@
 	return;
 }
 
+/* bias a monster's preferences towards armor that has special benefits. */
+/* currently only does speed boots, but might be expanded if monsters get to
+   use more armor abilities */
+static int
+extra_pref(mon, obj)
+struct monst *mon;
+struct obj *obj;
+{
+    if (obj) {
+	if (obj->otyp == SPEED_BOOTS && mon->permspeed != MFAST)
+	    return 20;
+    }
+    return 0;
+}
 /*worn.c*/
diff -Naurd ../nethack-3.3.1/src/write.c ./src/write.c
--- ../nethack-3.3.1/src/write.c Sat Apr 22 03:31:58 2000
+++ ./src/write.c Fri Mar 22 14:40:55 2002
@@ -85,8 +85,8 @@
 	    return 0;
 	} else if (Glib) {
 	    dropx(pen);
-	    pline("%s slips from your %s.", The(xname(pen)),
-			makeplural(body_part(FINGER)));
+	    pline("%s from your %s.",
+		  Tobjnam(pen, "slip"), makeplural(body_part(FINGER)));
 	    return 1;
 	}
 
@@ -178,17 +178,18 @@
 	curseval = bcsign(pen) + bcsign(paper);
 	exercise(A_WIS, TRUE);
 	/* dry out marker */
-	if(pen->spe < actualcost)  {
+	if (pen->spe < actualcost) {
+		pen->spe = 0;
 		Your("marker dries out!");
 		/* scrolls disappear, spellbooks don't */
-		if (paper->oclass == SPBOOK_CLASS)
+		if (paper->oclass == SPBOOK_CLASS) {
 			pline_The(
 		       "spellbook is left unfinished and your writing fades.");
-		else {
+			update_inventory();	/* pen charges */
+		} else {
 			pline_The("scroll is now useless and disappears!");
 			useup(paper);
 		}
-		pen->spe = 0;
 		obfree(new_obj, (struct obj *) 0);
 		return(1);
 	}
@@ -200,10 +201,11 @@
 	   (rnl(Role_if(PM_WIZARD) ? 3 : 15))) {
 		You("%s to write that!", by_descr ? "fail" : "don't know how");
 		/* scrolls disappear, spellbooks don't */
-		if (paper->oclass == SPBOOK_CLASS)
+		if (paper->oclass == SPBOOK_CLASS) {
 			You(
        "write in your best handwriting:  \"My Diary\", but it quickly fades.");
-		else {
+			update_inventory();	/* pen charges */
+		} else {
 			if (by_descr) {
 			    Strcpy(namebuf, OBJ_DESCR(objects[new_obj->otyp]));
 			    wipeout_text(namebuf, (6+MAXULEV - u.ulevel)/6, 0);
diff -Naurd ../nethack-3.3.1/src/zap.c ./src/zap.c
--- ../nethack-3.3.1/src/zap.c Sat Aug 5 01:23:29 2000
+++ ./src/zap.c Fri Mar 22 14:41:09 2002
@@ -28,7 +28,7 @@
 STATIC_DCL int FDECL(zhitm, (struct monst *,int,int,struct obj **));
 STATIC_DCL void FDECL(zhitu, (int,int,const char *,XCHAR_P,XCHAR_P));
 STATIC_DCL void FDECL(revive_egg, (struct obj *));
-STATIC_DCL boolean FDECL(hits_bars, (int));
+STATIC_DCL boolean FDECL(hits_bars, (struct obj *));
 #ifdef STEED
 STATIC_DCL boolean FDECL(zap_steed, (struct obj *));
 #endif
@@ -139,7 +139,8 @@
 	case WAN_SLOW_MONSTER:
 	case SPE_SLOW_MONSTER:
 		if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
-			mon_adjust_speed(mtmp, -1);
+			mon_adjust_speed(mtmp, -1, otmp);
+			m_dowear(mtmp, FALSE); /* might want speed boots */
 			if (u.uswallow && (mtmp == u.ustuck) &&
 			    is_whirly(mtmp->data)) {
 				You("disrupt %s!", mon_nam(mtmp));
@@ -149,8 +150,10 @@
 		}
 		break;
 	case WAN_SPEED_MONSTER:
-		if (!resist(mtmp, otmp->oclass, 0, NOTELL))
-			mon_adjust_speed(mtmp, 1);
+		if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
+			mon_adjust_speed(mtmp, 1, otmp);
+			m_dowear(mtmp, FALSE); /* might want speed boots */
+		}
 		break;
 	case WAN_UNDEAD_TURNING:
 	case SPE_TURN_UNDEAD:
@@ -163,8 +166,10 @@
 			if(dbldam) dmg *= 2;
 			if (otyp == SPE_TURN_UNDEAD)
 				dmg += spell_damage_bonus();
-			if(!resist(mtmp, otmp->oclass, dmg, NOTELL))
-				mtmp->mflee = TRUE;
+			flags.bypasses = TRUE;	/* for make_corpse() */
+			if (!resist(mtmp, otmp->oclass, dmg, NOTELL)) {
+			    if (mtmp->mhp > 0) monflee(mtmp, 0, FALSE, TRUE);
+			}
 		}
 		break;
 	case WAN_POLYMORPH:
@@ -175,17 +180,22 @@
 		       it guard against involuntary polymorph attacks too... */
 		    shieldeff(mtmp->mx, mtmp->my);
 		} else if (!resist(mtmp, otmp->oclass, 0, NOTELL)) {
-		    if (!rn2(25)) {
+		    /* natural shapechangers aren't affected by system shock
+		       (unless protection from shapechangers is interfering
+		       with their metabolism...) */
+		    if (mtmp->cham == CHAM_ORDINARY && !rn2(25)) {
 			if (canseemon(mtmp)) {
 			    pline("%s shudders!", Monnam(mtmp));
 			    makeknown(otyp);
 			}
+			/* flags.bypasses = TRUE; ## for make_corpse() */
 			/* no corpse after system shock */
 			xkilled(mtmp, 3);
-		    }
-		    else if (newcham(mtmp, (struct permonst *)0) )
+		    } else if (newcham(mtmp, (struct permonst *)0,
+				       (otyp != POT_POLYMORPH))) {
 			if (!Hallucination && canspotmon(mtmp))
 			    makeknown(otyp);
+		    }
 		}
 		break;
 	case WAN_CANCELLATION:
@@ -250,6 +260,10 @@
 		mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4);
 		if (mtmp->mhp > mtmp->mhpmax)
 		    mtmp->mhp = mtmp->mhpmax;
+		if (mtmp->mblinded) {
+		    mtmp->mblinded = 0;
+		    mtmp->mcansee = 1;
+		}
 		if (canseemon(mtmp))
 		    pline("%s looks%s better.", Monnam(mtmp),
 			  otyp == SPE_EXTRA_HEALING ? " much" : "" );
@@ -280,7 +294,7 @@
 		if (monsndx(mtmp->data) == PM_STONE_GOLEM) {
 		    char *name = Monnam(mtmp);
 		    /* turn into flesh golem */
-		    if (newcham(mtmp, &mons[PM_FLESH_GOLEM])) {
+		    if (newcham(mtmp, &mons[PM_FLESH_GOLEM], FALSE)) {
 			if (canseemon(mtmp))
 			    pline("%s turns to flesh!", name);
 		    } else {
@@ -302,12 +316,12 @@
 				mtmp->mhp > 0) {
 		    mtmp->mhp -= dmg;
 		    mtmp->mhpmax -= dmg;
-		    if (mtmp->mhp <= 0 || mtmp->mhpmax <= 0 || mtmp->m_lev <= 0)
-		    	xkilled(mtmp, 1);
+		    if (mtmp->mhp <= 0 || mtmp->mhpmax <= 0 || mtmp->m_lev < 1)
+			xkilled(mtmp, 1);
 		    else {
-		    	mtmp->m_lev--;
-		    	if (canseemon(mtmp))
-		    	    pline("%s suddenly seems weaker!", Monnam(mtmp));
+			mtmp->m_lev--;
+			if (canseemon(mtmp))
+			    pline("%s suddenly seems weaker!", Monnam(mtmp));
 		    }
 		}
 		break;
@@ -347,7 +361,7 @@
 	if (mtmp->minvent || mtmp->mgold) {
 	    for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
 		otmp->dknown = 1;	/* treat as "seen" */
-	    (void) display_minventory(mtmp, MINV_ALL);
+	    (void) display_minventory(mtmp, MINV_ALL, (char *)0);
 	} else {
 	    pline("%s is not carrying anything.", noit_Monnam(mtmp));
 	}
@@ -442,7 +456,7 @@
 		if (mtmp2->mhpmax <= 0 && !is_rider(mtmp2->data))
 			return (struct monst *)0;
 		mtmp = makemon(mtmp2->data,
-				cc->x, cc->y, NO_MINVENT|MM_NOWAIT);
+				cc->x, cc->y, NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH);
 		if (!mtmp) return mtmp;
 
 		/* heal the monster */
@@ -451,7 +465,11 @@
 		mtmp2->mhp = mtmp2->mhpmax;
 		/* Get these ones from mtmp */
 		mtmp2->minvent = mtmp->minvent; /*redundant*/
-		mtmp2->m_id = mtmp->m_id;
+		/* monster ID is available if the monster died in the current
+		   game, but should be zero if the corpse was in a bones level
+		   (we cleared it when loading bones) */
+		if (!mtmp2->m_id)
+		    mtmp2->m_id = mtmp->m_id;
 		mtmp2->mx   = mtmp->mx;
 		mtmp2->my   = mtmp->my;
 		mtmp2->mux  = mtmp->mux;
@@ -468,6 +486,7 @@
 		mtmp2->mlstmv = mtmp->mlstmv;
 		mtmp2->m_ap_type = mtmp->m_ap_type;
 		/* set these ones explicitly */
+		mtmp2->mavenge = 0;
 		mtmp2->meating = 0;
 		mtmp2->mleashed = 0;
 		mtmp2->mtrapped = 0;
@@ -485,6 +504,38 @@
 }
 
 /*
+ * get_container_location() returns the following information
+ * about the outermost container:
+ * loc argument gets set to: 
+ *	OBJ_INVENT	if in hero's inventory; return 0.
+ *	OBJ_FLOOR	if on the floor; return 0.
+ *	OBJ_BURIED	if buried; return 0.
+ *	OBJ_MINVENT	if in monster's inventory; return monster.
+ * container_nesting is updated with the nesting depth of the containers
+ * if applicable.
+ */
+struct monst *
+get_container_location(obj, loc, container_nesting)
+struct obj *obj;
+int *loc;
+int *container_nesting;
+{
+	if (!obj || !loc)
+		return 0;
+
+	if (container_nesting) *container_nesting = 0;
+	while (obj && obj->where == OBJ_CONTAINED) {
+		if (container_nesting) *container_nesting += 1;
+		obj = obj->ocontainer;
+	}
+	if (obj) {
+	    *loc = obj->where;	/* outermost container's location */
+	    if (obj->where == OBJ_MINVENT) return obj->ocarry;
+	}
+	return (struct monst *)0;
+}
+
+/*
  * Attempt to revive the given corpse, return the revived monster if
  * successful.  Note: this does NOT use up the corpse if it fails.
  */
@@ -493,16 +544,57 @@
 register struct obj *obj;
 {
 	register struct monst *mtmp = (struct monst *)0;
+	struct obj *container = (struct obj *)0;
+	int container_nesting = 0;
 	schar savetame = 0;
 	boolean recorporealization = FALSE;
-
+	boolean in_container = FALSE;
 	if(obj->otyp == CORPSE) {
 		int montype = obj->corpsenm;
 		xchar x, y;
 
-		/* only for invent, minvent, or floor */
-		if (!get_obj_location(obj, &x, &y, 0))
-		    return (struct monst *) 0;
+		if (obj->where == OBJ_CONTAINED) {
+			/* deal with corpses in [possibly nested] containers */
+			struct monst *carrier;
+			int holder = 0;
+
+			container = obj->ocontainer;
+			carrier = get_container_location(container, &holder,
+							&container_nesting);
+			switch(holder) {
+			    case OBJ_MINVENT:
+				x = carrier->mx; y = carrier->my;
+				in_container = TRUE;
+				break;
+			    case OBJ_INVENT:
+				x = u.ux; y = u.uy;
+				in_container = TRUE;
+				break;
+			    case OBJ_FLOOR:
+				if (!get_obj_location(obj, &x, &y, CONTAINED_TOO))
+					return (struct monst *) 0;
+				in_container = TRUE;
+				break;
+			    default:
+			    	return (struct monst *)0;
+			}
+		} else {
+			/* only for invent, minvent, or floor */
+			if (!get_obj_location(obj, &x, &y, 0))
+			    return (struct monst *) 0;
+		}
+		if (in_container) {
+			/* Rules for revival from containers:
+			   - the container cannot be locked
+			   - the container cannot be heavily nested (>2 is arbitrary)
+			   - the container cannot be a statue or bag of holding
+			     (except in very rare cases for the latter)
+			*/
+			if (!x || !y || container->olocked || container_nesting > 2 ||
+			    container->otyp == STATUE ||
+			    (container->otyp == BAG_OF_HOLDING && rn2(40)))
+				return (struct monst *)0;
+		}
 
 		if (MON_AT(x,y)) {
 		    coord new_xy;
@@ -517,7 +609,7 @@
 				       NO_MINVENT|MM_NOWAIT);
 			if (mtmp) {
 				mtmp->mhp = mtmp->mhpmax = 100;
-				mon_adjust_speed(mtmp, 2);	/* MFAST */
+				mon_adjust_speed(mtmp, 2, (struct obj *)0); /* MFAST */
 			}
 		} else {
 		    if (obj->oxlth && (obj->oattached == OATTACHED_MONST)) {
@@ -528,7 +620,7 @@
 				wary_dog(mtmp, TRUE);
 		    } else
  		            mtmp = makemon(&mons[montype], x, y,
-				       NO_MINVENT|MM_NOWAIT);
+				       NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH);
 		    if (mtmp) {
 			if (obj->oxlth && (obj->oattached == OATTACHED_M_ID)) {
 			    unsigned m_id;
@@ -554,6 +646,10 @@
 			/* Monster retains its name */
 			if (obj->onamelth)
 			    mtmp = christen_monst(mtmp, ONAME(obj));
+			/* flag the quest leader as alive. */
+			if (mtmp->data->msound == MS_LEADER || mtmp->m_id ==
+				quest_status.leader_m_id)
+			    quest_status.leader_is_dead = FALSE;
 		    }
 		}
 		if (mtmp) {
@@ -584,13 +680,17 @@
 				x = obj->ox,  y = obj->oy;
 				/* not useupf(), which charges */
 				if (obj->quan > 1L)
-				    (void) splitobj(obj, 1L);
+				    obj = splitobj(obj, 1L);
 				delobj(obj);
 				newsym(x, y);
 				break;
 			    case OBJ_MINVENT:
 				m_useup(obj->ocarry, obj);
 				break;
+			    case OBJ_CONTAINED:
+				obj_extract_self(obj);
+				obfree(obj, (struct obj *) 0);
+				break;
 			    default:
 				panic("revive");
 			}
@@ -792,7 +892,6 @@
 {
 	boolean u_ring;
 
-
 	/* Is this a charged/enchanted object? */
 	if (!obj || (!objects[obj->otyp].oc_charged &&
 			obj->oclass != WEAPON_CLASS &&
@@ -852,6 +951,7 @@
 	    flags.botl = 1;
 	    break;
 	}
+	if (carried(obj)) update_inventory();
 	return (TRUE);
 }
 
@@ -1018,7 +1118,7 @@
 	mtmp = makemon(mdat, obj->ox, obj->oy, NO_MM_FLAGS);
 	polyuse(obj, okind, (int)mons[pm_index].cwt);
 
-	if(!Blind && mtmp) {
+	if(mtmp && cansee(mtmp->mx, mtmp->my)) {
 	    pline("Some %sobjects meld, and %s arises from the pile!",
 		  material, a_monnam(mtmp));
 	}
@@ -1048,9 +1148,9 @@
 	/* if quan > 1 then some will survive intact */
 	if (obj->quan > 1L) {
 	    if (obj->quan > LARGEST_INT)
-		(void) splitobj(obj, (long)rnd(30000));
+		obj = splitobj(obj, (long)rnd(30000));
 	    else
-		(void) splitobj(obj, (long)rnd((int)obj->quan - 1));
+		obj = splitobj(obj, (long)rnd((int)obj->quan - 1));
 	}
 
 	/* appropriately add damage to bill */
@@ -1072,11 +1172,12 @@
  * "polymorph" case).  If ID is set to a specific object, inhibit fusing
  * n objects into 1.  This could have been added as a flag, but currently
  * it is tied to not being the standard polymorph case. The new polymorphed
- * object replaces obj in its link chains.
+ * object replaces obj in its link chains.  Return value is a pointer to
+ * the new object.
  *
  * This should be safe to call for an object anywhere.
  */
-void
+struct obj *
 poly_obj(obj, id)
 	struct obj *obj;
 	int id;
@@ -1219,7 +1320,7 @@
 	    while (otmp->otyp == SPE_POLYMORPH)
 		otmp->otyp = rnd_class(SPE_DIG, SPE_BLANK_PAPER);
 	    /* reduce spellbook abuse */
-	    otmp->spestudied += 1;
+	    otmp->spestudied = obj->spestudied + 1;
 	    break;
 
 	case GEM_CLASS:
@@ -1295,7 +1396,7 @@
 
 	    if ((!obj->no_charge ||
 		 (Has_contents(obj) &&
-		    (contained_cost(obj, shkp, 0L, FALSE) != 0L)))
+		    (contained_cost(obj, shkp, 0L, FALSE, FALSE) != 0L)))
 	       && inhishop(shkp)) {
 		if(shkp->mpeaceful) {
 		    if(*u.ushops && *in_rooms(u.ux, u.uy, 0) ==
@@ -1310,7 +1411,7 @@
 	    }
 	}
 	delobj(obj);
-	return;
+	return otmp;
 }
 
 /*
@@ -1323,8 +1424,40 @@
 {
 	int res = 1;	/* affected object by default */
 
+	if (obj->bypass) {
+		/* The bypass bit is currently only used as follows:
+		 *
+		 * POLYMORPH - When a monster being polymorphed drops something
+		 *             from its inventory as a result of the change.
+		 *             If the items fall to the floor, they are not
+		 *             subject to direct subsequent polymorphing
+		 *             themselves on that same zap. This makes it
+		 *             consistent with items that remain in the
+		 *             monster's inventory. They are not polymorphed
+		 *             either.
+		 * UNDEAD_TURNING - When an undead creature gets killed via
+		 *	       undead turning, prevent its corpse from being
+		 *	       immediately revived by the same effect.
+		 *
+		 * The bypass bit on all objects is reset each turn, whenever
+		 * flags.bypasses is set.
+		 *
+		 * We check the obj->bypass bit above AND flags.bypasses
+		 * as a safeguard against any stray occurrence left in an obj
+		 * struct someplace, although that should never happen.
+		 */
+		if (flags.bypasses)
+			return 0;
+		else {
+#ifdef DEBUG
+			pline("%s for a moment.", Tobjnam(obj, "pulsate"));
+#endif
+			obj->bypass = 0;
+		}
+	}
+
 	/*
-	 * Some parts of this function expect the object to on the floor
+	 * Some parts of this function expect the object to be on the floor
 	 * obj->{ox,oy} to be valid.  The exception to this (so far) is
 	 * for the STONE_TO_FLESH spell.
 	 */
@@ -1361,16 +1494,16 @@
 		    do_osshock(obj);
 		    break;
 		}
-		poly_obj(obj, STRANGE_OBJECT);
+		obj = poly_obj(obj, STRANGE_OBJECT);
 		newsym(obj->ox,obj->oy);
 		break;
 	case WAN_PROBING:
 		res = !obj->dknown;
 		/* target object has now been "seen (up close)" */
 		obj->dknown = 1;
-		if (Has_contents(obj)) {
+		if (Is_container(obj) || obj->otyp == STATUE) {
 		    if (!obj->cobj)
-			pline("%s is empty.", The(xname(obj)));
+			pline("%s empty.", Tobjnam(obj, "are"));
 		    else {
 			struct obj *o;
 			/* view contents (not recursively) */
@@ -1454,7 +1587,7 @@
 		switch (objects[obj->otyp].oc_class) {
 		    case ROCK_CLASS:	/* boulders and statues */
 			if (obj->otyp == BOULDER) {
-			    poly_obj(obj, HUGE_CHUNK_OF_MEAT);
+			    obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT);
 			    if (In_sokoban(&u.uz))
 				change_luck(-1);	/* Sokoban guilt */
 			    goto smell;
@@ -1472,7 +1605,7 @@
 				/* Unlikely to get here since genociding
 				 * monsters also sets the G_NOCORPSE flag.
 				 */
-				poly_obj(obj, CORPSE);
+				obj = poly_obj(obj, CORPSE);
 				break;
 			    }
 			} else { /* new rock class object... */
@@ -1502,13 +1635,13 @@
 		    }
 		    /* maybe add weird things to become? */
 		    case RING_CLASS:	/* some of the rings are stone */
-			poly_obj(obj, MEAT_RING);
+			obj = poly_obj(obj, MEAT_RING);
 			goto smell;
 		    case WAND_CLASS:	/* marble wand */
-			poly_obj(obj, MEAT_STICK);
+			obj = poly_obj(obj, MEAT_STICK);
 			goto smell;
 		    case GEM_CLASS:	/* rocks & gems */
-			poly_obj(obj, MEATBALL);
+			obj = poly_obj(obj, MEATBALL);
 smell:
 			if (herbivorous(youmonst.data) &&
 				!carnivorous(youmonst.data))
@@ -1667,9 +1800,11 @@
 		    pline("%s glows and fades.", The(xname(obj)));
 		/* make him pay for knowing !NODIR */
 	} else if(!u.dx && !u.dy && !u.dz && !(objects[obj->otyp].oc_dir == NODIR)) {
-	    if ((damage = zapyourself(obj, TRUE)) != 0)
-		losehp(damage, self_pronoun("zapped %sself with a wand", "him"),
-			NO_KILLER_PREFIX);
+	    if ((damage = zapyourself(obj, TRUE)) != 0) {
+		char buf[BUFSZ];
+		Sprintf(buf, "zapped %sself with a wand", uhim());
+		losehp(damage, buf, NO_KILLER_PREFIX);
+	    }
 	} else {
 
 		/*	Are we having fun yet?
@@ -1684,7 +1819,7 @@
 		current_wand = 0;
 	}
 	if (obj && obj->spe < 0) {
-	    pline("%s turns to dust.", The(xname(obj)));
+	    pline("%s to dust.", Tobjnam(obj, "turn"));
 	    useup(obj);
 	}
 	update_inventory();	/* maybe used a charge */
@@ -1697,6 +1832,7 @@
 boolean ordinary;
 {
 	int	damage = 0;
+	char buf[BUFSZ];
 
 	switch(obj->otyp) {
 		case WAN_STRIKING:
@@ -1731,12 +1867,13 @@
 		    if (!resists_blnd(&youmonst)) {
 			    You(are_blinded_by_the_flash);
 			    make_blinded((long)rnd(100),FALSE);
+			    if (!Blind) Your(vision_clears);
 		    }
 		    break;
 
 		case SPE_FIREBALL:
 		    You("explode a fireball on top of yourself!");
-		    explode(u.ux, u.uy, 11, d(6,6), WAND_CLASS);
+		    explode(u.ux, u.uy, 11, d(6,6), WAND_CLASS, EXPL_FIERY);
 		    break;
 		case WAN_FIRE:
 		    makeknown(WAN_FIRE);
@@ -1788,7 +1925,7 @@
 		    	makeknown(WAN_POLYMORPH);
 		case SPE_POLYMORPH:
 		    if (!Unchanging)
-		    	polyself();
+		    	polyself(FALSE);
 		    break;
 
 		case WAN_CANCELLATION:
@@ -1873,8 +2010,9 @@
 			  : "You seem no deader than before.");
 			break;
 		    }
+		    Sprintf(buf, "shot %sself with a death ray", uhim());
+		    killer = buf;
 		    killer_format = NO_KILLER_PREFIX;
-		    killer = self_pronoun("shot %sself with a death ray","him");
 		    You("irradiate yourself with pure energy!");
 		    You("die.");
 		    makeknown(obj->otyp);
@@ -1910,6 +2048,7 @@
 			You(are_blinded_by_the_flash);
 			make_blinded((long)damage, FALSE);
 			makeknown(obj->otyp);
+			if (!Blind) Your(vision_clears);
 		    }
 		    damage = 0;	/* reset */
 		    break;
@@ -2101,12 +2240,15 @@
 	int x, y, xx, yy, ptmp;
 	struct obj *otmp;
 	struct engr *e;
+	struct trap *ttmp;
 	char buf[BUFSZ];
 
 	/* some wands have special effects other than normal bhitpile */
 	/* drawbridge might change <u.ux,u.uy> */
 	x = xx = u.ux;	/* <x,y> is zap location */
 	y = yy = u.uy;	/* <xx,yy> is drawbridge (portcullis) position */
+	ttmp = t_at(x, y); /* trap if there is one */
+
 	switch (obj->otyp) {
 	case WAN_PROBING:
 	    ptmp = 0;
@@ -2156,11 +2298,26 @@
 		      ceiling(x, y), body_part(HEAD));
 		losehp(rnd((uarmh && is_metallic(uarmh)) ? 2 : 6),
 		       "falling rock", KILLED_BY_AN);
-		if ((otmp = mksobj_at(ROCK, x, y, FALSE)) != 0) {
+		if ((otmp = mksobj_at(ROCK, x, y, FALSE, FALSE)) != 0) {
 		    (void)xname(otmp);	/* set dknown, maybe bknown */
 		    stackobj(otmp);
 		}
 		newsym(x, y);
+	    } else if (!striking && ttmp && ttmp->ttyp == TRAPDOOR && u.dz > 0) {
+		if (!Blind) {
+			if (ttmp->tseen) {
+				pline("A trap door beneath you closes up then vanishes.");
+				disclose = TRUE;
+			} else {
+				You("see a swirl of %s beneath you.",
+					is_ice(x,y) ? "frost" : "dust");
+			}
+		} else {
+			You_hear("a twang followed by a thud.");
+		}
+		deltrap(ttmp);
+		ttmp = (struct trap *)0;
+		newsym(x, y);
 	    }
 	    break;
 	case SPE_STONE_TO_FLESH:
@@ -2172,13 +2329,19 @@
 	    } else if (u.dz > 0 && !OBJ_AT(u.ux, u.uy)) {
 		/*
 		Print this message only if there wasn't an engraving
-		affected here.
+		affected here.  If water or ice, act like waterlevel case.
 		*/
 		e = engr_at(u.ux, u.uy);
-		if (!(e && e->engr_type == ENGRAVE))
-		    pline("Blood pools at your %s.",
-			  makeplural(body_part(FOOT)));
+		if (!(e && e->engr_type == ENGRAVE)) {
+		    if (is_pool(u.ux, u.uy) || is_ice(u.ux, u.uy))
+			pline(nothing_happens);
+		    else
+			pline("Blood %ss %s your %s.",
+			      is_lava(u.ux, u.uy) ? "boil" : "pool",
+			      Levitation ? "beneath" : "at",
+			      makeplural(body_part(FOOT)));
 		}
+	    }
 	    break;
 	default:
 	    break;
@@ -2218,9 +2381,6 @@
 		case SPE_FORCE_BOLT:
 		    wipe_engr_at(x, y, d(2,4));
 		    break;
-		case SPE_DRAIN_LIFE:
-		    u_wipe_engr(3);
-		    break;
 		default:
 		    break;
 		}
@@ -2365,9 +2525,11 @@
 register struct monst *mtmp;
 register const char *force;		/* usually either "." or "!" */
 {
-	if(!cansee(bhitpos.x,bhitpos.y) || !flags.verbose)
-	    pline("%s hits it.", The(str));
-	else pline("%s hits %s%s", The(str), mon_nam(mtmp), force);
+	if((!cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp))
+	   || !flags.verbose)
+	    pline("%s %s it.", The(str), vtense(str, "hit"));
+	else pline("%s %s %s%s", The(str), vtense(str, "hit"),
+		   mon_nam(mtmp), force);
 }
 
 void
@@ -2375,8 +2537,9 @@
 register const char *str;
 register struct monst *mtmp;
 {
-	pline("%s misses %s.", The(str),
-	      (cansee(bhitpos.x,bhitpos.y) && flags.verbose) ?
+	pline("%s %s %s.", The(str), vtense(str, "miss"),
+	      ((cansee(bhitpos.x,bhitpos.y) || canspotmon(mtmp))
+	       && flags.verbose) ?
 	      mon_nam(mtmp) : "it");
 }
 #endif /*OVL0*/
@@ -2384,14 +2547,19 @@
 
 /* return TRUE if obj_type can't pass through iron bars */
 static boolean
-hits_bars(obj_type)
-int obj_type;
+hits_bars(obj)
+struct obj *obj;
 {
+    int obj_type = obj->otyp;
     /*
-    There should be a _lot_ of things here..., but lets start
-    with what started this change...
+    There should be a _lot_ of things here..., but iron ball
+    started this change and boulders, chests, and boxes were added later...
+    Corpses and statues that are at least medium size are also screened.
     */
-    if (obj_type == HEAVY_IRON_BALL)
+    if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL || obj_type == LARGE_BOX ||
+	obj_type == CHEST   || obj_type == ICE_BOX ||
+	((obj_type == CORPSE || obj_type == STATUE)
+	  && mons[obj->corpsenm].msize >= MZ_MEDIUM))
 	return TRUE;
     return FALSE;
 }
@@ -2455,16 +2623,16 @@
 	    }
 
 	    if(is_pick(obj) && inside_shop(x, y) &&
-					   shkcatch(obj, x, y)) {
+					   (mtmp = shkcatch(obj, x, y))) {
 		tmp_at(DISP_END, 0);
-		return(m_at(x, y));
+		return(mtmp);
 	    }
 
 	    typ = levl[bhitpos.x][bhitpos.y].typ;
 
 	    /* iron bars will block anything big enough */
 	    if ((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
-	    		 && typ == IRONBARS && hits_bars(obj->otyp)) {
+	    		 && typ == IRONBARS && hits_bars(obj)) {
 		bhitpos.x -= ddx;
 		bhitpos.y -= ddy;
 		break;
@@ -2498,21 +2666,37 @@
 	    if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
 		notonhead = (bhitpos.x != mtmp->mx ||
 			     bhitpos.y != mtmp->my);
-		    /* TODO: FLASHED_LIGHT hitting invisible monster
-		       should pass through instead of stop... */
-		if(weapon != ZAPPED_WAND) {
-		    if(weapon != INVIS_BEAM) tmp_at(DISP_END, 0);
-		    if (cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp)) {
+		if (weapon != FLASHED_LIGHT) {
+			if(weapon != ZAPPED_WAND) {
+			    if(weapon != INVIS_BEAM) tmp_at(DISP_END, 0);
+			    if (cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp)) {
+				if (weapon != INVIS_BEAM) {
+				    map_invisible(bhitpos.x, bhitpos.y);
+				    return(mtmp);
+				}
+			    } else
+				return(mtmp);
+			}
 			if (weapon != INVIS_BEAM) {
-			    map_invisible(bhitpos.x, bhitpos.y);
-			    return(mtmp);
+			    (*fhitm)(mtmp, obj);
+			    range -= 3;
 			}
-		    } else
-			return(mtmp);
-		}
-		if (weapon != INVIS_BEAM) {
-		    (*fhitm)(mtmp, obj);
-		    range -= 3;
+		} else {
+		    /* FLASHED_LIGHT hitting invisible monster
+		       should pass through instead of stop so
+		       we call flash_hits_mon() directly rather
+		       than returning mtmp back to caller. That
+		       allows the flash to keep on going. Note
+		       that we use mtmp->minvis not canspotmon()
+		       because it makes no difference whether
+		       the hero can see the monster or not.*/
+		    if (mtmp->minvis) {
+			obj->ox = u.ux,  obj->oy = u.uy;
+			(void) flash_hits_mon(mtmp, obj);
+		    } else {
+			tmp_at(DISP_END, 0);
+		    	return(mtmp); 	/* caller will call flash_hits_mon */
+		    }
 		}
 	    } else {
 		if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING &&
@@ -2574,7 +2758,8 @@
 		delay_output();
 		/* kicked objects fall in pools */
 		if((weapon == KICKED_WEAPON) &&
-		   is_pool(bhitpos.x, bhitpos.y))
+		   (is_pool(bhitpos.x, bhitpos.y) ||
+		   is_lava(bhitpos.x, bhitpos.y)))
 		    break;
 #ifdef SINKS
 		if(IS_SINK(typ) && weapon != FLASHED_LIGHT)
@@ -2800,7 +2985,7 @@
 		    break;
 		}
 		tmp = d(nd,6);
-		if (!rn2(6)) erode_weapon(MON_WEP(mon), TRUE);
+		if (!rn2(6)) erode_obj(MON_WEP(mon), TRUE, TRUE);
 		if (!rn2(6)) erode_armor(mon, TRUE);
 		break;
 	}
@@ -2810,6 +2995,7 @@
 	if (tmp > 0 && type >= 0 &&
 		resist(mon, type < ZT_SPELL(0) ? WAND_CLASS : '\0', 0, NOTELL))
 	    tmp /= 2;
+	if (tmp < 0) tmp = 0;		/* don't allow negative damage */
 #ifdef WIZ_PATCH_DEBUG
 	pline("zapped monster hp = %d (= %d - %d)", mon->mhp-tmp,mon->mhp,tmp);
 #endif
@@ -2928,8 +3114,8 @@
 		exercise(A_STR, FALSE);
 	    }
 	    /* using two weapons at once makes both of them more vulnerable */
-	    if (!rn2(u.twoweap ? 3 : 6)) erode_weapon(uwep, TRUE);
-	    if (u.twoweap && !rn2(3)) erode_weapon(uswapwep, TRUE);
+	    if (!rn2(u.twoweap ? 3 : 6)) erode_obj(uwep, TRUE, TRUE);
+	    if (u.twoweap && !rn2(3)) erode_obj(uswapwep, TRUE, TRUE);
 	    if (!rn2(6)) erode_armor(&youmonst, TRUE);
 	    break;
 	}
@@ -2949,9 +3135,10 @@
  * return the number of scrolls and spellbooks burned
  */
 int
-burn_floor_paper(x, y, give_feedback)
+burn_floor_paper(x, y, give_feedback, u_caused)
 int x, y;
 boolean give_feedback;	/* caller needs to decide about visibility checks */
+boolean u_caused;
 {
 	struct obj *obj, *obj2;
 	long i, scrquan, delquan;
@@ -2972,8 +3159,9 @@
 		    /* save name before potential delobj() */
 		    what = !give_feedback ? 0 : (x == u.ux && y == u.uy) ?
 				xname(obj) : distant_name(obj, xname);
-		    /* not useupf(), which charges */
-		    if (delquan < scrquan) obj->quan -= delquan;
+		    /* useupf(), which charges, only if hero caused damage */
+		    if (u_caused) useupf(obj, delquan);
+		    else if (delquan < scrquan) obj->quan -= delquan;
 		    else delobj(obj);
 		    cnt += delquan;
 		    if (give_feedback) {
@@ -3105,7 +3293,8 @@
 			    pline("%s disintegrates.", Monnam(mon));
 			    pline("%s body reintegrates before your %s!",
 				  s_suffix(Monnam(mon)),
-				  makeplural(body_part(EYE)));
+				  (eyecount(youmonst.data) == 1) ?
+				  	body_part(EYE) : makeplural(body_part(EYE)));
 			    pline("%s resurrects!", Monnam(mon));
 			}
 			mon->mhp = mon->mhpmax;
@@ -3204,6 +3393,7 @@
 	    if (abstype == ZT_LIGHTNING && !resists_blnd(&youmonst)) {
 		You(are_blinded_by_the_flash);
 		make_blinded((long)d(nd,50),FALSE);
+		if (!Blind) Your(vision_clears);
 	    }
 	    stop_occupation();
 	    nomul(0);
@@ -3248,7 +3438,7 @@
     }
     tmp_at(DISP_END,0);
     if (type == ZT_SPELL(ZT_FIRE))
-	explode(sx, sy, type, d(12,6), 0);
+	explode(sx, sy, type, d(12,6), 0, EXPL_FIERY);
     if (shopdamage)
 	pay_for_damage(abstype == ZT_FIRE ?  "burn away" :
 		       abstype == ZT_COLD ?  "shatter" :
@@ -3312,6 +3502,14 @@
 	int rangemod = 0;
 
 	if(abstype == ZT_FIRE) {
+	    struct trap *t = t_at(x, y);
+
+	    if (t && t->ttyp == WEB) {
+		/* a burning web is too flimsy to notice if you can't see it */
+		if (cansee(x,y)) Norep("A web bursts into flames!");
+		(void) delfloortrap(t);
+		if (cansee(x,y)) newsym(x,y);
+	    }
 	    if(is_ice(x, y)) {
 		melt_ice(x, y);
 	    } else if(is_pool(x,y)) {
@@ -3465,7 +3663,7 @@
 	}
 
 	if(OBJ_AT(x, y) && abstype == ZT_FIRE)
-		if (burn_floor_paper(x, y, FALSE) && couldsee(x, y))  {
+		if (burn_floor_paper(x, y, FALSE, type > 0) && couldsee(x, y)) {
 		    newsym(x,y);
 		    You("%s of smoke.",
 			!Blind ? "see a puff" : "smell a whiff");
@@ -3504,6 +3702,8 @@
 	obj->onamelth = 0;		/* no names */
 	obj->oxlth = 0;			/* no extra data */
 	obj->oattached = OATTACHED_NOTHING;
+	obj_extract_self(obj);		/* move rocks back on top */
+	place_object(obj, obj->ox, obj->oy);
 	if(!does_block(obj->ox,obj->oy,&levl[obj->ox][obj->oy]))
 	    unblock_point(obj->ox,obj->oy);
 	if(cansee(obj->ox,obj->oy))
@@ -3639,8 +3839,10 @@
 		pline("%s %s %s!", mult, xname(obj),
 			(cnt > 1L) ? destroy_strings[dindx*3 + 1]
 				  : destroy_strings[dindx*3]);
-		if(osym == POTION_CLASS && dmgtyp != AD_COLD)
-		    potionbreathe(obj);
+		if(osym == POTION_CLASS && dmgtyp != AD_COLD) {
+		    if (!breathless(youmonst.data) || haseyes(youmonst.data))
+		    	potionbreathe(obj);
+		}
 		if (obj->owornmask) {
 		    if (obj->owornmask & W_RING) /* ring being worn */
 			Ring_gone(obj);
@@ -3779,8 +3981,10 @@
 	/* attack level */
 	switch (oclass) {
 	    case WAND_CLASS:	alev = 12;	 break;
+	    case TOOL_CLASS:	alev = 10;	 break;
 	    case SCROLL_CLASS:	alev =  9;	 break;
 	    case POTION_CLASS:	alev =  6;	 break;
+	    case RING_CLASS:	alev =  5;	 break;
 	    default:		alev = u.ulevel; break;		/* spell */
 	}
 	/* defense level */
@@ -3789,18 +3993,20 @@
 	else if (dlev < 1) dlev = is_mplayer(mtmp->data) ? u.ulevel : 1;
 
 	resisted = rn2(100 + alev - dlev) < mtmp->data->mr;
-	if(resisted) {
-
-		if(tell) {
-		    shieldeff(mtmp->mx, mtmp->my);
-		    pline("%s resists!", Monnam(mtmp));
-		}
-		mtmp->mhp -= damage/2;
-	} else  mtmp->mhp -= damage;
+	if (resisted) {
+	    if (tell) {
+		shieldeff(mtmp->mx, mtmp->my);
+		pline("%s resists!", Monnam(mtmp));
+	    }
+	    damage = (damage + 1) / 2;
+	}
 
-	if(mtmp->mhp < 1) {
+	if (damage) {
+	    mtmp->mhp -= damage;
+	    if (mtmp->mhp < 1) {
 		if(m_using) monkilled(mtmp, "", AD_RBRE);
 		else killed(mtmp);
+	    }
 	}
 	return(resisted);
 }
@@ -3809,33 +4015,53 @@
 makewish()
 {
 	char buf[BUFSZ];
-	register struct obj *otmp;
+	struct obj *otmp, nothing;
 	int tries = 0;
 
+	nothing = zeroobj;  /* lint suppression; only its address matters */
 	if (flags.verbose) You("may wish for an object.");
 retry:
 	getlin("For what do you wish?", buf);
 	if(buf[0] == '\033') buf[0] = 0;
 	/*
 	 *  Note: if they wished for and got a non-object successfully,
-	 *  otmp == &zeroobj
+	 *  otmp == &zeroobj.  That includes gold, or an artifact that
+	 *  has been denied.  Wishing for "nothing" requires a separate
+	 *  value to remain distinct.
 	 */
-	otmp = readobjnam(buf);
+	otmp = readobjnam(buf, &nothing, TRUE);
 	if (!otmp) {
 	    pline("Nothing fitting that description exists in the game.");
 	    if (++tries < 5) goto retry;
 	    pline(thats_enough_tries);
-	    if (!(otmp = readobjnam((char *)0)))
-		return; /* for safety; should never happen */
+	    otmp = readobjnam((char *)0, (struct obj *)0, TRUE);
+	    if (!otmp) return;	/* for safety; should never happen */
+	} else if (otmp == &nothing) {
+	    /* explicitly wished for "nothing", presumeably attempting
+	       to retain wishless conduct */
+	    return;
 	}
 
 	/* KMH, conduct */
 	u.uconduct.wishes++;
 
 	if (otmp != &zeroobj) {
-	    if(otmp->oartifact && !touch_artifact(otmp,&youmonst))
-		dropy(otmp);
-	    else
+	    /* place_object looses these */
+	    boolean crysknife = (otmp->otyp == CRYSKNIFE);
+	    int oerode = otmp->oerodeproof;
+
+	    /* in case touching this object turns out to be fatal */
+	    place_object(otmp, u.ux, u.uy);
+
+	    if (otmp->oartifact && !touch_artifact(otmp,&youmonst)) {
+		obj_extract_self(otmp);	/* remove it from the floor */
+		dropy(otmp);		/* now put it back again :-) */
+	    } else {
+		obj_extract_self(otmp);
+		if (crysknife) {
+		    otmp->otyp = CRYSKNIFE;
+		    otmp->oerodeproof = oerode;
+		}
 		/* The(aobjnam()) is safe since otmp is unidentified -dlc */
 		(void) hold_another_object(otmp, u.uswallow ?
 				       "Oops!  %s out of your reach!" :
@@ -3846,6 +4072,7 @@
 					     Is_airlevel(&u.uz) || u.uinwater ?
 						   "slip" : "drop")),
 				       (const char *)0);
+	    }
 	    u.ublesscnt += rn1(100,50);  /* the gods take notice */
 	}
 }

