changeset 256:c495a4f288c6

Import UltraRogue from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 31 Jan 2017 19:56:04 -0500
parents d9badb9c0179
children c4b12d2d1dcd
files urogue/LICENSE.TXT urogue/Makefile urogue/README urogue/TODO urogue/armor.c urogue/artifact.c urogue/bag.c urogue/chase.c urogue/command.c urogue/daemon.c urogue/daemons.c urogue/dict.c urogue/dict.h urogue/dictutil.c urogue/dictutil.h urogue/encumb.c urogue/fight.c urogue/getplay.c urogue/history.txt urogue/ident.c urogue/init.c urogue/io.c urogue/lint-curses.h urogue/list.c urogue/magic.c urogue/main.c urogue/maze.c urogue/mdport.c urogue/memory.c urogue/misc.c urogue/monsdata.c urogue/monsters.c urogue/move.c urogue/newlvl.c urogue/options.c urogue/pack.c urogue/passages.c urogue/player.c urogue/potions.c urogue/random.c urogue/rings.c urogue/rip.c urogue/rogue.h urogue/rooms.c urogue/save.c urogue/scrolls.c urogue/state.c urogue/status.c urogue/sticks.c urogue/things.c urogue/trader.c urogue/urogue.6 urogue/urogue.sln urogue/urogue.vcproj urogue/verify.c urogue/vers.c urogue/weapons.c urogue/wizard.c urogue/xcrypt.c
diffstat 59 files changed, 42044 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/LICENSE.TXT	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,138 @@
+Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+Portions Copyright (C) 1985 Michael Morgan, Ken Dalka
+Portions Copyright (C) 1981 Michael Toy, Ken Arnold and Glenn Wichman
+Portions Copyright (C) 1993, 1995  Nicholas J. Kisseberth
+All rights reserved.
+
+===========================================================================
+
+UltaRogue: The Ultimate Adventure in the Dungeons of Doom
+Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name(s) of the author(s) nor the names of other contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+4. The name "UltraRogue" and "urogue" must not be used to endorse or 
+   promote products derived from this software without prior written 
+   permission.
+5. Products derived from this software may not be called "UltraRogue" or
+   "urogue", nor may "UltraRogue" or "urogue" appear in their name, 
+   without prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+===========================================================================
+
+Portions of this software are based on the work of Michael Morgan and 
+Ken Dalka. Used under license:
+
+Advanced Rogue
+Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka 
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name(s) of the author(s) nor the names of other contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+===========================================================================
+
+Portions of this software are based on the work of Michael Toy, Ken Arnold
+and Glenn Wichman. Used under license:
+
+Rogue: Exploring the Dungeons of Doom
+Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name(s) of the author(s) nor the names of other contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+===========================================================================
+
+Portions of this software (save/restore game state) are based on the work 
+of Nicholas J. Kisseberth. Used under license:
+
+Copyright (C) 1993, 1995 Nicholas J. Kisseberth
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name(s) of the author(s) nor the names of other contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/Makefile	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,167 @@
+# UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+# Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+# All rights reserved.
+#
+# See the file LICENSE.TXT for full copyright and licensing information.
+
+#
+# Makefile for urogue
+#
+
+DISTNAME=urogue1.0.7
+
+HDRS		  = dict.h dictutil.h rogue.h
+OBJS		  = armor.o \
+		artifact.o \
+		bag.o \
+		chase.o \
+		command.o \
+		daemon.o \
+		daemons.o \
+		dict.o \
+		dictutil.o \
+		encumb.o \
+		fight.o \
+		getplay.o \
+		ident.o \
+		init.o \
+		io.o \
+		list.o \
+		magic.o \
+		main.o \
+		maze.o \
+		memory.o \
+		misc.o \
+		monsdata.o \
+		monsters.o \
+		move.o \
+		newlvl.o \
+		options.o \
+		pack.o \
+		passages.o \
+		player.o \
+		potions.o \
+		random.o \
+		rings.o \
+		rip.o \
+		rooms.o \
+		save.o \
+		scrolls.o \
+		state.o \
+		status.o \
+		sticks.o \
+		things.o \
+		trader.o \
+		verify.o \
+		vers.o \
+		weapons.o \
+		wizard.o
+
+PROGRAM 	  = ur
+
+CFILES		  = armor.c \
+		artifact.c \
+		bag.c \
+		chase.c \
+		command.c \
+		daemon.c \
+		daemons.c \
+		dict.c \
+		dictutil.c \
+		encumb.c \
+		fight.c \
+		getplay.c \
+		ident.c \
+		init.c \
+		io.c \
+		list.c \
+		magic.c \
+		main.c \
+		maze.c \
+		memory.c \
+		misc.c \
+		monsdata.c \
+		monsters.c \
+		move.c \
+		newlvl.c \
+		options.c \
+		pack.c \
+		passages.c \
+		player.c \
+		potions.c \
+		random.c \
+		rings.c \
+		rip.c \
+		rooms.c \
+		save.c \
+		scrolls.c \
+		state.c \
+		status.c \
+		sticks.c \
+		things.c \
+		trader.c \
+		verify.c \
+		vers.c \
+		weapons.c \
+		wizard.c
+
+MISC=           Makefile README LICENSE.TXT history.txt TODO
+
+CC    = gcc
+CFLAGS= -O3
+CRLIB = -lcurses
+RM    = rm -f
+TAR   = tar
+
+urogue:	$(OBJS) $(MAKEFILE)
+	$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(CRLIB) -o $@
+
+clean:		
+	rm -f $(OBJS) urogue a.out core *.map urogue.exe urogue.cat 
+
+dist.src:
+	make clean
+	tar cf $(DISTNAME)-src.tar $(CFILES) $(HDRS) $(MISC)
+	gzip -f $(DISTNAME)-src.tar
+
+dist.irix:
+	make clean
+	make CC=cc CFLAGS="-woff 1116 -O3" urogue
+	nroff -man urogue.6 | colcrt - > urogue.cat
+	tar cf $(DISTNAME)-irix.tar urogue urogue.cat README LICENSE.TXT
+	gzip -f $(DISTNAME)-irix.tar
+
+dist.aix:
+	make clean
+	make CC=xlc CFLAGS="-qmaxmem=16768 -O3 -qstrict" urogue
+	nroff -man urogue.6 | colcrt - > urogue.cat
+	tar cf $(DISTNAME)-aix.tar urogue urogue.cat README LICENSE.TXT
+	gzip -f $(DISTNAME)-aix.tar
+
+dist.linux:
+	make clean
+	make urogue
+	groff -man urogue.6 | sed -e 's/.\x08//g' > urogue.cat
+	tar cf $(DISTNAME)-linux.tar urogue urogue.cat README LICENSE.TXT
+	gzip -f $(DISTNAME)-linux.tar
+
+dist.interix:
+	make clean
+	make urogue
+	groff -P-b -P-u -man -Tascii urogue.6 > urogue.cat
+	tar cf $(DISTNAME)-interix.tar urogue urogue.cat README LICENSE.TXT
+	gzip -f $(DISTNAME)-interix.tar
+
+dist.cygwin:
+	make clean
+	make urogue
+	groff -P-c -man -Tascii urogue.6 | sed -e 's/.\x08//g' > urogue.cat
+	tar cf $(DISTNAME)-cygwin.tar urogue.exe urogue.cat README LICENSE.TXT
+	gzip -f $(DISTNAME)-cygwin.tar
+
+dist.djgpp:
+	make clean
+	make LDFLAGS="-L$(DJDIR)/LIB" CRLIB="-lpdcurses" urogue
+	groff -man -Tascii urogue.6 | sed -e 's/.\x08//g' > urogue.cat
+	rm -f $(DISTNAME)-djgpp.zip
+	zip $(DISTNAME)-djgpp.zip urogue.exe urogue.cat README LICENSE.TXT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/README	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,74 @@
+UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+All rights reserved.
+
+There are 8 different treasures to be picked up in this version of rogue.
+The first is on level 25 and is relatively easy to pick up.  The last is
+on level 100 and is very difficult to get.  Carrying any one of the
+unique treasures allows the rogue to go up the stairs, but one has to
+be very careful.  To be a total winner, yu must pick up all 8 and
+return to the surface again.
+
+(note: the above may be incorrect, i think the number of levels is
+reduced to 50 and you have to get all 8 artifacts to go back up)
+
+The environment variable UROGUE is used to set up the default things
+for a player.  The options that can be set are:
+
+terse:		short output messages
+flush:		flush typeahead when fighting
+jump:		show position only at the end of running
+inven:		inventory style
+askme:		ask about unidentified things
+stopdoor:       stop running when next to something interesting
+name:		the rogue's name (string)
+fruit:		the funny fruit used by the rogue (string)
+file:		default savefile name (string)
+score:		default scorefile name (string)
+class:		default player class (string)
+
+Something like
+
+setenv UROGUE "name=George of the Jungle,fruit=peach,noterse,jump"
+
+does the obvious.  The "score" option is ignored unless you start
+urogue in wizard mode.  The class option string can have the following
+values: "fighter", "illus", "paladin", "ranger", "cleric", "magic",
+"assasin", "druid", "ninja", and "thief" and is initialized only at
+startup time.  The "inven" option can take the values "slow", "clear",
+and "overwrite".
+
+
+About the Author
+================
+
+Herb Chong is currently freelance writing and doing some teaching.
+If you pick up a copy of Windows Sources Magazine, you can usually
+find something he has written. Starting in October (1993) he will
+have a regular column. Herb has also just started teaching Adult Ed
+and undergraduate classes at Mercy College, a small and not very well
+known college in Westchester, NY.
+
+Acknowledgements
+================
+
+The source code for Rogue 3.6 (by Michael Toy, Ken Arnold and Glenn 
+Wichman) was used as the original basis for this game. A thousand 
+thanks go out to them and their classic adventure game of the early 1980s.
+
+Modifications of a long forgotten and bastardized nature were taken
+from Advanced Rogue 1.0 and/or SuperRogue. We believe that the flea market, 
+pools, and maze levels had their origin somewhere around here. 
+
+Chief Architect   : 	Herb Chong
+
+Major Contributers: 	Carlton Hommel	Mike Cooper 	Mike Laman
+			Jason Venner	Nick Kisseberth
+
+Other Contributers: 	Nick Flor	Henry Chai	Pat Place
+			Michael Maudlin	dan@ciprico 	edjames@ucbshadow
+			Web Dove	Tim Haapanen	tecot@cmu-cs-k.arpa
+
+Any omissions or errors are purely unintentional. The above list was
+compiled by Nick Kisseberth from the change log in UltraRogue through
+1987 and from correspondense with Herb Chong.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/TODO	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,23 @@
+UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+Copyright (C) 1993, 1995 Herb Chong
+All rights reserved.
+
+%W% %G%
+
+add_pack() calls ur_free() on the linked list pointer passed
+to it. Need to go through code and look for cases where we
+use that pointer after calling add_pack(). Electric
+fence in guard-free() mode should help.
+
+Similary throw_away(), discard(), discard_pack() free items...
+there will be more like this...
+
+Bug in genocide scroll: don't request list, type letter, etc...
+
+Implement potion of true sight.
+
+Bug: apparently when creating a new familiar after a player level
+change, something isn't begin reset because ur SEGVs when running
+through the monster list in do_chase() right after the new
+familiar is created. It appears that the familiar isn't being removed
+from the monster list.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/armor.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,207 @@
+/*
+    armor.c  -  functions for dealing with armor
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    Based on "Rogue: Exploring the Dungeons of Doom"
+    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include "rogue.h"
+
+/*
+    wear()
+        The player wants to wear something, so let him/her put it on.
+*/
+
+void
+wear(void)
+{
+    struct object *obj;
+
+    if (cur_armor != NULL)
+    {
+        msg("You are already wearing some.");
+
+        after = FALSE;
+
+        return;
+    }
+
+    /* What does player want to wear? */
+
+    if ((obj = get_object(pack, "wear", ARMOR, NULL)) == NULL)
+        return;
+
+    wear_ok(&player, obj, MESSAGE);
+    waste_time();
+
+    cur_armor = obj;
+    obj->o_flags |= ISKNOW;
+
+    msg("You are now wearing %s.", inv_name(obj, TRUE));
+
+    return;
+}
+
+/*
+    take_off()
+        Get the armor off of the players back
+*/
+
+void
+take_off(void)
+{
+    struct object   *obj;
+
+    if ((obj = cur_armor) == NULL)
+    {
+        msg("You aren't wearing armor!");
+        return;
+    }
+
+    if (!dropcheck(cur_armor))
+        return;
+
+    msg("You were wearing %c%c) %s.", ARMOR, print_letters[get_ident(obj)],
+        inv_name(obj, LOWERCASE));
+
+    cur_armor = NULL;
+
+    if (on(player, STUMBLER))
+    {
+        msg("Your foot feels a lot better now.");
+        turn_off(player, STUMBLER);
+    }
+}
+
+/*
+    wear_ok()
+        enforce player class armor restrictions
+*/
+
+int
+wear_ok(struct thing *wearee, struct object *obj, int print_message)
+{
+    int which      = obj->o_which;
+    int ret_val    = TRUE;
+    int class_type = wearee->t_ctype;
+
+    if (obj->o_type != ARMOR)
+        return(FALSE);
+    else
+        switch (class_type)
+        {
+            case C_MAGICIAN: /* cannot wear metal */
+            case C_ILLUSION:
+                switch (which)
+                {
+                    case RING_MAIL:
+                    case SCALE_MAIL:
+                    case PADDED_ARMOR:
+                    case CHAIN_MAIL:
+                    case BRIGANDINE:
+                    case SPLINT_MAIL:
+                    case GOOD_CHAIN:
+                    case PLATE_MAIL:
+                    case PLATE_ARMOR:
+                        ret_val = FALSE;
+                        break;
+                    default:
+                        break;
+                }
+
+            case C_THIEF:    /* cannot clank around */
+            case C_ASSASIN:
+            case C_NINJA:
+                switch (which)
+                {
+                    case CHAIN_MAIL:
+                    case BRIGANDINE:
+                    case SPLINT_MAIL:
+                    case GOOD_CHAIN:
+                    case PLATE_MAIL:
+                    case PLATE_ARMOR:
+                        ret_val = FALSE;
+                        break;
+                    default:
+                        break;
+                }
+
+            case C_CLERIC:   /* cannot wear plate */
+            case C_DRUID:
+                switch (which)
+                {
+                    case PLATE_MAIL:
+                    case PLATE_ARMOR:
+                    case MITHRIL:
+                        ret_val = FALSE;
+                        break;
+                    default:
+                        break;
+                }
+
+            case C_FIGHTER: /* wear anything */
+            case C_RANGER:
+                break;
+
+            case    C_PALADIN:  /* cannot wear common stuff */
+                switch (which)
+                {
+                    case SOFT_LEATHER:
+                    case CUIRBOLILLI:
+                    case HEAVY_LEATHER:
+                    case STUDDED_LEATHER:
+                    case PADDED_ARMOR:
+                    case BRIGANDINE:
+                        ret_val = FALSE;
+                        break;
+                    default:
+                        break;
+                }
+
+            case C_MONSTER:
+                break;
+
+            default:      /* Unknown class */
+                debug("Unknown class %d.", class_type);
+                break;
+        }
+
+    if (ret_val == FALSE && print_message == MESSAGE)
+        switch (class_type)
+        {
+            case C_MAGICIAN:
+            case C_ILLUSION:
+                msg("You cannot regenerate spell points while wearing that!");
+                break;
+
+            case C_THIEF:
+            case C_ASSASIN:
+            case C_NINJA:
+                msg("Don't expect to be stealthy while wearing that!");
+                break;
+
+            case C_CLERIC:
+            case C_DRUID:
+            case C_PALADIN:
+                msg("Your god strongly disapproves of your wearing that!");
+                break;
+
+            case C_FIGHTER:
+            case C_RANGER:
+            case C_MONSTER:
+                break;
+        }
+
+    return(ret_val);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/artifact.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,1922 @@
+/*
+    artifact.c  -  functions for dealing with artifacts
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include <stdlib.h>
+#include <ctype.h>
+#include "rogue.h"
+
+/*
+    apply()
+        apply an artifact
+*/
+
+void
+apply(void)
+{
+    struct linked_list  *item;
+    struct object   *obj;
+    int which;
+    int chance;
+
+    if ((item = get_item("activate", ARTIFACT)) == NULL)
+        return;
+
+    obj = OBJPTR(item);
+    which = obj->o_which;
+
+    if (!(obj->ar_flags & ISACTIVE))
+    {
+        chance = rnd(100) - 10 * rnd(luck);
+        debug("Rolled %d.", chance);
+        if (chance < 5)
+            do_major();
+        else if (chance < 50)
+            do_minor(obj);
+        else
+            obj->ar_flags |= ISACTIVE;
+    }
+
+    if (obj->ar_flags & ISACTIVE)
+    {
+        switch (which)
+        {
+            case TR_PURSE:    do_bag(obj);
+                              break;
+            case TR_PHIAL:    do_phial();
+                              break;
+            case TR_AMULET:   do_amulet();
+                              break;
+            case TR_PALANTIR: do_palantir();
+                              break;
+            case TR_CROWN:    do_crown();
+                              break;
+            case TR_SCEPTRE:  do_sceptre();
+                              break;
+            case TR_SILMARIL: do_silmaril();
+                              break;
+            case TR_WAND:     do_wand();
+                              break;
+            default:          nothing_message(ISCURSED);
+                              return;
+        }
+    }
+
+    if (rnd(pstats.s_lvl) < 6)
+        do_minor(obj);
+
+    turn_on(player, POWEREAT);
+}
+
+/*
+    possessed(int artifact)
+        was the hero carrying a particular artifact
+*/
+
+int
+possessed(int artifact)
+{
+    return (picked_artifact >> artifact) & 1;
+}
+
+/*
+    is_carrying(int artifact)
+        is the hero carrying a particular artifact
+*/
+
+int
+is_carrying(int artifact)
+{
+    return (has_artifact >> artifact) & 1;
+}
+
+/*
+    make_artifact()
+        is it time to make a new artifact?
+*/
+
+int
+make_artifact(void)
+{
+    int i;
+
+    mpos = 0;
+
+    debug("Artifact possession and picked flags : %x %x.",
+        has_artifact, picked_artifact);
+
+    for(i = 0; i < maxartifact; i++)
+    {
+       if (!is_carrying(i) && arts[i].ar_level <= level)
+           return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+    new_artifact(int which, struct object *cur)
+        make a specified artifact
+*/
+
+struct object *
+new_artifact(int which, struct object *cur)
+{
+    if (which >= maxartifact)
+    {
+        debug("Bad artifact %d.  Random one created.", which);
+        which = rnd(maxartifact);
+    }
+
+    if (which < 0)
+    {
+        for (which = 0; which < maxartifact; which++)
+            if (!is_carrying(which) && arts[which].ar_level <= level)
+                break;
+    }
+
+    debug("Artifact number: %d.", which);
+
+    cur->o_hplus = cur->o_dplus = 0;
+    cur->o_damage = cur->o_hurldmg = "0d0";
+    cur->o_ac = 11;
+    cur->o_mark[0] = '\0';
+    cur->o_type = ARTIFACT;
+    cur->o_which = which;
+    cur->o_weight = arts[which].ar_weight;
+    cur->o_flags = 0;
+    cur->o_group = 0;
+    cur->o_count = 1;
+    cur->o_bag = NULL;
+    cur->ar_flags = 0;
+
+    return(cur);
+}
+
+/*
+    do_minor(struct object *tr)
+        side effects and minor malevolent effects of artifacts
+*/
+
+void
+do_minor(struct object *tr)
+{
+    int which;
+    long loss;
+
+    which = rnd(110);
+
+    debug("Rolled %d.", which);
+
+    switch (which)
+    {
+        case 0:
+            seemsg("You develop some acne on your face.");
+            break;
+
+        case 1:
+            if (on(player, CANSCENT))
+            {
+                msg("A sudden whiff of BO causes you to faint.");
+                no_command = STONETIME;
+            }
+            else if (off(player, ISUNSMELL))
+                msg("You begin to smell funny.");
+            break;
+
+        case 2:
+            seemsg("A wart grows on the end of your nose.");
+            break;
+
+        case 3:
+            hearmsg("Your hear strange noises in the distance.");
+            break;
+
+        case 4:
+            hearmsg("You hear shuffling in the distance.");
+            break;
+
+        case 5:
+            hearmsg("You hear clanking in the distance.");
+            break;
+
+        case 6:
+            hearmsg("You hear water dripping onto the floor.");
+            break;
+
+        case 7:
+            hearmsg("The dungeon goes strangely silent.");
+            break;
+
+        case 8:
+            msg("You suddenly feel very warm.");
+            break;
+
+        case 9:
+            msg("You feel very hot.");
+            break;
+
+        case 10:
+            msg("A blast of heat hits you.");
+            break;
+
+        case 11:
+            {
+                 struct room *rp;
+
+                 if (off(player, ISBLIND))
+                    msg("A pillar of flame leaps up beside you.");
+                 else
+                    msg("You feel something very hot nearby.");
+
+                 if (ntraps + 1 < 2 * MAXTRAPS &&
+                     fallpos(hero, &traps[ntraps].tr_pos))
+                 {
+                    mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x,
+                       FIRETRAP);
+                    traps[ntraps].tr_type = FIRETRAP;
+                    traps[ntraps].tr_flags = ISFOUND;
+                    traps[ntraps].tr_show = FIRETRAP;
+                    ntraps++;
+
+                    if ((rp = roomin(hero)) != NULL)
+                    {
+                        rp->r_flags &= ~ISDARK;
+                        light(&hero);
+                        mvwaddch(cw, hero.y, hero.x, PLAYER);
+                    }
+                }
+            }
+            break;
+
+        case 12:
+            msg("You feel a blast of hot air.");
+            break;
+
+        case 13:
+            msg("You feel very cold.");
+            break;
+
+        case 14:
+            msg("You break out in a cold sweat.");
+            break;
+
+        case 15:
+            if (off(player, ISBLIND) && cur_armor == NULL)
+                msg("You are covered with frost.");
+            else if (off(player, ISBLIND))
+                msg("Your armor is covered with frost.");
+            else if (cur_armor == NULL)
+                msg("Your body feels very cold and you begin to shiver.");
+            else
+                msg("Your armor feels very cold.  You hear cracking ice.");
+            break;
+
+        case 16:
+            msg("A cold wind whistles through the dungeon.");
+            break;
+
+        case 17:
+            {
+                int change;
+
+                change = 18 - pstats.s_str;
+                chg_str(change, TRUE, FALSE);
+                chg_dext(-change, TRUE, FALSE);
+
+                if (change > 0)
+                    msg("You feel stronger and clumsier now.");
+                else if (change < 0)
+                    msg("You feel weaker and more dextrous now.");
+                else
+                    nothing_message(ISCURSED);
+            }
+            break;
+
+        case 18:
+            msg("You begin to itch all over.");
+            break;
+
+        case 19:
+            msg("You begin to feel hot and itchy.");
+            break;
+
+        case 20:
+            msg("You feel a burning itch.");
+            chg_dext(-1, FALSE, TRUE);
+
+            if (off(player, HASITCH))
+            {
+                turn_on(player, HASITCH);
+                light_fuse(FUSE_UNITCH, 0, roll(4,6), AFTER);
+            }
+            else
+                lengthen_fuse(FUSE_UNITCH, roll(4,6));
+            break;
+
+        case 21:
+            if (off(player, ISBLIND))
+               msg("Your skin begins to flake and peel.");
+            else
+               msg("You feel an urge to scratch an itch.");
+            break;
+
+
+        case 22:
+            seemsg("Your hair begins to turn grey.");
+            break;
+
+        case 23:
+            seemsg("Your hair begins to turn white.");
+            break;
+
+        case 24:
+            seemsg("Some of your hair instantly turns white.");
+            break;
+
+        case 25:
+            seemsg("You are covered with long white hair.");
+            break;
+
+        case 26:
+            seemsg("You are covered with long red hair.");
+            break;
+
+        case 27:
+            msg("You grow a beard.");
+            break;
+
+        case 28:
+            msg("Your hair falls out.");
+            break;
+
+        case 29:
+            msg("You feel a burning down below.");
+            break;
+
+        case 30:
+            msg("Your toes fall off.");
+            break;
+
+        case 31:
+            msg("You grow some extra toes.");
+            break;
+
+        case 32:
+            msg("You grow some extra fingers.");
+            break;
+
+        case 33:
+            msg("You grow an extra thumb.");
+            break;
+
+        case 34:
+            msg("Your nose falls off.");
+            break;
+
+        case 35:
+            msg("Your nose gets bigger.");
+            break;
+
+        case 36:
+            msg("Your nose shrinks.");
+            break;
+
+        case 37:
+            msg("An eye grows on your forehead.");
+            break;
+
+        case 38:
+            seemsg("You see beady eyes watching from a distance.");
+            break;
+
+        case 39:
+            msg("The dungeon rumbles for a moment.");
+            break;
+
+        case 40:
+            seemsg("A flower grows on the floor next to you.");
+            break;
+
+        case 41:
+            msg("You are stunned by a psionic blast.");
+
+            if (on(player, ISHUH))
+                lengthen_fuse(FUSE_UNCONFUSE, rnd(40) + (HUHDURATION * 3));
+            else
+            {
+                light_fuse(FUSE_UNCONFUSE,0,rnd(40)+(HUHDURATION * 3), AFTER);
+                turn_on(player, ISHUH);
+            }
+            break;
+
+        case 42:
+            msg("You are confused by thousands of voices in your head.");
+
+            if (on(player, ISHUH))
+                lengthen_fuse(FUSE_UNCONFUSE, rnd(10) + (HUHDURATION * 2));
+            else
+            {
+                light_fuse(FUSE_UNCONFUSE,0,rnd(10)+(HUHDURATION * 2), AFTER);
+                turn_on(player, ISHUH);
+            }
+            break;
+
+        case 43:
+            hearmsg("You hear voices in the distance.");
+            break;
+
+        case 44:
+            msg("You feel a strange pull.");
+            teleport();
+
+            if (off(player, ISCLEAR))
+            {
+                if (on(player, ISHUH))
+                    lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
+                else
+                {
+                    light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER);
+                    turn_on(player, ISHUH);
+                }
+            }
+            break;
+
+        case 45:
+            msg("You feel less healthy now.");
+            pstats.s_const = max(pstats.s_const - 1, 3);
+            max_stats.s_const = max(max_stats.s_const - 1, 3);
+            break;
+
+        case 46:
+            msg("You feel weaker now.");
+            chg_str(-1, TRUE, FALSE);
+            break;
+
+        case 47:
+            msg("You feel less wise now.");
+            pstats.s_wisdom = max(pstats.s_wisdom - 1, 3);
+            max_stats.s_wisdom = max(max_stats.s_wisdom - 1, 3);
+            break;
+
+        case 48:
+            msg("You feel less dextrous now.");
+            chg_dext(-1, TRUE, FALSE);
+            break;
+
+        case 49:
+            msg("You feel less intelligent now.");
+            pstats.s_intel = max(pstats.s_intel - 1, 3);
+            max_stats.s_intel = max(max_stats.s_intel - 1, 3);
+            break;
+
+        case 50:
+            msg("A trap door opens underneath your feet.");
+            mpos = 0;
+            level++;
+            new_level(NORMLEV,0);
+
+            if (rnd(4) < 2)
+            {
+                addmsg("You are damaged by the fall");
+
+                if ((pstats.s_hpt -= roll(1, 6)) <= 0)
+                {
+                    addmsg("!  The fall killed you.");
+                    endmsg();
+                    death(D_FALL);
+                    return;
+                }
+            }
+
+            addmsg("!");
+            endmsg();
+
+            if (off(player, ISCLEAR) && rnd(4) < 3)
+            {
+                if (on(player, ISHUH))
+                    lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
+                else
+                    light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER);
+
+                turn_on(player, ISHUH);
+            }
+            else
+                msg("You feel dizzy for a moment, but it quickly passes.");
+
+            break;
+
+        case 51:
+            msg("A maze entrance opens underneath your feet.");
+            mpos = 0;
+            level++;
+            new_level(MAZELEV,0);
+
+            if (rnd(4) < 2)
+            {
+                addmsg("You are damaged by the fall");
+
+                if ((pstats.s_hpt -= roll(1, 6)) <= 0)
+                {
+                    addmsg("!  The fall killed you.");
+                    endmsg();
+                    death(D_FALL);
+                    return;
+                }
+            }
+            addmsg("!");
+            endmsg();
+
+            if (off(player, ISCLEAR) && rnd(4) < 3)
+            {
+                if (on(player, ISHUH))
+                    lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
+                else
+                    light_fuse(FUSE_UNCONFUSE,0, rnd(8) + HUHDURATION, AFTER);
+
+                turn_on(player, ISHUH);
+            }
+            else
+                msg("You feel dizzy for a moment, but it quickly passes.");
+
+            break;
+
+        case 52:
+            hearmsg("You hear a wailing sound in the distance.");
+            aggravate();
+            break;
+
+        case 53:
+            read_scroll(&player, S_HOLD, ISCURSED);
+            break;
+
+        case 54:
+            msg("You can't move.");
+            no_command = 3 * HOLDTIME;
+            break;
+
+        case 55:
+            hearmsg("You hear a buzzing sound.");
+            aggravate();
+            break;
+
+        case 56:
+            msg("Your limbs stiffen.");
+            no_command = 3 * STONETIME;
+            break;
+
+        case 57:
+            msg("You feel a rock in your shoe hurting your foot.");
+            turn_on(player, STUMBLER);
+            break;
+
+        case 58:
+            msg("You get a hollow feeling in your stomach.");
+            food_left -= 500;
+            break;
+
+        case 59:
+            msg("Your purse feels lighter.");
+
+            loss  = 50L + ulrnd(purse / 2L);
+            purse = (purse > loss) ? purse - loss : 0L;
+            break;
+
+        case 60:
+            msg("A pixie appears and grabs gold from your purse.");
+
+            loss = 50L + rnd(50);
+            purse = (purse > loss) ? purse - loss : 0L;
+            break;
+
+        case 61:
+            msg("You feel a tingling sensation all over.");
+            pstats.s_hpt -= ulrnd(pstats.s_hpt / 3L);
+            break;
+
+        case 62:
+            msg("You feel a pull downwards.");
+            break;
+
+        case 63:
+            msg("You feel a strange pull downwards.");
+            break;
+
+        case 64:
+            msg("You feel a peculiar pull downwards.");
+            break;
+
+        case 65:
+            msg("You have a strange urge to go down.");
+            break;
+
+        case 66:
+            msg("You feel a pull upwards.");
+            break;
+
+        case 67:
+            msg("You feel a strange pull upwards.");
+            break;
+
+        case 68:
+            msg("You have a strange feeling for a moment.");
+            break;
+
+        case 69:
+            msg("You float in the air for a moment.");
+            break;
+
+        case 70:
+            msg("You feel very heavy for a moment.");
+            break;
+
+        case 71:
+            msg("You feel a strange sense of loss.");
+            break;
+
+        case 72:
+            msg("You feel the earth spinning underneath your feet.");
+            break;
+
+        case 73:
+            msg("You feel in touch with a Universal Oneness.");
+            break;
+
+        case 74:
+            hearmsg("You hear voices in the distance.");
+            break;
+
+        case 75:
+            msg("A strange feeling of power comes over you.");
+            break;
+
+        case 76:
+            msg("You feel a strange sense of unease.");
+            break;
+
+        case 77:
+            msg("You feel Lady Luck is looking the other way.");
+            luck++;
+            break;
+
+        case 78:
+            msg("You feel your pack vibrate for a moment.");
+            break;
+
+        case 79:
+            msg("You feel someone is watching you.");
+            break;
+
+        case 80:
+            msg("You feel your hair standing on end.");
+            break;
+
+        case 81:
+            msg("Wait!  The walls are moving!");
+            new_level(NORMLEV,0);
+            break;
+
+        case 82:
+            msg("Wait!  Walls are appearing out of nowhere!");
+            new_level(MAZELEV,0);
+            break;
+
+        case 83:
+            blue_light(ISCURSED);
+            break;
+
+        case 84:
+            msg("Your mind goes blank for a moment.");
+            wclear(cw);
+            light(&hero);
+            status(TRUE);
+            break;
+
+        case 85:
+            if (on(player, ISDEAF))
+            {
+                msg("You feel your ears burn for a moment.");
+                lengthen_fuse(FUSE_HEAR, 2 * PHASEDURATION);
+            }
+            else
+            {
+                msg("You are suddenly surrounded by silence.");
+                turn_on(player, ISDEAF);
+                light_fuse(FUSE_HEAR, 0, 2 * PHASEDURATION, AFTER);
+            }
+            break;
+
+        case 86:
+            {
+                apply_to_bag(pack, 0, NULL, baf_curse, NULL);
+
+                if (off(player, ISUNSMELL))
+                    msg("You smell a faint trace of burning sulfur.");
+            }
+            break;
+
+        case 87:
+            msg("You have contracted a parasitic infestation.");
+            infest_dam++;
+            turn_on(player, HASINFEST);
+            break;
+
+        case 88:
+            msg("You suddenly feel a chill run up and down your spine.");
+            turn_on(player, ISFLEE);
+            player.t_ischasing = FALSE;
+            player.t_chasee = &player;
+            break;
+
+        case 89:
+            if (cur_weapon != NULL)
+                msg("You feel your %s get very hot.",
+                    inv_name(cur_weapon, LOWERCASE));
+            break;
+
+        case 90:
+            if (cur_weapon != NULL)
+                msg("Your %s glows white for an instant.",
+                    inv_name(cur_weapon, LOWERCASE));
+            break;
+
+        case 91:
+            if (cur_armor != NULL)
+                msg("Your %s gets very hot.", inv_name(cur_armor, LOWERCASE));
+            break;
+
+        case 92:
+            if (cur_weapon != NULL)
+                msg("Your %s suddenly feels very cold.",
+                    inv_name(cur_weapon, LOWERCASE));
+            break;
+
+        case 93:
+            if (cur_armor != NULL)
+                msg("Your armor is covered by an oily film.");
+            break;
+
+        case 94:
+            read_scroll(&player, S_CREATE, ISNORMAL);
+            break;
+
+        case 95:
+            lower_level(D_POTION);
+            break;
+
+        case 96:
+            {
+                int x, y;
+
+                for (x = -1; x <= 1; x++)
+                {
+                    for (y = -1; y <= 1; y++)
+                    {
+                        if (x == 0 && y == 0)
+                            continue;
+
+                        delta.x = x;
+                        delta.y = y;
+
+                        do_zap(&player, WS_POLYMORPH, rnd(2)
+                            ? ISCURSED : ISNORMAL);
+                    }
+                }
+            }
+            break;
+
+        case 97:
+            {
+                int x, y;
+
+                for (x = -1; x <= 1; x++)
+                {
+                    for (y = -1; y <= 1; y++)
+                    {
+                        if (x == 0 && y == 0)
+                            continue;
+
+                        delta.x = x;
+                        delta.y = y;
+
+                        do_zap(&player, WS_INVIS, ISNORMAL);
+                    }
+                }
+            }
+            break;
+
+        default:
+            tr->ar_flags &= ~ISACTIVE;
+            hearmsg("You hear a click coming from %s.",inv_name(tr,LOWERCASE));
+            break;
+
+    }
+}
+
+/*
+    do_major()
+
+        major malevolent effects
+
+        0.  read_scroll(S_SELFTELEPORT, ISCURSED)
+        1.  PERMBLIND for twice normal duration
+        2.  new_level(THRONE);
+        3.  turn_on(player, SUPEREAT);
+        4.  lengthen(noslow, 20 + rnd(20));
+        5.  lower_level(D_POTION) * roll(1,4)
+        6.  change stats
+        7.  FIRETRAP
+        8.  armor crumbles
+        9.  weapon crumbles
+       10. weapon crumbles
+       11. curse weapon
+*/
+
+void
+do_major(void)
+{
+    int which;
+
+    which = rnd(12);
+
+    debug("Rolled %d.", which);
+
+    switch (which)
+    {
+        case 0:
+            read_scroll(&player, S_SELFTELEP, ISCURSED);
+            break;
+
+        case 1:
+            quaff(&player, P_TRUESEE, ISCURSED);
+            quaff(&player, P_TRUESEE, ISCURSED);
+            break;
+
+        case 2:
+            new_level(THRONE,0);
+            break;
+
+        case 3: /* Turn off other body-affecting spells */
+
+            if (on(player, ISREGEN))
+            {
+                extinguish_fuse(FUSE_UNREGEN);
+                turn_off(player, ISREGEN);
+                unregen(NULL);
+            }
+
+            if (on(player, NOCOLD))
+            {
+                extinguish_fuse(FUSE_UNCOLD);
+                turn_off(player, NOCOLD);
+                uncold(NULL);
+            }
+
+            if (on(player, NOFIRE))
+            {
+                extinguish_fuse(FUSE_UNHOT);
+                turn_off(player, NOFIRE);
+                unhot(NULL);
+            }
+
+            if (on(player, SUPEREAT))
+            {
+                lengthen_fuse(FUSE_UNSUPEREAT, 2 * PHASEDURATION);
+                msg("Your body temperature rises still further.");
+            }
+            else
+            {
+                msg("You feel very warm all over.");
+                light_fuse(FUSE_UNSUPEREAT, 0, 2 * PHASEDURATION, AFTER);
+                turn_on(player, SUPEREAT);
+            }
+            break;
+
+        case 4:
+            msg("You feel yourself moving %sslower.",
+            on(player, ISSLOW) ? "even " : "");
+
+            if (on(player, ISSLOW))
+                lengthen_fuse(FUSE_NOSLOW, PHASEDURATION);
+            else
+            {
+                turn_on(player, ISSLOW);
+                player.t_turn = TRUE;
+                light_fuse(FUSE_NOSLOW, 0, PHASEDURATION, AFTER);
+            }
+            break;
+
+        case 5:
+            {
+                int i, n = roll(1, 4);
+
+                for (i = 1; i < n; i++)
+                    lower_level(D_POTION);
+            }
+            break;
+
+        case 6:
+            if (rnd(2))
+                add_intelligence(TRUE);
+
+            if (rnd(2))
+                chg_dext(-1, TRUE, FALSE);
+
+            if (rnd(2))
+                chg_str(-1, TRUE, FALSE);
+
+            if (rnd(2))
+                add_wisdom(TRUE);
+
+            if (rnd(2))
+                add_const(TRUE);
+
+            break;
+
+        case 7:
+            {
+                struct room *rp;
+
+                if (ntraps + 1 >= MAXTRAPS)
+                {
+                    msg("You feel a puff of hot air.");
+                    return;
+                }
+
+                for (; ntraps < 2 * MAXTRAPS; ntraps++)
+                {
+                    if (!fallpos(hero, &traps[ntraps].tr_pos))
+                        break;
+
+                    mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x,
+                        FIRETRAP);
+                    traps[ntraps].tr_type   = FIRETRAP;
+                    traps[ntraps].tr_flags |= ISFOUND;
+                    traps[ntraps].tr_show   = FIRETRAP;
+
+                    if ((rp = roomin(hero)) != NULL)
+                        rp->r_flags &= ~ISDARK;
+                }
+            }
+            break;
+
+        case 8:
+            {
+                object  *obj;
+
+                if (cur_weapon == NULL)
+                {
+                    msg("You feel your hands tingle a moment.");
+                    pstats.s_dmg = "1d2";
+                    return;
+                }
+
+                obj = apply_to_bag(pack, 0, NULL, bafcweapon, NULL);
+
+                if (obj->o_flags & ISMETAL)
+                    msg("Your %s melts and disappears.",
+                        inv_name(obj,LOWERCASE));
+                else
+                    msg("Your %s crumbles in your hands.",
+                        inv_name(obj, LOWERCASE));
+
+                obj->o_flags &= ~ISCURSED;
+                dropcheck(obj);
+                del_bag(pack, obj);
+
+            }
+            break;
+
+        case 9:
+            {
+                object  *obj;
+
+                if (cur_armor == NULL)
+                {
+                    msg("Your body tingles a moment.");
+                    return;
+                }
+
+                obj = apply_to_bag(pack, 0, NULL, bafcarmor, NULL);
+
+                msg("Your %s crumbles into small black powdery dust.",
+                    inv_name(obj, LOWERCASE));
+
+                obj->o_flags &= ~ISCURSED;
+                dropcheck(obj);
+                del_bag(pack, obj);
+            }
+            break;
+
+        default:
+
+            if (cur_weapon == NULL)
+            {
+                seemsg("Your hand glows yellow for an instant.");
+                pstats.s_dmg = "1d3";
+                return;
+            }
+
+            seemsg("Your %s glows bright red for a moment.",
+                   weaps[cur_weapon->o_which].w_name);
+
+            if (cur_weapon->o_hplus > 0)
+                cur_weapon->o_hplus = -rnd(3);
+            else
+                cur_weapon->o_hplus -= rnd(3);
+
+            if (cur_weapon->o_dplus > 0)
+                cur_weapon->o_dplus = -rnd(3);
+            else
+                cur_weapon->o_dplus -= rnd(3);
+
+            cur_weapon->o_flags = ISCURSED | ISLOST;
+            cur_weapon->o_ac = 0;
+
+            break;
+    }
+}
+
+/*
+    do_phial()
+        handle powers of the Phial of Galadriel
+*/
+
+void
+do_phial(void)
+{
+    int which;
+
+    /* Prompt for action */
+
+    msg("How do you wish to apply the Phial of Galadriel (* for list)? ");
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    if (which < 0 || which > 1)
+    {
+        add_line("[a] total healing");
+        add_line("[b] total monster confusion");
+        end_line();
+        msg("");
+        msg("Which power do you wish to use? ");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > 1)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+
+            msg("");
+            msg("Please enter one of the listed powers: ");
+
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successsful.");
+
+    switch (which)
+    {
+        case 0:
+            pstats.s_hpt = max_stats.s_hpt += rnd(pstats.s_lvl) + 1;
+            pstats.s_power = max_stats.s_power += rnd(pstats.s_lvl) + 1;
+            break;
+
+        case 1:
+            {
+                struct linked_list  *mi;
+                struct thing    *tp;
+
+                for (mi = mlist; mi != NULL; mi = next(mi))
+                {
+                    tp = THINGPTR(mi);
+
+                    if (off(*tp, ISUNIQUE) || !save_throw(VS_MAGIC, tp))
+                        turn_on(*tp, ISHUH);
+                }
+            }
+            break;
+
+        default:
+            msg("What a strange thing to do!!");
+            break;
+
+    }
+}
+
+/*
+    do_palantir()
+        handle powers of the Palantir of Might
+*/
+
+void
+do_palantir(void)
+{
+    int which, limit;
+
+    /* Prompt for action */
+
+    msg("How do you wish to apply the Palantir of Might? (* for list): ");
+
+    limit = 3;
+
+    if (is_carrying(TR_SCEPTRE))
+        limit += 1;
+
+    if (is_carrying(TR_CROWN))
+        limit += 1;
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    if (which < 0 || which > limit)
+    {
+        msg("");
+        add_line("[a] monster detection");
+        add_line("[b] gold detection");
+        add_line("[c] magic detection");
+        add_line("[d] food detection");
+
+        if (limit >= 4)
+            add_line("[e] teleportation");
+
+        if (limit >= 5)
+            add_line("[f] clear thought");
+
+        end_line();
+
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > limit)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    switch (which)
+    {
+        case 0: quaff(&player, P_MONSTDET, ISNORMAL);
+                break;
+        case 1: read_scroll(&player, S_GFIND, ISNORMAL);
+                break;
+        case 2: quaff(&player, P_TREASDET, ISNORMAL);
+                break;
+        case 3: read_scroll(&player, S_FOODDET, ISNORMAL);
+                break;
+        case 4: read_scroll(&player, S_SELFTELEP, ISNORMAL);
+                break;
+        case 5: quaff(&player, P_CLEAR, ISNORMAL);
+                break;
+        default:
+                msg("What a strange thing to do!!");
+                break;
+    }
+}
+
+/*
+    do_silmaril()
+        handle powers of the Silamril of Ea
+*/
+
+void
+do_silmaril(void)
+{
+    int which;
+
+    /* Prompt for action */
+    msg("How do you wish to apply the Silamril of Ea (* for list)? ");
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    if (which < 0 || which > 2)
+    {
+        msg("");
+        add_line("[a] magic mapping");
+        add_line("[b] petrification");
+        add_line("[c] stairwell downwards");
+        end_line();
+
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > 2)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+            msg("");
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    switch (which)
+    {
+        case 0: read_scroll(&player, S_MAP, ISNORMAL);
+                break;
+        case 1: read_scroll(&player, S_PETRIFY, ISNORMAL);
+                break;
+        case 2: msg("A stairwell opens beneath your feet and you go down.");
+                level++;
+                new_level(NORMLEV,0);
+                break;
+        default:msg("What a strange thing to do!!");
+                break;
+    }
+}
+
+/*
+    do_amulet()
+        handle powers of the Amulet of Yendor
+*/
+
+void
+do_amulet(void)
+{
+    int which, limit;
+
+    /* Prompt for action */
+    msg("How do you wish to apply the Amulet of Yendor (* for list)? ");
+
+    limit = 0;
+
+    if (is_carrying(TR_PURSE))
+        limit += 1;
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    if (which < 0 || which > limit)
+    {
+        msg("");
+        add_line("[a] level evaluation");
+
+        if (limit >= 1)
+            add_line("[b] invisibility");
+
+        end_line();
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > limit)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+
+            msg("");
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    switch (which)
+    {
+        case 0: level_eval();
+                break;
+        case 1: quaff(&player, P_INVIS, ISNORMAL);
+                break;
+        default:msg("What a strange thing to do!!");
+                break;
+    }
+}
+
+/*
+    do_bag()
+        handle powers of the Magic Purse of Yendor as a bag of holding
+*/
+
+void
+do_bag(struct object *obj)
+{
+    int which, limit;
+
+    /* Prompt for action */
+    msg("How do you wish to apply the Magic Purse of Yendor (* for list)? ");
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    limit = 2;
+
+    if (is_carrying(TR_AMULET))
+        limit += 1;
+
+    if (which < 0 || which > limit)
+    {
+        msg("");
+        add_line("[a] inventory");
+        add_line("[b] add to bag");
+        add_line("[c] remove from bag");
+
+        if (limit >= 3)
+            add_line("[d] see invisible");
+
+        end_line();
+
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > limit)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+
+            msg("");
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    switch (which)
+    {
+        case 0:
+            inventory(obj->o_bag, 0);
+            break;
+
+        case 1:
+            {
+                object  *new_obj_p; /* what the user selected */
+
+                if ((new_obj_p = get_object(pack, "add", 0, NULL)) != NULL)
+                {
+                    rem_pack(new_obj_p);    /* free up pack slot */
+                    push_bag(&obj->o_bag, new_obj_p);
+                    pack_report(new_obj_p, MESSAGE, "You just added ");
+                }
+            }
+            break;
+
+        case 2:
+            {
+                object  *obj_p;
+                linked_list *item_p;
+
+                if ((obj_p=get_object(obj->o_bag,"remove",0,NULL)) != NULL)
+                {
+                    item_p = make_item(obj_p);  /* attach upper structure */
+
+                    if (add_pack(item_p, MESSAGE) != FALSE)
+                        pop_bag(&obj->o_bag, obj_p);
+                }
+            }
+            break;
+
+        case 3:
+            quaff(&player, P_TRUESEE, ISBLESSED);
+            break;
+
+        default:
+            msg("What a strange thing to do!!");
+    }
+}
+
+/*
+    do_sceptre()
+        handle powers of the Sceptre of Might
+*/
+
+void
+do_sceptre(void)
+{
+    int which, limit;
+
+    /* Prompt for action */
+    msg("How do you wish to apply the Sceptre of Might (* for list)? ");
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    limit = 5;
+
+    if (is_carrying(TR_CROWN))
+        limit += 1;
+
+    if (is_carrying(TR_PALANTIR))
+        limit += 1;
+
+    if (which < 0 || which > limit)
+    {
+        msg("");
+        add_line("[a] cancellation");
+        add_line("[b] polymorph monster");
+        add_line("[c] slow monster");
+        add_line("[d] teleport monster");
+        add_line("[e] monster confusion");
+        add_line("[f] paralyze monster");
+
+        if (limit >= 6)
+            add_line("[g] drain life");
+
+        if (limit >= 7)
+            add_line("[h] smell monster");
+
+        end_line();
+
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > limit)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+
+            msg("");
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    if (rnd(pstats.s_lvl) < 7)
+    {
+        msg("Your finger slips.");
+        which = rnd(6);
+        if (wizard)
+        {
+            msg("What wand? (%d)", which);
+
+            if (get_string(prbuf, cw) == NORM)
+            {
+                which = atoi(prbuf);
+                if (which < 0 || which > 5)
+                {
+                    msg("Invalid selection.");
+                    which = rnd(6);
+                    msg("Rolled %d.", which);
+                }
+            }
+        }
+    }
+
+    switch (which)
+    {
+        case 0:
+            if (get_dir())
+                do_zap(&player, WS_CANCEL, ISBLESSED);
+            break;
+
+        case 1:
+            if (get_dir())
+                do_zap(&player, WS_POLYMORPH, ISBLESSED);
+            break;
+
+        case 2:
+            if (get_dir())
+                do_zap(&player, WS_SLOW_M, ISBLESSED);
+            break;
+
+        case 3:
+            if (get_dir())
+                do_zap(&player, WS_MONSTELEP, ISBLESSED);
+            break;
+
+        case 4:
+            if (get_dir())
+                do_zap(&player, WS_CONFMON, ISBLESSED);
+            break;
+
+        case 5:
+            if (get_dir())
+                do_zap(&player, WS_PARALYZE, ISBLESSED);
+            break;
+
+        case 6:
+            if (get_dir())
+                do_zap(&player, WS_DRAIN, ISBLESSED);
+            break;
+
+        case 7:
+            quaff(&player, P_SMELL, ISBLESSED);
+            break;
+
+        default:
+            msg("What a strange thing to do!!");
+            break;
+    }
+}
+
+/*
+    do_wand()
+        handle powers of the Wand of Yendor
+*/
+
+void
+do_wand(void)
+{
+    int which, i;
+
+    /* Prompt for action */
+    msg("How do you wish to apply the Wand of Yendor (* for list)? ");
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    if (which < 0 || which >= maxsticks)
+    {
+        msg("");
+
+        for (i = 0; i < maxsticks; i++)
+        {
+            sprintf(prbuf, "[%c] %s", i + 'a', ws_magic[i].mi_name);
+            add_line(prbuf);
+        }
+
+        end_line();
+
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which >= maxsticks)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+
+            msg("");
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    if (rnd(pstats.s_lvl) < 12)
+    {
+        msg("Your finger slips.");
+        which = rnd(maxsticks);
+
+        if (wizard)
+        {
+            msg("What wand? (%d)", which);
+
+            if (get_string(prbuf, cw) == NORM)
+            {
+                which = atoi(prbuf);
+
+                if (which < 0 || which >= maxsticks)
+                {
+                    msg("Invalid selection.");
+                    which = rnd(maxsticks);
+                    msg("Rolled %d.", which);
+                }
+            }
+        }
+    }
+
+    if (get_dir())
+        do_zap(&player, which, ISBLESSED);
+}
+
+/*
+    do_crown()
+        handle powers of the Crown of Might
+*/
+
+void
+do_crown(void)
+{
+    int which, limit;
+
+    /* Prompt for action */
+    msg("How do you wish to apply the Crown of Might (* for list)? ");
+
+    which = (short) ((readchar() & 0177) - 'a');
+
+    if (which == (short) ESCAPE - (short) 'a')
+    {
+        after = FALSE;
+        return;
+    }
+
+    limit = 9;
+
+    if (is_carrying(TR_PALANTIR))
+        limit += 1;
+
+    if (is_carrying(TR_SCEPTRE))
+        limit += 1;
+
+    if (which < 0 || which > limit)
+    {
+        msg("");
+        add_line("[a] add strength");
+        add_line("[b] add intelligence");
+        add_line("[c] add wisdom");
+        add_line("[d] add dexterity");
+        add_line("[e] add constitution");
+        add_line("[f] normal strength");
+        add_line("[g] normal intelligence");
+        add_line("[h] normal wisdom");
+        add_line("[i] normal dexterity");
+        add_line("[j] normal constitution");
+
+        if (limit >= 10)
+            add_line("[k] disguise");
+
+        if (limit >= 11)
+            add_line("[l] super heroism");
+
+        end_line();
+
+        msg("Which power do you wish to use?");
+
+        which = (short) ((readchar() & 0177) - 'a');
+
+        while (which < 0 || which > limit)
+        {
+            if (which == (short) ESCAPE - (short) 'a')
+            {
+                after = FALSE;
+                return;
+            }
+            msg("");
+            msg("Please enter one of the listed powers: ");
+            which = (short) ((readchar() & 0177) - 'a');
+        }
+
+        msg("Your attempt is successful.");
+    }
+    else
+        msg("Your attempt is successful.");
+
+    switch (which)
+    {
+        case 0:
+            if (off(player, POWERSTR))
+            {
+                turn_on(player, POWERSTR);
+                chg_str(10, FALSE, FALSE);
+                msg("You feel much stronger now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 1:
+            if (off(player, POWERINTEL))
+            {
+                pstats.s_intel += 10;
+                turn_on(player, POWERINTEL);
+                msg("You feel much more intelligent now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 2:
+            if (off(player, POWERWISDOM))
+            {
+                pstats.s_wisdom += 10;
+                turn_on(player, POWERWISDOM);
+                msg("Your feel much wiser know.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 3:
+            if (off(player, POWERDEXT))
+            {
+                turn_on(player, POWERDEXT);
+                chg_dext(10, FALSE, FALSE);
+                msg("You feel much more dextrous now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 4:
+            if (off(player, POWERCONST))
+            {
+                pstats.s_const += 10;
+                turn_on(player, POWERCONST);
+                msg("You feel much healthier now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 5:
+            if (on(player, POWERSTR))
+            {
+                turn_off(player, POWERSTR);
+                chg_str(-10, FALSE, FALSE);
+                msg("Your muscles bulge less now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 6:
+            if (on(player, POWERINTEL))
+            {
+                pstats.s_intel = max(pstats.s_intel - 10,
+                             3 + ring_value(R_ADDINTEL));
+                turn_off(player, POWERINTEL);
+                msg("You feel less intelligent now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 7:
+            if (on(player, POWERWISDOM))
+            {
+                pstats.s_wisdom = max(pstats.s_wisdom - 10,
+                              3 + ring_value(R_ADDWISDOM));
+                turn_off(player, POWERWISDOM);
+                msg("You feel less wise now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 8:
+            if (on(player, POWERDEXT))
+            {
+                turn_off(player, POWERDEXT);
+                chg_dext(-10, FALSE, FALSE);
+                msg("You feel less dextrous now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 9:
+            if (on(player, POWERCONST))
+            {
+                pstats.s_const -= 10;
+                turn_off(player, POWERCONST);
+                msg("You feel less healthy now.");
+            }
+            else
+                nothing_message(ISCURSED);
+            break;
+
+        case 10: quaff(&player, P_DISGUISE, ISNORMAL);
+            break;
+
+        case 11: quaff(&player, P_SHERO, ISNORMAL);
+            break;
+
+        default:
+            msg("What a strange thing to do!!");
+            break;
+
+    }
+}
+
+/*
+    level_eval()
+        have amulet evaluate danger on this level
+*/
+
+void
+level_eval(void)
+{
+    int cnt = 0;
+    long max_nasty = 0;
+    struct linked_list  *item;
+    struct thing    *tp;
+    char    *colour, *temp;
+
+    for (item = mlist; item != NULL; item = next(item))
+    {
+        tp = THINGPTR(item);
+        cnt++;
+        max_nasty = max(max_nasty,(10L-tp->t_stats.s_arm) * tp->t_stats.s_hpt);
+    }
+
+    if (cnt < 3)
+        colour = "black";
+    else if (cnt < 6)
+        colour = "red";
+    else if (cnt < 9)
+        colour = "orange";
+    else if (cnt < 12)
+        colour = "yellow";
+    else if (cnt < 15)
+        colour = "green";
+    else if (cnt < 18)
+        colour = "blue";
+    else if (cnt < 25)
+        colour = "violet";
+    else
+        colour = "pink with purple polka dots";
+
+    if (max_nasty < 10)
+        temp = "feels cold and lifeless";
+    else if (max_nasty < 30)
+        temp = "feels cool";
+    else if (max_nasty < 200)
+        temp = "feels warm and soft";
+    else if (max_nasty < 1000)
+        temp = "feels warm and slippery";
+    else if (max_nasty < 5000)
+        temp = "feels hot and dry";
+    else if (max_nasty < 10000)
+        temp = "feels too hot to hold";
+    else if (max_nasty < 20000)
+        temp = "burns your hand";
+    else
+        temp = "jumps up and down shrieking 'DANGER! DANGER'";
+
+    msg("The amulet glows %s and %s.", colour, temp);
+
+    return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/bag.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,448 @@
+/*
+    bag.c  -  functions for dealing with bags
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+/*
+ * new bag functions
+ *
+ * This is a simple version of bag.c that uses linked lists to perform the bag
+ * functions. The bag is just a linked list of objects (struct object) to be
+ * specific, but most of that is supposed to be hidden from the user, who
+ * should access the bag only through the functions presented here.
+ */
+
+#include <stdlib.h>
+#include "rogue.h"
+
+/*
+ * apply_to_bag
+ *
+ * This is the general bag manipulation routine. The bag is subjected to
+ * selection criteria and those objects which pass are processed by an action
+ * routine. The two criteria are type and filter function. The filter
+ * function returns TRUE if the object passes and FALSE otherwise. The filter
+ * function is passed the object and the user-supplied argument. This gives
+ * the user plenty of flexibility in determining which items will be
+ * processed. The action routine is passed the object, the id, and the
+ * user-supplied argument given to apply_to_bag. Specifying NULL for either
+ * the type or filter function means that criterion always selects. A NULL
+ * action routine means no processing is done and the first object which
+ * passes the filter is returned to the user. The action routine returns TRUE
+ * if processing should continue or FALSE if the current item should be
+ * returned to the caller.
+ *
+ * Returns NULL if the bag is empty or if nothing qualified.
+ *
+ * linked_list *bag_p;       // linked list of objects
+ * int          type;        // what is its type (ARMOR, ...)
+ * int        (*bff_p)();    // bag filter function
+ * int        (*baf_p)();    // bag action routine
+ * long         user_arg;    // user argument for filter, action
+ *
+ */
+
+struct object *
+apply_to_bag(struct linked_list *bag_p,
+             int type,
+             int (*bff_p)(struct object *obj, bag_arg *user_arg),
+             int (*baf_p)(struct object *obj, bag_arg *user_arg, int id),
+             void *user_arg)
+{
+    struct object *bag_obj_p = NULL;  /* qualifying object */
+    struct object *cur_obj_p;         /* current object */
+    bag_arg arg;
+
+    arg.varg = user_arg;
+
+    if (bag_p == NULL)
+        return (NULL);
+
+    for (; bag_p != NULL; bag_p = next(bag_p))
+    {
+        cur_obj_p = OBJPTR(bag_p);
+
+        if (type != 0 && type != cur_obj_p->o_type)
+            continue;
+
+        if (bff_p != NULL && !(*bff_p)(cur_obj_p, &arg))
+            continue;
+
+        /*
+         * At this point, we have an object which qualifies for
+         * processing
+         */
+
+        bag_obj_p = cur_obj_p;  /* in case the user wants it */
+
+        if (baf_p != NULL && (*baf_p)(cur_obj_p, &arg, identifier(bag_obj_p)))
+            continue;
+
+        /*
+         * We have an object which qualifies, quit now!
+         */
+
+        break;
+    }
+
+    if (bag_p == NULL)
+        return (NULL);
+
+    return (bag_obj_p);
+}
+
+/*
+    count_bag()
+
+        Counts up all bag items which meet the selection criteria
+*/
+
+int
+count_bag(linked_list *bag_p,
+          int type,
+          int (*bff_p)(struct object *obj, bag_arg *junk))
+{
+    int cnt = 0;
+    apply_to_bag(bag_p, type, bff_p, baf_increment, &cnt);
+
+    return(cnt);
+}
+
+/*
+    del_bag()
+
+        Removes an object from a bag and throws it away.
+*/
+
+void
+del_bag(linked_list *bag_p, object *obj_p)
+{
+    pop_bag(&bag_p, obj_p);  /* get the thing from the bag */
+    ur_free(obj_p);         /* release the memory */
+}
+
+/*
+    pop_bag()
+
+        Removes an item from a bag and returns it to the user. If the item is
+        not in the bag, return NULL.
+*/
+
+struct object *
+pop_bag(linked_list **bag_pp, object *obj_p)
+{
+    linked_list *item_p;
+
+    for (item_p = *bag_pp; item_p != NULL && OBJPTR(item_p) != obj_p;
+         item_p = next(item_p));
+
+    if (item_p == NULL)
+        return (NULL);
+
+    _detach(bag_pp, item_p);
+
+    return (obj_p);
+}
+
+/*
+    push_bag()
+
+        stuff another item into the bag
+*/
+
+void
+push_bag(linked_list **bag_pp, object *obj_p)
+{
+    struct linked_list *item_p = NULL;
+    struct linked_list *new_p  = NULL;
+    struct linked_list *best_p = NULL;
+
+    new_p             = new_list();
+    new_p->data.obj   = obj_p;              /* attach our object   */
+    identifier(obj_p) = get_ident(obj_p);   /* tag this object for */
+                                            /*     inventory       */
+    /*
+     * Find a place in the bag - try to match the type, then sort by
+     * identifier
+     */
+
+    for (item_p = *bag_pp; item_p != NULL; item_p = next(item_p))
+    {
+        if ((OBJPTR(item_p))->o_type == obj_p->o_type)
+        {
+            if (best_p == NULL)
+                best_p = item_p;
+            else if (identifier((OBJPTR(item_p))) >
+                     identifier((OBJPTR(best_p))) &&
+                     identifier((OBJPTR(item_p))) <
+                     identifier(obj_p))
+                best_p = item_p;
+        }
+    }
+
+    _attach_after(bag_pp, best_p, new_p);   /* stuff it in the list */
+
+    return;
+}
+
+/*
+    scan_bag()
+
+        Gets the object from the bag that matches the type and id. The object
+        is not removed from the bag.
+*/
+
+struct object *
+scan_bag(linked_list *bag_p, int type, int id)
+{
+    object  *obj_p = NULL;
+
+    for (; bag_p != NULL; bag_p = next(bag_p))
+    {
+        obj_p = OBJPTR(bag_p);
+
+        if (obj_p->o_type == type && identifier(obj_p) == id)
+            break;
+    }
+
+    if (bag_p == NULL)
+        return(NULL);
+
+    return(obj_p);
+}
+
+/*
+    baf_decrement_test()
+
+        Assumes the argument is a pointer to int and it just decrements it.
+        Returns TRUE, except when the count goes to zero.
+*/
+
+int
+baf_decrement_test(struct object *obj_p, bag_arg *count_p, int id)
+{
+    NOOP(obj_p);
+    NOOP(id);
+
+    if (*count_p->iarg > 0)
+        return(TRUE);
+
+    return(FALSE);
+}
+
+/*
+    baf_identify()
+
+        Bag action function to identify an object. This is needed to conform
+        to bag action routine calling conventions and to put the linked list
+        structure on top of the object before calling whatis()
+*/
+
+int
+baf_identify(struct object *obj_p, bag_arg *junk, int id)
+{
+    linked_list l;
+    linked_list *lp = &l;
+
+    NOOP(junk);
+    NOOP(id);
+
+    lp->data.obj = obj_p;    /* stuff object in the right place */
+    whatis(lp);
+
+    return(TRUE);
+}
+
+/*
+    baf_increment()
+
+        Assumes the argument is a pointer to int and it just increments it and
+        returns TRUE
+*/
+
+int
+baf_increment(object *obj_p, bag_arg *count_p, int id)
+{
+    NOOP(obj_p);
+    NOOP(id);
+
+    (*count_p->iarg)++;
+
+    return(TRUE);
+}
+
+/*
+    baf_print_item()
+        Bag action function to print a single item, inventory style.
+*/
+
+int
+baf_print_item(struct object *obj_p, bag_arg *type, int id)
+{
+    char inv_temp[3 * LINELEN];  /* plenty of space for paranoid programmers */
+
+    if (*type->iarg == 0)
+        sprintf(inv_temp, "%c%c) %s", obj_p->o_type,
+              print_letters[id], inv_name(obj_p, LOWERCASE), FALSE);
+    else
+        sprintf(inv_temp, "%c) %s", print_letters[id],
+            inv_name(obj_p, LOWERCASE), FALSE);
+
+    add_line(inv_temp);
+    return(TRUE);
+}
+
+/*
+    bff_group()
+        This bag filter function checks to see if two items can be combined by
+        adjusting the count. Grouped items can be combined if the group numbers
+        match. The only other item that is allowed to have a count is food, and
+        there an exact match is required.
+*/
+
+int
+bff_group(struct object *obj_p, bag_arg *arg)
+{
+    struct object *new_obj_p = arg->obj;
+
+    if (new_obj_p->o_group > 0 && new_obj_p->o_group == obj_p->o_group)
+        return(TRUE);
+
+    if (new_obj_p->o_type == FOOD &&
+        obj_p->o_type == new_obj_p->o_type &&
+        obj_p->o_which == new_obj_p->o_which)
+        return(TRUE);
+
+    return(FALSE);
+}
+
+/*
+    bff_callable
+        Figures out which items can be callable: current rules are:
+            potions, scrolls, staffs, and rings.
+*/
+
+int
+bff_callable(struct object *obj_p, bag_arg *junk)
+{
+    NOOP(junk);
+
+    if (obj_p->o_type == POTION || obj_p->o_type == RING ||
+        obj_p->o_type == STICK || obj_p->o_type == SCROLL)
+        return(TRUE);
+
+    return(FALSE);
+}
+
+/*
+    bff_markable()
+        Selects which items can be marked. Current rules exclude only gold.
+*/
+
+int
+bff_markable(struct object *obj_p, bag_arg *junk)
+{
+    NOOP(junk);
+
+    if (obj_p->o_type == GOLD)
+        return(FALSE);
+
+    return(TRUE);
+}
+
+/*
+    bffron()
+        returns TRUE if hero is wearing this ring
+*/
+
+int
+bffron(object *obj_p, bag_arg *junk)
+{
+    NOOP(junk);
+
+    return(cur_ring[LEFT_1] == obj_p || cur_ring[LEFT_2] == obj_p ||
+        cur_ring[LEFT_3] == obj_p || cur_ring[LEFT_4] == obj_p ||
+        cur_ring[LEFT_5] ||
+        cur_ring[RIGHT_1] == obj_p || cur_ring[RIGHT_2] == obj_p ||
+        cur_ring[RIGHT_3] == obj_p || cur_ring[RIGHT_4] == obj_p ||
+        cur_ring[RIGHT_5]);
+}
+
+/*
+    bff_zappable()
+        Selects which items can be zapped. This includes both sticks and
+        magically enhanced weapons with lightning ability.
+*/
+
+int
+bff_zappable(struct object *obj_p, bag_arg *junk)
+{
+    NOOP(junk);
+
+    if (obj_p->o_type == STICK)
+        return(TRUE);
+
+    if (obj_p->o_type == WEAPON && obj_p->o_flags & ISZAPPED)
+        return(TRUE);
+
+    return (FALSE);
+}
+
+/*
+    baf_curse()
+        Curse all non-artifact items in the player's pack
+*/
+
+int
+baf_curse(struct object *obj_p, bag_arg *junk, int id)
+{
+    NOOP(junk);
+    NOOP(id);
+
+    if (obj_p->o_type != ARTIFACT && rnd(8) == 0)
+    {
+        obj_p->o_flags |= ISCURSED;
+        obj_p->o_flags &= ~ISBLESSED;
+    }
+
+    return(TRUE);
+}
+
+/*
+    bafcweapon()
+        bag action routine to fetch the current weapon
+*/
+
+int
+bafcweapon(struct object *obj_p, bag_arg *junk, int id)
+{
+    NOOP(junk);
+    NOOP(id);
+
+    if (obj_p == cur_weapon)
+        return(FALSE); /* found what we wanted - stop and return it */
+
+    return(TRUE);
+}
+
+/*
+    bafcarmor()
+        bag action routine to fetch the current armor
+*/
+
+int
+bafcarmor(struct object *obj_p, bag_arg *junk, int id)
+{
+    NOOP(junk);
+    NOOP(id);
+
+    if (obj_p == cur_armor)
+        return(FALSE); /* found what we wanted - stop and return it */
+
+    return(TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/chase.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,1326 @@
+/*
+    chase.c  -  Code for one creature to chase another
+ 
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    Based on "Rogue: Exploring the Dungeons of Doom"
+    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include "rogue.h"
+
+/*
+    do_chase()
+        Make one thing chase another.
+*/
+
+void
+do_chase(struct thing *th, int flee)
+{
+    struct room    *rer;        /* room of chaser */
+    struct room    *ree;        /* room of chasee */
+    struct room    *old_room;   /* old room of monster */
+    struct room    *new_room;   /* new room of monster */
+
+    int i, mindist = INT_MAX, maxdist = INT_MIN, dist = INT_MIN;
+
+    int last_door = -1;     /* Door we just came from */
+    int stoprun = FALSE;    /* TRUE means we are there */
+    int rundoor;            /* TRUE means run to a door */
+    int hit_bad = FALSE;    /* TRUE means hit bad monster */
+    int mon_attack;         /* TRUE means find a monster to hit */
+
+    char    sch;
+    struct linked_list *item;
+    coord   this;           /* Temporary destination for chaser */
+
+    if (!th->t_ischasing)
+        return;
+
+    /* Make sure the monster can move */
+
+    if (th->t_no_move != 0)
+    {
+        th->t_no_move--;
+        return;
+    }
+
+    /*
+     * Bad monsters check for a good monster to hit, friendly monsters
+     * check for a bad monster to hit.
+     */
+
+    mon_attack = FALSE;
+
+    if (good_monster(*th))
+    {
+        hit_bad = TRUE;
+        mon_attack = TRUE;
+    }
+    else if (on(*th, ISMEAN))
+    {
+        hit_bad = FALSE;
+        mon_attack = TRUE;
+    }
+
+    if (mon_attack)
+    {
+        struct linked_list  *mon_to_hit;
+
+	mon_to_hit = f_mons_a(th->t_pos.y, th->t_pos.x, hit_bad);
+
+        if (mon_to_hit)
+        {
+            mon_mon_attack(th, mon_to_hit, pick_weap(th), NOTHROWN);
+            return;
+        }
+    }
+	
+    /* no nearby monster to hit */
+	
+    rer = roomin(th->t_pos);            /* Find room of chaser */
+    ree = roomin(th->t_chasee->t_pos);  /* Find room of chasee */
+
+    /*
+     * We don't count doors as inside rooms for this routine
+     */
+
+    if (mvwinch(stdscr, th->t_pos.y, th->t_pos.x) == DOOR)
+        rer = NULL;
+
+    this = th->t_chasee->t_pos;
+
+    /*
+     * If we are not in a corridor and not a phasing monster, then if we
+     * are running after the player, we run to a door if he is not in the
+     * same room. If we are fleeing, we run to a door if he IS in the
+     * same room.  Note:  We don't bother with doors in mazes. Phasing 
+     * monsters don't need to look for doors. There are no doors in mazes
+     * and throne rooms. 
+     */
+
+    if (levtype != MAZELEV && levtype != THRONE && rer != NULL && off(*th, CANINWALL))
+    {
+        if (flee)
+            rundoor = (rer == ree);
+        else
+            rundoor = (rer != ree);
+    }
+    else
+        rundoor = FALSE;
+
+    if (rundoor)
+    {
+        coord   d_exit;   /* A particular door */
+        int exity, exitx;   /* Door's coordinates */
+
+        if (th->t_doorgoal != -1)
+        { /* Do we already have the goal? */
+            this = rer->r_exit[th->t_doorgoal];
+            dist = 0;   /* Indicate that we have our door */
+        }
+        else
+            for (i = 0; i < rer->r_nexits; i++)
+            {   /* Loop through doors */
+                d_exit = rer->r_exit[i];
+                exity = d_exit.y;
+                exitx = d_exit.x;
+
+                /* Avoid secret doors */
+                if (mvwinch(stdscr, exity, exitx) == DOOR)
+                {
+                    /* Were we just on this door? */
+                    if (ce(d_exit, th->t_oldpos))
+                        last_door = i;
+                    else
+                    {
+                        dist = DISTANCE(th->t_chasee->t_pos, d_exit);
+
+                        /*
+                         * If fleeing, we want to
+                         * maximize distance from
+                         * door to what we flee, and
+                         * minimize distance from
+                         * door to us.
+                         */
+
+                        if (flee)
+                            dist-=DISTANCE(th->t_pos,d_exit);
+
+                        /*
+                         * Maximize distance if
+                         * fleeing, otherwise
+                         * minimize it
+                         */
+
+                        if ((flee && (dist > maxdist)) ||
+                            (!flee && (dist < mindist)))
+                        {
+                            th->t_doorgoal = i; /* Use this door */
+                            this = d_exit;
+                            mindist = maxdist = dist;
+                        }
+                    }
+                }
+            }
+
+        /* Could we not find a door? */
+        if (dist == INT_MIN)
+        {
+            /* If we were on a door, go ahead and use it */
+            if (last_door != -1)
+            {
+                th->t_doorgoal = last_door;
+                this = th->t_oldpos;
+                dist = 0;   /* Indicate that we found a door */
+            }
+        }
+
+        /* Indicate that we do not want to flee from the door */
+        if (dist != INT_MIN)
+            flee = FALSE;
+    }
+    else
+        th->t_doorgoal = -1;    /* Not going to any door */
+
+    /*
+     * this now contains what we want to run to this time so we run to
+     * it.  If we hit it we either want to fight it or stop running
+     */
+
+    if (!chase(th, &this, flee))
+    {
+        if (ce(th->t_nxtpos, hero))
+        {
+            /* merchants try to sell something */
+
+            if (on(*th, CANSELL))
+            {
+                sell(th);
+                return;
+            }
+            else if (off(*th, ISFRIENDLY) && off(*th, ISCHARMED)
+                    && (off(*th, CANFLY) || (on(*th, CANFLY) && rnd(2))))
+                    attack(th, pick_weap(th), FALSE);
+                return;
+        }
+        else if (on(*th, NOMOVE))
+            stoprun = TRUE;
+    }
+
+    if (!curr_mons)
+        return;     /* Did monster get itself killed? */
+
+    if (on(*th, NOMOVE))
+        return;
+
+    /* If we have a scavenger, it can pick something up */
+
+    if ((item = find_obj(th->t_nxtpos.y, th->t_nxtpos.x)) != NULL)
+    {
+		struct linked_list *node, *top = item;
+        struct object *obt;
+		
+		while(top)
+		{
+			/* grab all objects that qualify */
+			
+			struct object *obj = OBJPTR(item);
+			
+			obt = OBJPTR(top);
+			node = obt->next_obj;
+			
+			if (on(*th, ISSCAVENGE) ||
+                ((on(*th, CANWIELD) || on(*th, CANSHOOT)) &&
+                (obj->o_type == WEAPON || obj->o_type == ARMOR)) ||
+                (on(*th, CANCAST) && is_magic(obj))) 
+			{
+                rem_obj(top, FALSE);
+                attach(th->t_pack, top);
+            }
+			
+			top = node;
+		}
+		
+		light(&hero);
+    }
+
+    mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch);
+    sch = CCHAR( mvwinch(cw, th->t_nxtpos.y, th->t_nxtpos.x) );
+
+    /* Get old and new room of monster */
+    old_room = roomin(th->t_pos);
+    new_room = roomin(th->t_nxtpos);
+
+    /* If the monster can illuminate rooms, check for a change */
+    if (on(*th, HASFIRE))
+    {
+        /* Is monster entering a room? */
+        if (old_room != new_room && new_room != NULL)
+        {
+            new_room->r_flags |= HASFIRE;
+            new_room->r_fires++;
+            if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && new_room->r_fires==1)
+                light(&hero);
+        }
+
+        /* Is monster leaving a room? */
+        if (old_room != new_room && old_room != NULL)
+        {
+            if (--(old_room->r_fires) <= 0)
+            {
+                old_room->r_flags &= ~HASFIRE;
+                if (cansee(th->t_pos.y, th->t_pos.x))
+                    light(&th->t_pos);
+            }
+        }
+    }
+
+    /*
+     * If monster is entering player's room and player can see it, stop
+     * the player's running.
+     */
+
+    if (new_room != old_room && new_room != NULL &&
+        new_room == ree && cansee(th->t_nxtpos.y, th->t_nxtpos.x) &&
+        (off(*th, ISINVIS) || (off(*th, ISSHADOW) || rnd(10) == 0) ||
+         on(player, CANSEE)) && off(*th, CANSURPRISE))
+        running = FALSE;
+
+    if (rer != NULL && (rer->r_flags & ISDARK) &&
+        !(rer->r_flags & HASFIRE) && sch == FLOOR &&
+         DISTANCE(th->t_nxtpos, th->t_pos) < see_dist &&
+        off(player, ISBLIND))
+        th->t_oldch = ' ';
+    else
+        th->t_oldch = sch;
+
+    if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) &&
+      off(*th, ISINWALL) &&
+      ((off(*th, ISINVIS) && (off(*th, ISSHADOW) || rnd(100) < 10)) ||
+      on(player, CANSEE)) &&
+      off(*th, CANSURPRISE))
+        mvwaddch(cw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type);
+
+    mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');
+    mvwaddch(mw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type);
+
+    /* Record monster's last position (if new one is different) */
+
+    if (!ce(th->t_nxtpos, th->t_pos))
+        th->t_oldpos = th->t_pos;
+
+    th->t_pos = th->t_nxtpos; /* Mark the monster's new position */
+
+    /* If the monster is on a trap, trap it */
+
+    sch = CCHAR(mvinch(th->t_nxtpos.y, th->t_nxtpos.x));
+
+    if (isatrap(sch))
+    {
+        debug("Monster trapped by %c.", sch);
+
+        if (cansee(th->t_nxtpos.y, th->t_nxtpos.x))
+            th->t_oldch = sch;
+
+        be_trapped(th, th->t_nxtpos);
+    }
+
+    /* And stop running if need be */
+
+    if (stoprun && ce(th->t_pos, th->t_chasee->t_pos))
+    {
+        th->t_ischasing = FALSE;
+        turn_off(*th, ISRUN);
+    }
+}
+
+/*
+    chase_it()
+        Set a monster running after something or stop it from running (for
+        when it dies)
+*/
+
+void
+chase_it(coord *runner, struct thing *th)
+{
+    struct linked_list  *item;
+    struct thing    *tp;
+
+    /* If we couldn't find him, something is funny */
+
+    if ((item = find_mons(runner->y, runner->x)) == NULL)
+    {
+        debug("CHASER '%s'", unctrl(winat(runner->y, runner->x)));
+        return;
+    }
+
+    tp = THINGPTR(item);
+
+    /* Start the beastie running */
+
+    tp->t_ischasing = TRUE;
+    tp->t_chasee    = th;
+
+    turn_on(*tp, ISRUN);
+    turn_off(*tp, ISDISGUISE);
+
+    return;
+}
+
+/*
+    chase()
+        Find the spot for the chaser(er) to move closer to the chasee(ee).
+        Returns TRUE if we want to keep on chasing later, FALSE if we reach the
+        goal.
+*/
+
+int
+chase(struct thing *tp, coord *ee, int flee)
+{
+    int x, y;
+    int dist, thisdist, monst_dist = INT_MAX;
+    struct linked_list  *weapon;
+    coord   *er = &tp->t_pos;
+    coord shoot;
+    coord *shootit_dir = NULL;
+    int ch;
+    char   mch;
+    int    next_player = FALSE;
+
+    /* Take care of shooting directions */
+
+    if (on(*tp, CANBREATHE) || on(*tp, CANSHOOT) || on(*tp, CANCAST))
+    {
+        if (good_monster(*tp))
+        {
+            shootit_dir = find_shoot(tp, &shoot); /* find a mean monster */
+
+            if (wizard && shootit_dir)
+                msg("Found monster to attack towards (%d,%d).",
+                    shootit_dir->x, shootit_dir->y);
+        }
+        else
+            shootit_dir = can_shoot(er, ee, &shoot);  /* shoot hero */
+    }
+
+    /*
+     * If the thing is confused, let it move randomly. Some monsters are
+     * slightly confused all of the time.
+     */
+
+    if ((on(*tp, ISHUH) && rnd(10) < 8) ||
+        ((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) ||
+        (on(player, ISINVIS) && off(*tp, CANSEE)))
+    {   /* Player is invisible */
+
+        /* get a valid random move */
+
+        tp->t_nxtpos = rndmove(tp);
+
+        dist = DISTANCE(tp->t_nxtpos, *ee);
+
+        if (on(*tp, ISHUH) && rnd(20) == 0) /* monster might lose confusion */
+            turn_off(*tp, ISHUH);
+
+        /*
+         * check to see if random move takes creature away from
+         * player if it does then turn off ISHELD
+         */
+
+        if (dist > 1 && on(*tp, DIDHOLD))
+        {
+            turn_off(*tp, DIDHOLD);
+            turn_on(*tp, CANHOLD);
+
+            if (--hold_count == 0)
+                turn_off(player, ISHELD);
+        }
+    } /* If we can breathe, we may do so */
+    else if (on(*tp, CANBREATHE) && (shootit_dir) && (rnd(100) < 67) &&
+         (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) &&
+         (DISTANCE(*er, *ee) < BOLT_LENGTH * BOLT_LENGTH))
+    {
+        int   chance;
+        char    *breath;
+
+        /* Will it breathe at random */
+
+        if (on(*tp, CANBRANDOM))
+        {
+            if (rnd(level / 20) == 0 && tp->t_index != nummonst + 1
+                && !(good_monster(*tp)))
+                turn_off(*tp, CANBRANDOM);
+
+            /* Select type of breath */
+
+            chance = rnd(100);
+
+            if (chance < 11)
+                breath = "acid";
+            else if (chance < 22)
+                breath = "flame";
+            else if (chance < 33)
+                breath = "lightning bolt";
+            else if (chance < 44)
+                breath = "chlorine gas";
+            else if (chance < 55)
+                breath = "ice";
+            else if (chance < 66)
+                breath = "nerve gas";
+            else if (chance < 77)
+                breath = "sleeping gas";
+            else if (chance < 88)
+                breath = "slow gas";
+            else
+                breath = "fear gas";
+        } /* Or can it breathe acid? */
+        else if (on(*tp, CANBACID))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBACID);
+
+            breath = "acid";
+        } /* Or can it breathe fire */
+        else if (on(*tp, CANBFIRE))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBFIRE);
+
+            breath = "flame";
+        } /* Or can it breathe electricity? */
+        else if (on(*tp, CANBBOLT))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBBOLT);
+
+            breath = "lightning bolt";
+        } /* Or can it breathe gas? */
+        else if (on(*tp, CANBGAS))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBGAS);
+
+            breath = "chlorine gas";
+        } /* Or can it breathe ice? */
+        else if (on(*tp, CANBICE))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBICE);
+
+            breath = "ice";
+        }
+        else if (on(*tp, CANBPGAS))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBPGAS);
+
+            breath = "nerve gas";
+        }
+        else if (on(*tp, CANBSGAS))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBSGAS);
+
+            breath = "sleeping gas";
+        }
+        else if (on(*tp, CANBSLGAS))
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBSLGAS);
+
+            breath = "slow gas";
+        }
+        else
+        {
+            if (!good_monster(*tp) && rnd(level / 15) == 0)
+                turn_off(*tp, CANBFGAS);
+
+            breath = "fear gas";
+        }
+
+        shoot_bolt(tp, *er, *shootit_dir, (tp == THINGPTR(fam_ptr)),
+               tp->t_index, breath, roll(tp->t_stats.s_lvl, 6));
+
+        tp->t_nxtpos = *er;
+
+        dist = DISTANCE(tp->t_nxtpos, *ee);
+
+        if (!curr_mons)
+            return (TRUE);
+    }
+    else if (shootit_dir && on(*tp, CANCAST) &&
+         (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)))
+    {
+        /*
+            If we can cast spells we might do so - even if adjacent fleeing
+            monsters are restricted to certain spells
+        */
+
+        incant(tp, *shootit_dir);
+        tp->t_nxtpos = *er;
+        dist = DISTANCE(tp->t_nxtpos, *ee);
+    }
+    else if (shootit_dir && on(*tp, CANSHOOT)) 
+    {
+	weapon = get_hurl(tp);
+	
+	if (weapon &&
+         (off(*tp, ISFLEE) || rnd(DISTANCE(*er, *ee)) > 2) &&
+         (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)))
+	{
+	    /*
+	        Should we shoot or throw something? fleeing monsters 
+		may to shoot anyway if far enough away
+	    */
+
+	    missile(shootit_dir->y, shootit_dir->x, weapon, tp);
+	    tp->t_nxtpos = *er;
+	    dist = DISTANCE(tp->t_nxtpos, *ee);
+	}
+    }
+    else
+    {
+        /*
+            Otherwise, find the empty spot next to the chaser that is closest
+            to the chasee.
+        */
+        int ey, ex;
+        struct room *rer, *ree;
+        int dist_to_old = INT_MIN;   /* Dist from goal to old position */
+
+        /* Get rooms */
+        rer = roomin(*er);
+        ree = roomin(*ee);
+
+        /*
+         * This will eventually hold where we move to get closer. If
+         * we can't find an empty spot, we stay where we are.
+         */
+
+        dist = flee ? 0 : INT_MAX;
+        tp->t_nxtpos = *er;
+
+        /* Are we at our goal already? */
+
+        if (!flee && ce(tp->t_nxtpos, *ee))
+            return (FALSE);
+
+        ey = er->y + 1;
+        ex = er->x + 1;
+
+        for (x = er->x - 1; x <= ex; x++)
+            for (y = er->y - 1; y <= ey; y++)
+            {
+                coord   tryp; /* test position */
+
+                /* Don't try off the screen */
+
+                if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2))
+                    continue;
+
+                /*
+                 * Don't try the player if not going after
+                 * the player or he's disguised and monster is dumb
+                 */
+
+                if (((off(*tp, ISFLEE) && !ce(hero, *ee)) ||
+                     (on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6))
+                     || good_monster(*tp))
+                    && x == hero.x && y == hero.y)
+                    continue;
+
+                tryp.x = x;
+                tryp.y = y;
+
+                /*
+                 * Is there a monster on this spot closer to
+                 * our goal? Don't look in our spot or where
+                 * we were.
+                 */
+
+                if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
+                    isalpha( (mch = CCHAR(mvwinch(mw, y, x))) ) )
+                {
+                    int test_dist;
+
+                    test_dist = DISTANCE(tryp,*ee);
+                    if (test_dist <= 25 &&  /* Let's be fairly close */
+                        test_dist < monst_dist)
+                    {
+
+                        /* Could we really move there? */
+
+                        mvwaddch(mw, y, x, ' '); /* Temp blank monst */
+
+                        if (diag_ok(er, &tryp, tp))
+                            monst_dist = test_dist;
+
+                        mvwaddch(mw, y, x, mch);    /* Restore monster */
+                    }
+                }
+
+                if (!diag_ok(er, &tryp, tp))
+                    continue;
+
+                ch = mvwinch(cw, y, x); /* Screen character */
+
+                /*
+                 * Stepping on player is NOT okay if we are
+                 * fleeing
+                 */
+
+                if (on(*tp, ISFLEE) && (ch == PLAYER))
+				    next_player = TRUE;
+					
+                if (step_ok(y, x, NOMONST, tp) &&
+                    (off(*tp, ISFLEE) || ch != PLAYER))
+                {
+
+                    /*
+                     * If it is a trap, an intelligent
+                     * monster may not step on it (unless
+                     * our hero is on top!)
+                     */
+
+                    if (isatrap(ch))
+                    {
+                        if (!(ch == RUSTTRAP) &&
+                            !(ch == FIRETRAP && on(*tp, NOFIRE)) &&
+                            rnd(10) < tp->t_stats.s_intel &&
+                        (y != hero.y || x != hero.x))
+                            continue;
+                    }
+
+                    /*
+                     * OK -- this place counts
+                     */
+
+                    thisdist = DISTANCE(tryp, *ee);
+					
+                    /*
+                     * Adjust distance if we are being
+                     * shot at to moving out of line of sight.
+                     */
+
+                    if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
+                        ce(hero, *ee))
+                    {
+                        /* Move out of line of sight */
+                        if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL))
+                        {
+                            if (flee)
+                                thisdist -= SHOTPENALTY;
+                            else
+                                thisdist += SHOTPENALTY;
+                        }
+
+                        /*
+                         * But do we want to leave
+                         * the room?
+                         */
+                        else if (rer && rer == ree && ch == DOOR)
+                            thisdist += DOORPENALTY;
+                    }
+
+                    /*
+                     * Don't move to the last position if
+                     * we can help it
+                     */
+
+                    if (ce(tryp, tp->t_oldpos))
+                        dist_to_old = thisdist;
+                    else if ((flee && (thisdist > dist)) ||
+                         (!flee && (thisdist < dist)))
+                    {
+                        tp->t_nxtpos = tryp;
+                        dist = thisdist;
+                    }
+                }
+            }
+
+        /*
+         * If we are running from the player and he is in our way, go
+         * ahead and slug him.
+         */
+
+        if (next_player && DISTANCE(*er,*ee) < dist &&
+            step_ok(tp->t_chasee->t_pos.y, tp->t_chasee->t_pos.x, NOMONST, tp))
+        {
+            tp->t_nxtpos = tp->t_chasee->t_pos;    /* Okay to hit player */
+            return(FALSE);
+        }
+
+
+        /*
+         * If we can't get closer to the player (if that's our goal)
+         * because other monsters are in the way, just stay put
+         */
+
+        if (!flee && ce(hero, *ee) && monst_dist < INT_MAX &&
+            DISTANCE(*er, hero) < dist)
+            tp->t_nxtpos = *er;
+
+        /* Do we want to go back to the last position? */
+        else if (dist_to_old != INT_MIN &&   /* It is possible to move back */
+             ((flee && dist == 0) ||        /* No other possible moves */
+              (!flee && dist == INT_MAX)))
+        {
+            /* Do we move back or just stay put (default)? */
+
+            dist = DISTANCE(*er,*ee); /* Current distance */
+
+            if (!flee || (flee && (dist_to_old > dist)))
+                tp->t_nxtpos = tp->t_oldpos;
+        }
+    }
+
+    /* Make sure we have the real distance now */
+    dist = DISTANCE(tp->t_nxtpos, *ee);
+
+    /* Mark monsters in a wall */
+
+    switch(mvinch(tp->t_nxtpos.y, tp->t_nxtpos.x))
+    {
+        case WALL:
+        case '-':
+        case '|':
+            turn_on(*tp, ISINWALL);
+            break;
+        default:
+            turn_off(*tp, ISINWALL);
+    }
+
+    if (off(*tp, ISFLEE) &&
+        !(!SAME_POS((tp->t_chasee->t_pos),hero) || off(player, ISINWALL) || on(*tp, CANINWALL)))
+        return(dist != 0);
+    else /* May actually hit here from a confused move */
+        return(!ce(tp->t_nxtpos, hero));
+}
+
+/*
+    roomin(coord *cp)
+
+        Find what room some coordinates are in.
+        NULL means they aren't in any room.
+*/
+
+struct room *
+roomin(coord cp)
+{
+    struct room *rp;
+    int i;
+
+    for (i = 0; i < MAXROOMS; i++)
+    {
+        rp = &rooms[i];
+
+        if ((cp.x <= (rp->r_pos.x + (rp->r_max.x - 1))) &&
+            (cp.y <= (rp->r_pos.y + (rp->r_max.y - 1))) &&
+            (cp.x >= rp->r_pos.x)                       &&
+            (cp.y >= rp->r_pos.y))
+        {
+            return(rp);
+        }
+    }
+
+    return(NULL);
+}
+
+/*
+ * find_mons: Find the monster from his corrdinates
+ */
+
+struct linked_list  *
+find_mons(int y, int x)
+{
+    struct linked_list  *item;
+
+    for (item = mlist; item != NULL; item = next(item))
+    {
+        struct thing *th = THINGPTR(item);
+
+        if (th->t_pos.y == y && th->t_pos.x == x)
+            return item;
+    }
+    return NULL;
+}
+
+/*
+ * Find an unfriendly monster around us to hit
+ */
+
+struct linked_list  *
+f_mons_a(int y, int x, int hit_bad)
+{
+    int row, col;
+    struct linked_list  *item;
+    struct thing    *tp;
+
+    for (row = x - 1; row <= x + 1; row++)
+        for (col = y - 1; col <= y + 1; col++)
+            if (row == x && col == y)
+                continue;
+            else if (col > 0 && row > 0 &&
+                isalpha(mvwinch(mw, col, row)) &&
+                 ((item = find_mons(col, row)) != NULL))
+            {
+                tp = THINGPTR(item);
+                if ((good_monster(*tp) && !hit_bad) ||
+                    (!good_monster(*tp) && hit_bad))
+                    return (item);
+            }
+
+    return (NULL);
+}
+
+
+/*
+    diag_ok()
+        Check to see if the move is legal if it is diagonal
+*/
+
+int
+diag_ok(coord *sp, coord *ep, struct thing *flgptr)
+{
+    if (ep->x == sp->x || ep->y == sp->y)
+        return TRUE;
+
+    return (step_ok(ep->y, sp->x, MONSTOK, flgptr) &&
+        step_ok(sp->y, ep->x, MONSTOK, flgptr));
+}
+
+/*
+    cansee()
+        returns true if the hero can see a certain coordinate.
+*/
+
+int
+cansee(int y, int x)
+{
+    struct room *rer;
+    coord   tp;
+
+    if (on(player, ISBLIND))
+        return FALSE;
+
+    tp.y = y;
+    tp.x = x;
+    rer = roomin(tp);
+
+    /*
+     * We can only see if the hero in the same room as the coordinate and
+     * the room is lit or if it is close.
+     */
+
+    return ((rer != NULL &&
+         rer == roomin(hero) &&
+         (!(rer->r_flags & ISDARK) || (rer->r_flags & HASFIRE)) &&
+         (levtype != MAZELEV || /* Maze level needs direct line */
+          maze_view(tp.y, tp.x))) ||
+        DISTANCE(tp,hero) < see_dist);
+}
+
+coord   *
+find_shoot(struct thing *tp, coord *dir)
+{
+    struct room *rtp;
+    int ulx, uly, xmx, ymx, xmon, ymon, tpx, tpy, row, col;
+    struct linked_list  *mon;
+    struct thing    *ick;
+
+    rtp = roomin(tp->t_pos);   /* Find room of chaser */
+
+    if (rtp == NULL)
+        return NULL;
+
+    ulx = rtp->r_pos.x;
+    uly = rtp->r_pos.y;
+    xmx = rtp->r_max.x;
+    ymx = rtp->r_max.y;
+
+    tpx = tp->t_pos.x;
+    tpy = tp->t_pos.y;
+
+    for (col = ulx; col < (ulx + xmx); col++)
+        for (row = uly; row < (uly + ymx); row++)
+        {
+            if (row > 0 && col > 0 && isalpha(mvwinch(mw, row, col)))
+            {
+		mon = find_mons(row, col);
+
+                if (mon)
+                {
+                    ick = THINGPTR(mon);
+                    xmon = ick->t_pos.x;
+                    ymon = ick->t_pos.y;
+
+                    if (!(good_monster(*ick)))
+                    {
+                        if (straight_shot(tpy, tpx, ymon, xmon, dir))
+                            return(dir);
+                    }
+                }
+            }
+        }
+
+    return(NULL);
+}
+
+/*
+    can_shoot()
+        determines if the monster (er) has a direct line of shot at the
+        player (ee).  If so, it returns the direction in which to shoot.
+*/
+
+coord *
+can_shoot(coord *er, coord *ee, coord *dir)
+{
+    int ery, erx, eey, eex;
+
+    /* Make sure we are chasing the player */
+
+    if (!ce((*ee), hero))
+        return(NULL);
+
+    /* They must be in the same room */
+
+    if (roomin(*er) != roomin(hero))
+        return(NULL);
+
+    ery = er->y;
+    erx = er->x;
+    eey = ee->y;
+    eex = ee->x;
+
+    /* Will shoot unless next to player, then 80% prob will fight */
+
+    if ((DISTANCE(*er,*ee) < 4) && (rnd(100) < 80))
+        return(NULL);
+
+    /* Do we have a straight shot? */
+
+    if (!straight_shot(ery, erx, eey, eex, dir))
+        return(NULL);
+    else
+        return(dir);
+}
+
+/*
+    straight_shot()
+        See if there is a straight line of sight between the two
+        given coordinates.  If shooting is not NULL, it is a pointer to a
+        structure which should be filled with the direction to shoot (if
+        there is a line of sight).  If shooting, monsters get in the way.
+        Otherwise, they do not.
+*/
+
+int
+straight_shot(int ery, int erx, int eey, int eex, coord *dir)
+{
+    int dy, dx; /* Deltas */
+    int ch;
+
+    /* Does the monster have a straight shot at player */
+
+    if ((ery != eey) && (erx != eex) &&
+        (abs(ery - eey) != abs(erx - eex)))
+        return (FALSE);
+
+    /* Get the direction to shoot */
+
+    if (eey > ery)
+        dy = 1;
+    else if (eey == ery)
+        dy = 0;
+    else
+        dy = -1;
+
+    if (eex > erx)
+        dx = 1;
+    else if (eex == erx)
+        dx = 0;
+    else
+        dx = -1;
+
+    /* Make sure we have free area all the way to the player */
+
+    ery += dy;
+    erx += dx;
+
+    while ((ery != eey) || (erx != eex))
+    {
+        switch(ch = winat(ery, erx))
+        {
+            case '|':
+            case '-':
+            case WALL:
+            case DOOR:
+            case SECRETDOOR:
+                return(FALSE);
+            default:
+                if (dir && isalpha(ch))
+                    return(FALSE);
+        }
+
+        ery += dy;
+        erx += dx;
+    }
+
+    if (dir)
+    {     /* If we are shooting -- put in the directions */
+        dir->y = dy;
+        dir->x = dx;
+    }
+
+    return(TRUE);
+}
+
+/*
+    get_hurl
+        returns the weapon that the monster will "throw" if it has one
+*/
+
+struct linked_list  *
+get_hurl(struct thing *tp)
+{
+    struct linked_list  *arrow,  *bolt,      *rock, *silverarrow, *fbbolt;
+    struct linked_list  *bullet, *firearrow, *dart, *dagger,      *shuriken;
+    struct linked_list  *oil,    *grenade;
+
+    struct linked_list  *pitem;
+    int bow = FALSE, crossbow = FALSE, sling = FALSE, footbow = FALSE;
+
+    /* Don't point to anything to begin with */
+
+    arrow = bolt = rock = silverarrow = fbbolt = NULL;
+    bullet = firearrow = dart = dagger = shuriken = NULL;
+    oil = grenade = NULL;
+
+    for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem))
+        if ((OBJPTR(pitem))->o_type == WEAPON)
+            switch ((OBJPTR(pitem))->o_which)
+            {
+                case    BOW:bow = TRUE; break;
+                case    CROSSBOW:crossbow = TRUE; break;
+                case    SLING:sling = TRUE; break;
+                case    FOOTBOW:footbow = TRUE; break;
+                case    ROCK:rock = pitem; break;
+                case    ARROW:arrow = pitem; break;
+                case    SILVERARROW:silverarrow = pitem; break;
+                case    BOLT:bolt = pitem; break;
+                case    FBBOLT:fbbolt = pitem; break;
+                case    BULLET:bullet = pitem; break;
+                case    FLAMEARROW:firearrow = pitem; break;
+                case    DART:dart = pitem; break;
+                case    DAGGER:dagger = pitem; break;
+                case    SHURIKEN:shuriken = pitem; break;
+                case    MOLOTOV:oil = pitem; break;
+                case    GRENADE:shuriken = pitem; break;
+            }
+
+    if (bow && silverarrow)
+        return(silverarrow);
+
+    if (crossbow && bolt)
+        return(bolt);
+
+    if (bow && firearrow)
+        return(firearrow);
+
+    if (off(*tp, ISCHARMED) && oil)
+        return(oil);
+		
+	if (off(*tp, ISCHARMED) && grenade)
+		return(grenade);
+
+    if (footbow && fbbolt)
+        return(fbbolt);
+
+    if (bow && arrow)
+        return(arrow);
+
+    if (sling && bullet)
+        return(bullet);
+
+    if (sling && rock)
+        return(rock);
+
+    if (shuriken)
+        return(shuriken);
+
+    if (dagger)
+        return(dagger);
+
+    if (silverarrow)
+        return(silverarrow);
+
+    if (firearrow)
+        return(firearrow);
+
+    if (fbbolt)
+        return(fbbolt);
+
+    if (bolt)
+        return(bolt);
+
+    if (bullet)
+        return(bullet);
+
+    if (dart)
+        return(dart);
+
+    if (rock)
+        return(rock);
+
+    return(NULL);
+}
+
+/*
+    pick_weap()
+        returns the biggest weapon that the monster will wield if it
+        has a non-launching or non-missile weapon returns NULL if no weapon, or
+        bare hands is better
+*/
+
+struct object *
+pick_weap(struct thing *tp)
+{
+    int weap_dam = maxdamage(tp->t_stats.s_dmg);
+    struct object   *ret_obj = NULL;
+    struct linked_list  *pitem;
+
+    if (on(*tp, CANWIELD))
+    {
+        for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem))
+        {
+            struct object   *obj = OBJPTR(pitem);
+
+            if (obj->o_type != WEAPON && !(obj->o_flags&(ISLAUNCHER|ISMISL)) &&
+                maxdamage(obj->o_damage) > weap_dam)
+            {
+                weap_dam = maxdamage(obj->o_damage);
+                ret_obj = obj;
+            }
+        }
+    }
+
+    return (ret_obj);
+}
+
+/*
+    canblink()
+        checks if the monster can teleport (blink).  If so, it will try
+        to blink the monster next to the player.
+*/
+
+int
+can_blink(struct thing *tp)
+{
+    int   y, x, index = 9;
+    coord   tryp;       /* To hold the coordinates for use in diag_ok */
+    int    spots[9], found_one = FALSE;
+
+    /*
+     * First, can the monster even blink?  And if so, there is only a 30%
+     * chance that it will do so.  And it won't blink if it is running.
+     */
+
+    if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) ||
+        on(*tp, ISFLEE) ||
+        (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) ||
+        (rnd(10) < 9))
+        return (FALSE);
+
+    /* Initialize the spots as illegal */
+
+    do
+    {
+        spots[--index] = FALSE;
+    }
+    while (index > 0);
+
+    /* Find a suitable spot next to the player */
+
+    for (y = hero.y - 1; y < hero.y + 2; y++)
+        for (x = hero.x - 1; x < hero.x + 2; x++, index++)
+        {
+            /*
+             * Make sure x coordinate is in range and that we are
+             * not at the player's position
+             */
+
+            if (x < 0 || x >= COLS || index == 4)
+                continue;
+
+            /* Is it OK to move there? */
+
+            if (!step_ok(y, x, NOMONST, tp))
+                spots[index] = FALSE;
+           else
+           {
+
+                /*
+                 * OK, we can go here.  But don't go there if
+                 * monster can't get at player from there
+                 */
+
+                tryp.y = y;
+                tryp.x = x;
+                if (diag_ok(&tryp, &hero, tp))
+                {
+                    spots[index] = TRUE;
+                    found_one = TRUE;
+                }
+            }
+        }
+
+    /* If we found one, go to it */
+
+    if (found_one)
+    {
+        /* Find a legal spot */
+
+        while (spots[index = rnd(9)] == FALSE)
+            continue;
+
+        /* Get the coordinates */
+
+        y = hero.y + (index / 3) - 1;
+        x = hero.x + (index % 3) - 1;
+
+        /* Move the monster from the old space */
+
+        mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
+
+        /* Move it to the new space */
+
+        tp->t_oldch = CCHAR( mvwinch(cw, y, x) );
+
+        if (cansee(y, x) &&
+            off(*tp, ISINWALL) &&
+            ((off(*tp, ISINVIS) &&
+              (off(*tp, ISSHADOW) || rnd(100) < 10)) || on(player, CANSEE)) &&
+            off(*tp, CANSURPRISE))
+            mvwaddch(cw, y, x, tp->t_type);
+
+        mvwaddch(mw, tp->t_pos.y,tp->t_pos.x,' '); /*Clear old position */
+        mvwaddch(mw, y, x, tp->t_type);
+        tp->t_pos.y = y;
+        tp->t_pos.x = x;
+    }
+
+    return (found_one);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/command.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,1383 @@
+/*
+    command.c  -  Read and execute the user commands
+ 
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    Based on "Rogue: Exploring the Dungeons of Doom"
+    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "rogue.h"
+
+/*
+    command()
+        Process the user commands
+*/
+
+void
+command(void)
+{
+    static char repcommand;     /* Command to repeat if we are repeating */
+    static int fight_to_death; /* Flags if we are fighting to death     */
+    static coord dir;          /* Last direction specified              */
+
+    object  *obj;
+    char     ch;
+    int      ntimes = 1; /* Number of player moves */
+    coord    nullcoord;
+
+    nullcoord.x = nullcoord.y = 0;
+
+    if (on(player, CANFLY) && rnd(2))
+        ntimes++;
+
+    if (on(player, ISHASTE))
+        ntimes++;
+
+    if (fighting && att_bonus())
+        ntimes *= 2;
+
+    if (on(player, ISSLOW))
+    {
+        if (player.t_turn != TRUE)
+            ntimes--;
+
+        player.t_turn ^= TRUE;
+    }
+
+    if (ntimes == 0)
+        return;
+
+    while (ntimes--)
+    {
+        moving = FALSE;
+
+        /* If player is infested, take off a hit point */
+
+        if (on(player, HASINFEST) && !is_wearing(R_HEALTH))
+        {
+            if ((pstats.s_hpt -= infest_dam) <= 0)
+            {
+                death(D_INFESTATION);
+                return;
+            }
+        }
+
+        look(after);
+
+        if (!running)
+            door_stop = FALSE;
+
+        status(FALSE);
+        wmove(cw, hero.y, hero.x);
+
+        if (!((running || count) && jump))
+            wrefresh(cw);   /* Draw screen */
+
+        take = 0;
+        after = TRUE;
+
+        /*
+         * Read command or continue run
+         */
+
+        if (!no_command)
+        {
+            if (fighting)
+            {
+                ch = (fight_to_death) ? 'F' : 'f';
+            }
+            else if (running)
+            {
+                /*
+                 * If in a corridor, if we are at a turn with
+                 * only one way to go, turn that way.
+                 */
+
+                if ((winat(hero.y, hero.x) == PASSAGE) && off(player, ISHUH) &&
+                    (off(player, ISBLIND)))
+                    switch (runch)
+                    {
+                        case 'h': corr_move(0, -1); break;
+                        case 'j': corr_move(1, 0); break;
+                        case 'k': corr_move(-1, 0); break;
+                        case 'l': corr_move(0, 1); break;
+                    }
+
+                ch = runch;
+            }
+            else if (count)
+                ch = repcommand;
+            else
+            {
+                ch = readchar();
+
+                if (mpos != 0 && !running)
+                    msg("");    /* Erase message if its there */
+            }
+        }
+        else
+        {
+            ch = '.';
+            fighting = moving = FALSE;
+        }
+
+        if (no_command)
+        {
+            if (--no_command == 0)
+                msg("You can move again.");
+        }
+        else
+        {
+
+            /*
+             * check for prefixes
+             */
+
+            if (isdigit(ch))
+            {
+                count = 0;
+                while (isdigit(ch))
+                {
+                    count = count * 10 + (ch - '0');
+                    ch = readcharw(cw);
+                }
+                repcommand = ch;
+
+                /*
+                 * Preserve count for commands which can be
+                 * repeated.
+                 */
+
+                switch(ch)
+                {
+                    case 'h':
+                    case 'j':
+                    case 'k':
+                    case 'l':
+                    case 'y':
+                    case 'u':
+                    case 'b':
+                    case 'n':
+                    case 'H':
+                    case 'J':
+                    case 'K':
+                    case 'L':
+                    case 'Y':
+                    case 'U':
+                    case 'B':
+                    case 'N':
+                    case 'q':
+                    case 'r':
+                    case 's':
+                    case 'm':
+                    case 't':
+                    case 'C':
+                    case 'I':
+                    case '.':
+                    case 'z':
+                    case 'p':
+                        break;
+                    default:
+                        count = 0;
+                }
+            }
+
+            /* Save current direction */
+
+            if (!running)   /* If running, it is already saved */
+                switch (ch)
+                {
+                    case 'h':
+                    case 'j':
+                    case 'k':
+                    case 'l':
+                    case 'y':
+                    case 'u':
+                    case 'b':
+                    case 'n':
+                        runch = ch;
+                        break;
+                    case 'H':
+                    case 'J':
+                    case 'K':
+                    case 'L':
+                    case 'Y':
+                    case 'U':
+                    case 'B':
+                    case 'N':
+                        runch = (char) tolower(ch);
+                        break;
+                }
+
+            /*
+             * execute a command
+             */
+
+            if (count && !running)
+                count--;
+
+            switch(ch)
+            {
+                /*
+                 * Movement and combat commands
+                 */
+
+                case 'h': do_move(0,-1); break;
+                case 'j': do_move(1, 0);  break;
+                case 'k': do_move(-1, 0); break;
+                case 'l': do_move(0, 1); break;
+                case 'y': do_move(-1, -1); break;
+                case 'u': do_move(-1, 1); break;
+                case 'b': do_move(1, -1); break;
+                case 'n': do_move(1, 1); break;
+                case 'H': do_run('h'); break;
+                case 'J': do_run('j'); break;
+                case 'K': do_run('k'); break;
+                case 'L': do_run('l'); break;
+                case 'Y': do_run('y'); break;
+                case 'U': do_run('u'); break;
+                case 'B': do_run('b'); break;
+                case 'N': do_run('n'); break;
+                case 'm':
+                    moving = TRUE;
+                    if (!get_dir())
+                    {
+                        after = FALSE;
+                        break;
+                    }
+                    do_move(delta.y, delta.x);
+                    break;
+                case 'F':
+                case 'f':
+                    fight_to_death = (ch == 'F');
+                    if (!fighting)
+                    {
+                        if (get_dir())
+                        {
+                            dir = delta;
+                            beast = NULL;
+                        }
+                        else
+                        {
+                            after = FALSE;
+                            break;
+                        }
+                    }
+                    do_fight(dir, (ch == 'F') ? TRUE : FALSE);
+                    break;
+                case 't':
+                    if (get_dir())
+                        missile(delta.y, delta.x, get_item("throw", 0),
+                            &player);
+                    else
+                        after = FALSE;
+
+                    /*
+                     * Informational commands - Do not do
+                     * after daemons
+                     */
+                     break;
+
+                case 0x7f:            /* sometime generated by */
+                                      /* suspend/foreground    */
+                case ESCAPE:
+                case ' ':
+                    after = FALSE;    /* do nothing */
+                    break;
+                case 'Q':
+                    after = FALSE;
+                    quit();
+                    break;
+                case 'i':
+                    after = FALSE;
+                    inventory(pack, '*');
+                    break;
+                case 'I':
+                    after = FALSE;
+                    inventory(pack, 0);
+                    break;
+                case '~':
+                    after = FALSE;
+                    next_exp_level(MESSAGE);
+                    break;
+                case '>':
+                    after = FALSE;
+                    d_level();
+                    break;
+                case '<':
+                    after = FALSE;
+                    u_level();
+                    break;
+                case '?':
+                    after = FALSE;
+                    help();
+                    break;
+                case '/':
+                    after = FALSE;
+                    identify();
+                    break;
+                case 'v':
+                    after = FALSE;
+                    msg("UltraRogue Version %s.", release);
+                    break;
+                case 'o':
+                    after = FALSE;
+                    option();
+                    strcpy(fd_data[1].mi_name, fruit);
+                    break;
+                case 12:    /* ctrl-l */
+                case 18:    /* ctrl-r */
+                    after = FALSE;
+                    clearok(cw, TRUE);
+					wrefresh(cw);
+                    break;
+                case 16: /* ctrl-p */
+                {
+                    int decrement = FALSE;
+                    after = FALSE;
+
+                    if (mpos == 0)
+                        decrement = TRUE;
+
+                    msg_index = (msg_index + 9) % 10;
+                    msg(msgbuf[msg_index]);
+                    if (decrement)
+                        msg_index = (msg_index + 9) % 10;
+                }
+                break;
+
+                case 'S':
+                    after = FALSE;
+                    if (save_game())
+                    {
+                        wclear(cw);
+                        wrefresh(cw);
+                        endwin();
+                        exit(0);
+                    }
+                    break;
+
+                /*
+                 * Other misc commands
+                 */
+
+                case '.':   break;   /* rest */
+                case ',':   add_pack(NULL, NOMESSAGE); break;
+                case 'q': quaff(&player, -1, ISNORMAL); break;
+                case 'r': read_scroll(&player, -1, ISNORMAL); break;
+                case 'd': drop(NULL); break;
+                case '^': set_trap(&player, hero.y, hero.x); break;
+                case 'c': incant(&player, nullcoord); break;
+                case 'D': dip_it(); break;
+                case 'e': eat(); break;
+                case '=': listen(); break;
+                case 'A': apply(); break;
+                case 'w': wield(); break;
+                case 'W': wear(); break;
+                case 'T': take_off(); break;
+                case 'P': ring_on(); break;
+                case 'R': ring_off(); break;
+                case 'p': prayer(); break;
+                case 'C': call(FALSE); break;
+                case 'M': call(TRUE); break;
+                case 's': search(FALSE); break;
+
+                /*
+                 * Directional commands - get_dir sets delta
+                 */
+                case 20:    /* ctrl-t */
+                    if (get_dir())
+                        steal();
+                    else
+                        after = FALSE;
+                    break;
+
+                case 'z':
+                    if (get_dir())
+                        do_zap(&player, -1, ISNORMAL);
+                    else
+                        after = FALSE;
+                    break;
+
+                case 'a':
+                    if (get_dir())
+                        affect();
+                    else
+                        after = FALSE;
+                    touchwin(cw);
+                    break;
+
+                /*
+                 * wizard commands
+                 */
+
+                case 0x17:    /* ctrl-w */
+                    after = FALSE;
+
+                    if (!wizard)
+                    {
+                        if (!canwizard)
+                        {
+                            msg("Illegal command '^W'.");
+                            break;
+                        }
+
+                        if (passwd())
+                        {
+                            msg("Welcome, oh mighty wizard.");
+                            wizard = waswizard = TRUE;
+                        }
+                        else
+                        {
+                            msg("Incorrect password.");
+                            break;
+                        }
+                    }
+
+                    msg("Wizard command: ");
+                    mpos = 0;
+                    ch = readchar();
+
+                    switch (ch)
+                    {
+                        case 'v':
+                            wiz_verbose = !wiz_verbose;
+                            break;
+
+                        case 'e':
+                            wizard = FALSE;
+                            msg("Not wizard any more.");
+                            break;
+
+                        case 's': activity(); break;
+                        case 't': teleport(); break;
+                        case 'm': overlay(mw, cw); break;
+                        case 'f': overlay(stdscr, cw); break;
+                        case 'i': inventory(lvl_obj, 0); break;
+                        case 'c': buy_it('\0', ISNORMAL); break;
+                        case 'I': whatis(NULL); break;
+                        case 'F':
+                             msg("food left: %d\tfood level: %d",
+                                  food_left,foodlev);
+                             break;
+                        case 'M':
+                            creat_mons(&player, get_monster_number("create"),
+                                       MESSAGE);
+                            break;
+
+                        case 'r':
+                            msg("rnd(4)%d, rnd(40)%d, rnd(100)%d",
+                                 rnd(4), rnd(40), rnd(100));
+                            break;
+
+                        case 'C':
+                            obj = get_object(pack, "charge", STICK, NULL);
+
+                            if (obj != NULL)
+                                obj->o_charges = 10000;
+
+                            break;
+
+                        case 'w':
+                            obj = get_object(pack, "price", 0, NULL);
+
+                            if (obj != NULL)
+                                msg("Worth %d.", get_worth(obj));
+
+                            break;
+
+                        case 'g':
+                            {
+                                int tlev;
+
+                                prbuf[0] = '\0';
+                                msg("Which level? ");
+
+                                if (get_string(prbuf, cw) == NORM)
+                                {
+                                    msg("");
+
+                                    if ((tlev = atoi(prbuf)) < 1)
+                                        msg("Illegal level.");
+                                    else if (tlev > 3000)
+                                    {
+                                        levtype = THRONE;
+                                        level = tlev - 3000;
+                                    }
+                                    else if (tlev > 2000)
+                                    {
+                                        levtype = MAZELEV;
+                                        level = tlev - 2000;
+                                    }
+                                    else if (tlev > 1000)
+                                    {
+                                        levtype = POSTLEV;
+                                        level = tlev - 1000;
+                                    }
+                                    else
+                                    {
+                                        levtype = NORMLEV;
+                                        level = tlev;
+                                    }
+
+                                    new_level(levtype,0);
+                                }
+                            }
+                            break;
+
+                        case 'o':   make_omnipotent(); break;
+
+                        case ESCAPE: /* Escape */
+                            door_stop = FALSE;
+
+                            count = 0;
+                            after = FALSE;
+                            break;
+
+                        default:
+                            msg("Illegal wizard command '%s', %d.",
+                                unctrl(ch), ch);
+                            count = 0;
+                            break;
+
+                    }
+
+                break;
+
+                default:
+                    msg("Illegal command '%s', %d.",
+                        unctrl(ch), ch);
+                    count = 0;
+                    after = FALSE;
+                    break;
+            }
+
+            /*
+             * turn off flags if no longer needed
+             */
+            if (!running)
+                door_stop = FALSE;
+        }
+
+        /*
+         * If he ran into something to take, let him pick it up.
+         */
+        if (take != 0)
+            if (!moving)
+                pick_up(take);
+            else
+                show_floor();
+        if (!running)
+            door_stop = FALSE;
+    } /* end while */
+}
+
+
+void
+do_after_effects(void)
+{
+    int i;
+
+    /* Kick off the rest of the daemons and fuses */
+
+    look(FALSE);
+    do_daemons(AFTER);
+    do_fuses(AFTER);
+
+    /* Special abilities */
+
+    if ((player.t_ctype == C_THIEF || player.t_ctype == C_ASSASIN ||
+     player.t_ctype == C_NINJA || player.t_ctype == C_RANGER) &&
+     (rnd(100) < (2 * pstats.s_dext + 5 * pstats.s_lvl)))
+        search(TRUE);
+
+    for (i = 0; i < ring_value(R_SEARCH); i++)
+        search(FALSE);
+
+    if (is_wearing(R_TELEPORT) && rnd(50) < 2)
+    {
+        teleport();
+
+        if (off(player, ISCLEAR))
+        {
+            if (on(player, ISHUH))
+                lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
+            else
+                light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER);
+
+            turn_on(player, ISHUH);
+        }
+        else
+            msg("You feel dizzy for a moment, but it quickly passes.");
+    }
+
+    /* accidents and general clumsiness */
+
+    if (fighting && rnd(50) == 0)
+    {
+        msg("You become tired of this nonsense.");
+        fighting = after = FALSE;
+    }
+
+    if (on(player, ISELECTRIC))
+        electrificate();
+
+    if (!fighting && (no_command == 0) && cur_weapon != NULL
+        && rnd(on(player, STUMBLER) ? 399 : 9999) == 0
+        && rnd(pstats.s_dext) <
+        2 - hitweight() + (on(player, STUMBLER) ? 4 : 0))
+    {
+        msg("You trip and stumble over your weapon.");
+        running = after = FALSE;
+
+        if (rnd(8) == 0 && (pstats.s_hpt -= roll(1, 10)) <= 0)
+        {
+            msg("You break your neck and die.");
+            death(D_FALL);
+            return;
+        }
+        else if (cur_weapon->o_flags & ISPOISON && rnd(4) == 0)
+        {
+            msg("You are cut by your %s!",
+                inv_name(cur_weapon, LOWERCASE));
+
+            if (player.t_ctype != C_PALADIN
+                && !(player.t_ctype == C_NINJA &&
+                pstats.s_lvl > 12)
+                && !save(VS_POISON))
+            {
+                if (pstats.s_hpt == 1)
+                {
+                    msg("You die from the poison in the cut.");
+                    death(D_POISON);
+                    return;
+                }
+                else
+                {
+                    msg("You feel very sick now.");
+                    pstats.s_hpt /= 2;
+                    chg_str(-2, FALSE, FALSE);
+                }
+            }
+       }
+   }
+
+   /* Time to enforce weapon and armor restrictions */
+   if (rnd(9999) == 0)
+        if (((cur_weapon == NULL) ||
+            (wield_ok(&player, cur_weapon, NOMESSAGE)))
+            && ((cur_armor == NULL) ||
+            (wear_ok(&player, cur_armor, NOMESSAGE))))
+        {
+            switch (player.t_ctype)
+            {
+                case C_CLERIC:
+                case C_DRUID:
+                case C_RANGER:
+                case C_PALADIN:
+                    if (rnd(luck) != 0)
+                        /* You better have done
+                         * little wrong */
+                        goto bad_cleric;
+
+                    msg("You are enraptured by the renewed "
+                        "power of your god.");
+                    break;
+
+                case C_MAGICIAN:
+                case C_ILLUSION:
+                    msg("You become in tune with the universe.");
+                    break;
+
+                case C_THIEF:
+                case C_NINJA:
+                case C_ASSASIN:
+                    msg("You become supernaly sensitive to your "
+                        "surroundings.");
+                    break;
+
+                case C_FIGHTER:
+                    msg("You catch your second wind.");
+                    break;
+
+                default:
+                    msg("What a strange type you are!");
+                    break;
+           }
+           pstats.s_hpt = max_stats.s_hpt += rnd(pstats.s_lvl) + 1;
+            pstats.s_power = max_stats.s_power += rnd(pstats.s_lvl) + 1;
+        }
+        else
+        {   /* he blew it - make him pay */
+
+            int death_cause = 0;
+
+            switch (player.t_ctype)
+            {
+                case C_CLERIC:
+                case C_DRUID:
+                case C_RANGER:
+                case C_PALADIN:
+            bad_cleric:
+                    msg("Your god scourges you for your misdeeds.");
+                    death_cause = D_GODWRATH;
+                    break;
+
+                case C_MAGICIAN:
+                case C_ILLUSION:
+                    msg("You short out your manna on the unfamiliar %s.",
+                        (cur_armor != NULL ? "armor" : "weapon"));
+
+                   death_cause = D_SPELLFUMBLE;
+                   break;
+
+                case C_THIEF:
+                case C_NINJA:
+                case C_ASSASIN:
+                    msg("You trip and fall because of the unfamiliar %s.",
+                        (cur_armor != NULL ? "armor" : "weapon"));
+                    death_cause = D_CLUMSY;
+                    break;
+
+                case C_FIGHTER:
+                    debug("Fighter getting raw deal?");
+                   break;
+
+                default:
+                    msg("What a strange type you are!");
+                    break;
+            }
+
+            aggravate();
+            pstats.s_power /= 2;
+            pstats.s_hpt /= 2;
+            player.t_no_move++;
+
+            if ((pstats.s_hpt -= rnd(pstats.s_lvl)) <= 0)
+            {
+                death(death_cause);
+            }
+        }
+
+    if (rnd(500000) == 0)
+    {
+        new_level(THRONE,0);
+        fighting = running = after = FALSE;
+        command();
+    }
+}
+
+void
+make_omnipotent(void)
+{
+    int i;
+    struct linked_list  *item;
+    struct object *obj;
+
+    for (i = 0; i < 20; i++)
+        raise_level();
+
+    max_stats.s_hpt += 1000;
+    max_stats.s_power += 1000;
+    pstats.s_hpt = max_stats.s_hpt;
+    pstats.s_power = max_stats.s_power;
+    max_stats.s_str = pstats.s_str = 25;
+    max_stats.s_intel = pstats.s_intel = 25;
+    max_stats.s_wisdom = pstats.s_wisdom = 25;
+    max_stats.s_dext = pstats.s_dext = 25;
+    max_stats.s_const = pstats.s_const = 25;
+
+    if (cur_weapon == NULL || cur_weapon->o_which != CLAYMORE)
+    {
+        item = spec_item(WEAPON, CLAYMORE, 10, 10);
+        cur_weapon = OBJPTR(item);
+        cur_weapon->o_flags |= ISKNOW;
+        add_pack(item, NOMESSAGE);
+    }
+
+    /* and a kill-o-zap stick */
+
+    item = spec_item(STICK, WS_DISINTEGRATE, 10000, 0);
+    obj = OBJPTR(item);
+    obj->o_flags |= ISKNOW;
+    know_items[TYP_STICK][WS_DISINTEGRATE] = TRUE;
+
+    if (guess_items[TYP_STICK][WS_DISINTEGRATE])
+    {
+        ur_free(guess_items[TYP_STICK][WS_DISINTEGRATE]);
+        guess_items[TYP_STICK][WS_DISINTEGRATE] = NULL;
+    }
+
+    add_pack(item, NOMESSAGE);
+
+    /* and his suit of armor */
+
+    if (cur_armor == NULL ||
+        !(cur_armor->o_which == CRYSTAL_ARMOR ||
+          cur_armor->o_which == MITHRIL))
+    {
+        item = spec_item(ARMOR, CRYSTAL_ARMOR, 15, 0);
+        obj = OBJPTR(item);
+        obj->o_flags |= ISKNOW;
+        obj->o_weight =
+            (int) (armors[CRYSTAL_ARMOR].a_wght * 0.2);
+        cur_armor = obj;
+        add_pack(item, NOMESSAGE);
+    }
+
+    /* and some rings (have to put them on, for now) */
+
+
+    if (!is_wearing(R_SEARCH))
+    {
+        item = spec_item(RING, R_SEARCH, 0, 0);
+        obj = OBJPTR(item);
+        obj->o_flags |= ISKNOW;
+        know_items[TYP_RING][R_SEARCH] = TRUE;
+
+        if (guess_items[TYP_RING][R_SEARCH])
+        {
+            ur_free(guess_items[TYP_RING][R_SEARCH]);
+            guess_items[TYP_RING][R_SEARCH] = NULL;
+        }
+
+        add_pack(item, NOMESSAGE);
+    }
+
+    if (!is_wearing(R_PIETY))
+    {
+        item = spec_item(RING, R_PIETY, 0, 0);
+        obj = OBJPTR(item);
+        obj->o_flags |= ISKNOW;
+        know_items[TYP_RING][R_PIETY] = TRUE;
+
+        if (guess_items[TYP_RING][R_PIETY])
+        {
+            ur_free(guess_items[TYP_RING][R_PIETY]);
+            guess_items[TYP_RING][R_PIETY] = NULL;
+        }
+
+        add_pack(item, NOMESSAGE);
+    }
+
+    item = spec_item(SCROLL, S_ELECTRIFY, 0, 0);
+    obj = OBJPTR(item);
+    obj->o_flags |= ISKNOW;
+    know_items[TYP_SCROLL][S_ELECTRIFY] = TRUE;
+
+    if (guess_items[TYP_SCROLL][S_ELECTRIFY])
+    {
+        ur_free(guess_items[TYP_SCROLL][S_ELECTRIFY]);
+        guess_items[TYP_SCROLL][S_ELECTRIFY] = NULL;
+    }
+
+    add_pack(item, NOMESSAGE);
+
+    /* Spiff him up a bit */
+    quaff(&player, P_SHERO, ISBLESSED);
+    quaff(&player, P_CLEAR, ISBLESSED);
+    quaff(&player, P_FIRERESIST, ISBLESSED);
+    quaff(&player, P_TRUESEE, ISBLESSED);
+    quaff(&player, P_PHASE, ISBLESSED);
+    purse += 50000L;
+    updpack();
+}
+
+
+/*
+    quit()
+        Have player make certain, then exit.
+*/
+
+void
+quit_handler(int sig)
+{
+    if (signal(SIGINT, quit_handler) != quit_handler)
+        mpos = 0;
+
+    sig = 0;
+
+    quit();
+}
+
+void
+quit(void)
+{
+    msg("Really quit?");
+
+    wrefresh(cw);
+
+    if (readchar() == 'y')
+    {
+        clear();
+        wclear(cw);
+        wrefresh(cw);
+        move(LINES - 1, 0);
+        wrefresh(stdscr);
+        score(pstats.s_exp, pstats.s_lvl, CHICKEN, 0);
+        byebye();
+    }
+    else
+    {
+        signal(SIGINT, quit_handler);
+        wmove(cw, 0, 0);
+        wclrtoeol(cw);
+        status(FALSE);
+        wrefresh(cw);
+        mpos = 0;
+        count = 0;
+        fighting = running = 0;
+    }
+}
+
+/*
+    search()
+        Player gropes about him to find hidden things.
+*/
+
+void
+search(int is_thief)
+{
+    int x, y;
+    char ch;
+
+    /*
+     * Look all around the hero, if there is something hidden there, give
+     * him a chance to find it.  If its found, display it.
+     */
+
+    if (on(player, ISBLIND))
+        return;
+
+    for (x = hero.x - 1; x <= hero.x + 1; x++)
+        for (y = hero.y - 1; y <= hero.y + 1; y++)
+        {
+            ch = winat(y, x);
+
+            if (isatrap(ch))
+            {
+                static char trname[1024]; /* temp scratch space */
+                struct trap *tp;
+                struct room *rp;
+
+                if (isatrap( mvwinch(cw, y, x)))
+                    continue;
+
+                tp = trap_at(y, x);
+
+                if ((tp->tr_flags & ISTHIEFSET) ||
+                    (rnd(100) > 50 && !is_thief))
+                    break;
+
+                rp = roomin(hero);
+
+                if (tp->tr_type == FIRETRAP && rp != NULL)
+                {
+                    rp->r_flags &= ~ISDARK;
+                    light(&hero);
+                }
+
+                tp->tr_flags |= ISFOUND;
+                mvwaddch(cw, y, x, ch);
+                count = 0;
+                running = FALSE;
+                msg(tr_name(tp->tr_type,trname));
+            }
+            else if (ch == SECRETDOOR)
+            {
+                if (rnd(100) < 20 && !is_thief)
+                {
+                    mvaddch(y, x, DOOR);
+                    count = 0;
+                }
+            }
+        }
+}
+
+/*
+    help()
+        Give single character help, or the whole mess if he wants it
+*/
+
+void
+help(void)
+{
+    const struct h_list *strp = helpstr;
+    char helpch;
+    int cnt;
+
+    msg("Character you want help for (* for all): ");
+    helpch = readchar();
+    mpos = 0;
+
+    /*
+     * If its not a *, print the right help string or an error if he
+     * typed a funny character.
+     */
+
+    if (helpch != '*')
+    {
+        wmove(cw, 0, 0);
+
+        while (strp->h_ch)
+        {
+            if (strp->h_desc == 0)
+                if (!wizard)
+                    break;
+                else
+                {
+                    strp++;
+                    continue;
+                }
+
+            if (strp->h_ch == helpch)
+            {
+                msg("%s%s", unctrl(strp->h_ch), strp->h_desc);
+                break;
+            }
+            strp++;
+        }
+
+        if (strp->h_ch != helpch)
+            msg("Unknown character '%s'.", unctrl(helpch));
+
+        return;
+    }
+
+    /*
+     * Here we print help for everything. Then wait before we return to
+     * command mode
+     */
+
+    wclear(hw);
+    cnt = 0;
+
+    while (strp->h_ch)
+    {
+        if (strp->h_desc == 0)
+            if (!wizard)
+                break;
+            else
+            {
+                strp++;
+                continue;
+            }
+
+        mvwaddstr(hw, cnt % 23, cnt > 22 ? 40 : 0, unctrl(strp->h_ch));
+        waddstr(hw, strp->h_desc);
+        strp++;
+
+        if (++cnt >= 46 && strp->h_ch && (strp->h_desc != NULL || wizard))
+        {
+            wmove(hw, LINES - 1, 0);
+            wprintw(hw, (char *) morestr);
+            wrefresh(hw);
+            wait_for(' ');
+            wclear(hw);
+            cnt = 0;
+        }
+    }
+
+    wmove(hw, LINES - 1, 0);
+    wprintw(hw, (char *) morestr);
+    wrefresh(hw);
+    wait_for(' ');
+    wclear(hw);
+    wrefresh(hw);
+
+    wmove(cw, 0, 0);
+    wclrtoeol(cw);
+    status(FALSE);
+    touchwin(cw);
+
+    return;
+}
+
+/*
+    identify()
+        Tell the player what a certain thing is.
+*/
+
+void
+identify(void)
+{
+    int   ch;
+    char *str;
+
+    msg("What do you want identified? ");
+    mpos = 0;
+
+    if ((ch = readchar()) == ESCAPE)
+    {
+        msg("");
+        return;
+    }
+
+    if (isalpha(ch))
+    {
+        id_monst(ch);
+        return;
+    }
+
+    switch (ch)
+    {
+        case '|':
+        case '-':        str = "wall of a room";                 break;
+        case GOLD:       str = "gold";                           break;
+        case STAIRS:     str = "passage leading down";           break;
+        case DOOR:       str = "door";                           break;
+        case FLOOR:      str = "room floor";                     break;
+        case VPLAYER:    str = "The hero of the game ---> you";  break;
+        case IPLAYER:    str = "you (but invisible)";            break;
+        case PASSAGE:    str = "passage";                        break;
+        case POST:       str = "trading post";                   break;
+        case POOL:       str = "a shimmering pool";              break;
+        case TRAPDOOR:   str = "trapdoor";                       break;
+        case ARROWTRAP:  str = "arrow trap";                     break;
+        case SLEEPTRAP:  str = "sleeping gas trap";              break;
+        case BEARTRAP:   str = "bear trap";                      break;
+        case TELTRAP:    str = "teleport trap";                  break;
+        case DARTTRAP:   str = "dart trap";                      break;
+        case MAZETRAP:   str = "entrance to a maze";             break;
+        case FIRETRAP:   str = "fire trap";                      break;
+        case POISONTRAP: str = "poison pool trap";               break;
+        case LAIR:       str = "monster lair entrance";          break;
+        case RUSTTRAP:   str = "rust trap";                      break;
+        case POTION:     str = "potion";                         break;
+        case SCROLL:     str = "scroll";                         break;
+        case FOOD:       str = "food";                           break;
+        case WEAPON:     str = "weapon";                         break;
+        case ' ':        str = "solid rock";                     break;
+        case ARMOR:      str = "armor";                          break;
+        case ARTIFACT:   str = "an artifact from bygone ages";   break;
+        case RING:       str = "ring";                           break;
+        case STICK:      str = "wand or staff";                  break;
+        default:         str = "unknown character";              break;
+    }
+    msg("'%s'; %s", unctrl(ch), str);
+}
+
+/*
+    d_level()
+        He wants to go down a level
+*/
+
+void
+d_level(void)
+{
+    int no_phase = FALSE;
+
+    if (mvinch(hero.y, hero.x) != STAIRS)
+    {
+        if (off(player, CANINWALL))  /* Must use stairs if can't phase */
+        {
+            msg("I see no way down.");
+            return;
+        }
+
+        extinguish_fuse(FUSE_UNPHASE);/*Using phase to go down gets rid of it*/
+        no_phase = TRUE;
+    }
+
+    if (is_wearing(R_LEVITATION) || on(player, CANFLY))
+    {
+        msg("You can't!  You're floating in the air.");
+        return;
+    }
+
+    if (rnd(pstats.s_dext) < 3 * (2 - hitweight() +
+        (on(player, STUMBLER) ? 4 : 0)))
+    {
+        msg("You trip and fall down the stairs.");
+
+        if ((pstats.s_hpt -= roll(1, 10)) <= 0)
+        {
+            msg("You break your neck and die.");
+            death(D_FALL);
+            return;
+        }
+    }
+
+    level++;
+    new_level(NORMLEV,0);
+
+    if (no_phase)
+        unphase(NULL);
+
+    return;
+}
+
+/*
+    u_level()
+        He wants to go up a level
+*/
+
+void
+u_level(void)
+{
+    char ch = 0;
+
+    if (has_artifact && ((ch = CCHAR(mvinch(hero.y, hero.x))) == STAIRS ||
+                 (on(player, CANINWALL)
+            && (is_wearing(R_LEVITATION) || on(player, CANFLY)))))
+    {
+        if (--level == 0)
+            total_winner();
+        else if (rnd(wizard ? 3 : 15) == 0)
+            new_level(THRONE,0);
+        else
+        {
+            new_level(NORMLEV,0);
+            msg("You feel a wrenching sensation in your gut.");
+        }
+
+        if (on(player, CANINWALL) && ch != STAIRS)
+        {
+            extinguish_fuse(FUSE_UNPHASE);
+            unphase(NULL);
+        }
+
+        return;
+    }
+    else if (ch != STAIRS &&
+         !(on(player, CANINWALL) && (is_wearing(R_LEVITATION)
+                         || on(player, CANFLY))))
+        msg("I see no way up.");
+    else
+        msg("Your way is magically blocked.");
+
+    return;
+}
+
+/*
+    call()
+        allow a user to call a potion, scroll, or ring something
+*/
+
+void
+call(int mark)
+{
+    struct object    *obj;
+    char             *elsewise;
+    int item_type =   numthings;
+    char            **item_color = NULL;
+
+    if (mark)
+        obj = get_object(pack, "mark", 0, bff_markable);
+    else
+        obj = get_object(pack, "call", 0, bff_callable);
+
+    if (obj == NULL)
+        return;
+
+    switch (obj->o_type)
+    {
+        case RING:
+            item_type = TYP_RING;
+            item_color = r_stones;
+            break;
+        case POTION:
+            item_type = TYP_POTION;
+            item_color = p_colors;
+            break;
+        case SCROLL:
+            item_type = TYP_SCROLL;
+            item_color = s_names;
+            break;
+        case STICK:
+            item_type = TYP_STICK;
+            item_color = ws_made;
+        default:
+            if (!mark)
+            {
+                msg("You can't call that anything.");
+                return;
+            }
+            break;
+    }
+
+    elsewise = (guess_items[item_type][obj->o_which] != NULL ?
+       guess_items[item_type][obj->o_which] : item_color[obj->o_which]);
+
+    if (know_items[item_type][obj->o_which] && !mark)
+    {
+        msg("That has already been identified.");
+        return;
+    }
+
+    if (mark)
+    {
+        if (obj->o_mark[0])
+            msg("Was marked \"%s\".", obj->o_mark);
+
+        msg("What do you want to mark it? ");
+        prbuf[0] = '\0';
+    }
+    else
+    {
+        msg("Was called \"%s\".", elsewise);
+        msg("What do you want to call it? ");
+
+        if (guess_items[item_type][obj->o_which] != NULL)
+            ur_free(guess_items[item_type][obj->o_which]);
+
+        strcpy(prbuf, elsewise);
+    }
+
+    if (get_string(prbuf, cw) == NORM)
+    {
+        if (mark)
+        {
+            strncpy(obj->o_mark, prbuf, MARKLEN - 1);
+            obj->o_mark[MARKLEN - 1] = '\0';
+        }
+        else
+        {
+            guess_items[item_type][obj->o_which] = new_alloc(strlen(prbuf) + 1);
+            strcpy(guess_items[item_type][obj->o_which], prbuf);
+        }
+    }
+
+    return;
+}
+
+/*
+    att_bonus()
+        bonus attacks for certain player classes
+*/
+
+int
+att_bonus(void)
+{
+    int bonus = FALSE;
+
+    if ((player.t_ctype == C_FIGHTER || player.t_ctype == C_PALADIN)
+            && (pstats.s_lvl > 12 ||
+            (pstats.s_lvl > 6 && pstats.s_lvl < 13 && rnd(2))))
+        bonus = TRUE;
+
+    else if ((player.t_ctype == C_RANGER)
+             && (pstats.s_lvl > 14 ||
+             (pstats.s_lvl > 7 && pstats.s_lvl < 15 && rnd(2))))
+        bonus = TRUE;
+
+    else if ((player.t_ctype == C_NINJA)
+             && (pstats.s_lvl > 8 ||
+             (pstats.s_lvl > 4 && pstats.s_lvl < 9 && rnd(2))))
+        bonus = TRUE;
+
+    return(bonus);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/daemon.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,327 @@
+/*
+    daemon.c - functions for dealing with things that happen in the future
+ 
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    Based on "Rogue: Exploring the Dungeons of Doom"
+    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+/*
+        Needs to be rewritten again to work on a per creature basis.
+        Either each monster will have a list of effect, or each
+        fuse will take a creature pointer and multiple entries
+        for each fuse will be allowed. I tend to want to attach
+        the effects to the creature.
+*/
+
+#include "rogue.h"
+
+int demoncnt;
+
+struct daemon daemons[DAEMON_MAX] =
+{
+    { DAEMON_NULL,       NULL         },
+    { DAEMON_DOCTOR,     doctor       },
+    { DAEMON_ROLLWAND,   rollwand     },
+    { DAEMON_STOMACH,    stomach      },
+    { DAEMON_RUNNERS,    runners      }
+};
+
+struct fuse fuses[FUSE_MAX] =
+{
+    { FUSE_NULL,         NULL         },
+    { FUSE_SWANDER,      swander      },
+    { FUSE_UNCONFUSE,    unconfuse    },
+    { FUSE_UNSCENT,      unscent      },
+    { FUSE_SCENT,        scent        },
+    { FUSE_UNHEAR,       unhear       },
+    { FUSE_HEAR,         hear         },
+    { FUSE_UNSEE,        unsee        },
+    { FUSE_UNSTINK,      unstink      },
+    { FUSE_UNCLRHEAD,    unclrhead    },
+    { FUSE_UNPHASE,      unphase      },
+    { FUSE_SIGHT,        sight        },
+    { FUSE_RES_STRENGTH, res_strength },
+    { FUSE_NOHASTE,      nohaste      },
+    { FUSE_NOSLOW,       noslow       },
+    { FUSE_SUFFOCATE,    suffocate    },
+    { FUSE_CURE_DISEASE, cure_disease },
+    { FUSE_UNITCH,       un_itch      },
+    { FUSE_APPEAR,       appear       },
+    { FUSE_UNELECTRIFY,  unelectrify  },
+    { FUSE_UNBHERO,      unbhero      },
+    { FUSE_UNSHERO,      unshero      },
+    { FUSE_UNXRAY,       NULL         },
+    { FUSE_UNDISGUISE,   undisguise   },
+    { FUSE_SHERO,        shero        },
+    { FUSE_WGHTCHK,      wghtchk      },
+    { FUSE_UNSUMMON,     unsummon     },
+    { FUSE_UNGAZE,       ungaze       },
+    { FUSE_UNCOLD,       uncold       },
+    { FUSE_UNHOT,        unhot        },
+    { FUSE_UNFLY,        unfly        },
+    { FUSE_UNBREATHE,    unbreathe    },
+    { FUSE_UNREGEN,      unregen      },
+    { FUSE_UNSUPEREAT,   unsupereat   },
+    { FUSE_UNSHIELD,     unshield     },
+    { FUSE_UNMSHIELD,    unmshield    },
+    { FUSE_UNTRUESEE,    untruesee    }
+};
+
+/*
+    d_slot()
+        Find an empty slot in the daemon/fuse list
+*/
+
+struct delayed_action *
+d_slot(void)
+{
+    int i;
+    struct delayed_action *dev;
+
+    for (i = 0, dev = d_list; i < MAXDAEMONS; i++, dev++)
+        if (dev->d_type == EMPTY)
+            return(dev);
+
+    return(NULL);
+}
+
+
+/*
+     find_slot()
+         Find a particular slot in the table
+*/
+
+struct delayed_action *
+find_slot(int type, int id)
+{
+    int i;
+    struct delayed_action *dev;
+
+    for (i = 0, dev = d_list; i < MAXDAEMONS; i++, dev++)
+        if ( (dev->d_type == type) && (id == dev->d_id) )
+            return(dev);
+
+    return(NULL);
+}
+
+
+/*
+    daemon()
+        Start a daemon, takes a function.
+*/
+
+void
+start_daemon(int id, void *arg, int whendo)
+{
+    struct delayed_action *dev;
+
+    dev = d_slot();
+
+    if (dev != NULL)
+    {
+        dev->d_type = DAEMON;
+        dev->d_when = whendo;
+        dev->d_id = id;
+        dev->d_arg = arg;
+        dev->d_time = 1;
+        demoncnt += 1;  /* update count */
+    }
+}
+
+
+/*
+    kill_daemon()
+        Remove a daemon from the list
+*/
+
+void
+kill_daemon(int id)
+{
+    struct delayed_action *dev;
+
+    if ((dev = find_slot(DAEMON, id)) == NULL)
+        return;
+
+    /* Take it out of the list */
+
+    dev->d_type = EMPTY;
+    demoncnt -= 1;      /* update count */
+
+    return;
+}
+
+
+/*
+    do_daemons()
+        Run all the daemons that are active with the current flag,
+        passing the argument to the function.
+*/
+
+void
+do_daemons(int now)
+{
+    struct delayed_action *dev;
+
+    /* Loop through the devil list */
+
+    for (dev = d_list; dev < &d_list[MAXDAEMONS]; dev++)
+        /* Executing each one, giving it the proper arguments */
+        if ( (dev->d_when == now) && (dev->d_type == DAEMON))
+        {
+            if ((dev->d_id < 1) || (dev->d_id >= DAEMON_MAX))
+                printf("Bad daemon id %d\n", dev->d_id);
+            else if (daemons[dev->d_id].func == NULL)
+                printf("No action for daemon %d!!!\n", dev->d_id);
+            else
+            {
+                daemon_arg arg;
+				
+                arg.varg = dev->d_arg;
+                daemons[dev->d_id].func(&arg);
+            }
+        }
+
+}
+
+
+/*
+    fuse()
+        Start a fuse to go off in a certain number of turns
+*/
+
+void
+light_fuse(int id, void *arg, int time, int whendo)
+{
+    struct delayed_action   *wire;
+
+    wire = d_slot();
+
+    if (wire != NULL)
+    {
+        wire->d_type = FUSE;
+        wire->d_when = whendo;
+        wire->d_id   = id;
+        wire->d_arg  = arg;
+        wire->d_time = time;
+        demoncnt += 1;  /* update count */
+    }
+}
+
+
+/*
+    lengthen()
+        Increase the time until a fuse goes off
+*/
+
+void
+lengthen_fuse(int id, int xtime)
+{
+    struct delayed_action *wire;
+
+    if ((wire = find_slot(FUSE,id)) == NULL)
+        return;
+
+    wire->d_time += xtime;
+
+    return;
+}
+
+
+/*
+    extinguish()
+        Put out a fuse
+*/
+
+void
+extinguish_fuse(int id)
+{
+    struct delayed_action   *wire;
+
+    if ((wire = find_slot(FUSE,id)) == NULL)
+        return;
+
+    wire->d_type = EMPTY;
+    demoncnt -= 1;
+
+    return;
+}
+
+
+/*
+    do_fuses()
+        Decrement counters and start needed fuses
+*/
+
+void
+do_fuses(int now)
+{
+    struct delayed_action   *wire;
+
+    /* Step though the list */
+
+    for (wire = d_list; wire < &d_list[MAXDAEMONS]; wire++)
+    {
+        /*
+         * Decrementing counters and starting things we want.  We
+         * also need to remove the fuse from the list once it has
+         * gone off.
+         */
+
+        if( (wire->d_type == FUSE) && (wire->d_when == now) )
+        {
+            if (--wire->d_time <= 0)
+            {
+                fuse_arg arg;
+
+                arg.varg = wire->d_arg;
+                wire->d_type = EMPTY;
+                fuses[wire->d_id].func(&arg);
+                demoncnt -= 1;
+            }
+        }
+
+    }
+
+    return;
+}
+
+
+/*
+    activity()
+        Show wizard number of demaons and memory blocks used
+*/
+
+void
+activity(void)
+{
+    msg("Daemons = %d : Memory Items = %d ", demoncnt, total);
+    return;
+}
+
+/*
+    waste_time()
+        Do nothing but let other things happen
+*/
+
+void
+waste_time(void)
+{
+    if (inwhgt)     /* if from wghtchk then done */
+        return;
+
+    do_daemons(BEFORE);
+    do_fuses(BEFORE);
+    do_daemons(AFTER);
+    do_fuses(AFTER);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/daemons.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,999 @@
+/*
+    daemons.c - All the daemon and fuse functions are in here
+  
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    Based on "Rogue: Exploring the Dungeons of Doom"
+    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include "rogue.h"
+
+/*
+    doctor()
+        A healing daemon that restors spell and hit points after rest
+*/
+
+void
+doctor(daemon_arg *who)
+{
+    struct thing *tp = who->thingptr;
+    long ohp;   /* turn off ISFLEE? */
+    struct stats *curp;  /* current stats pointer */
+    struct stats *maxp;  /* max stats pointer */
+    int         turns_quiet, new_points;
+
+    curp = &(tp->t_stats);
+    maxp = &(tp->maxstats);
+
+    if (on(*tp, ISINWALL))
+    {
+        tp->t_rest_hpt = 0;
+        return;
+    }
+
+    /* Check for regenerating spell points first */
+
+    doctor_spell_points(tp);
+
+    if (curp->s_hpt == maxp->s_hpt)
+    {
+        tp->t_rest_hpt = 0;
+        return;
+    }
+
+    tp->t_rest_hpt++;
+
+    switch (tp->t_ctype)
+    {
+        case C_MAGICIAN:
+        case C_ILLUSION:
+            turns_quiet = 24 - curp->s_lvl;
+            new_points = curp->s_lvl / 2 - 4;
+            break;
+
+        case C_THIEF:
+        case C_ASSASIN:
+        case C_NINJA:
+            turns_quiet = 16 - curp->s_lvl;
+            new_points = curp->s_lvl / 2 - 1;
+            break;
+
+        case C_CLERIC:
+        case C_DRUID:
+            turns_quiet = 16 - curp->s_lvl;
+            new_points = curp->s_lvl / 2 - 2;
+            break;
+
+        case C_FIGHTER:
+        case C_RANGER:
+        case C_PALADIN:
+            turns_quiet = 8 - curp->s_lvl / 2;
+            new_points = curp->s_lvl / 2 - 1;
+            break;
+
+        case C_MONSTER:
+            turns_quiet = 16 - curp->s_lvl;
+            new_points = curp->s_lvl / 2 - 6;
+            break;
+
+        default:
+            debug("What a strange character you are!");
+            return;
+    }
+
+    ohp = curp->s_hpt;
+
+    if (off(*tp, HASDISEASE))
+    {
+        if (curp->s_lvl < 8)
+        {
+            if (tp->t_rest_hpt > turns_quiet)
+                curp->s_hpt++;
+        }
+        else if (tp->t_rest_hpt >= 15)
+            curp->s_hpt += rnd(new_points) + 1;
+    }
+
+    if (tp == &player)
+    {
+        if (curp->s_lvl > 10)
+            turns_quiet = 2;
+        else
+            turns_quiet = rnd(turns_quiet / 6) + 1;
+
+        if (is_wearing(R_REGEN))
+            curp->s_hpt += ring_value(R_REGEN);
+    }
+
+    if (on(*tp, ISREGEN))
+        curp->s_hpt += curp->s_lvl / 5 + 1;
+
+    if (ohp != curp->s_hpt)
+    {
+        if (curp->s_hpt >= maxp->s_hpt)
+        {
+            curp->s_hpt = maxp->s_hpt;
+
+            if (off(*tp, WASTURNED) && on(*tp, ISFLEE)
+                && tp != &player)
+            {
+                turn_off(*tp, ISFLEE);
+                tp->t_oldpos = tp->t_pos;
+                /* Start our trek over */
+            }
+        }
+        tp->t_rest_hpt = 0;
+    }
+
+    return;
+}
+
+
+/*
+    doctor_spell_points()
+        A healing daemon that restors spell points
+*/
+
+void
+doctor_spell_points(struct thing *tp)
+{
+    int        turns_quiet, new_points;
+    struct stats *curp;   /* current stats pointer */
+    struct stats *maxp;   /* max stats pointer */
+    int         opower; /* current power */
+
+    curp = &(tp->t_stats);
+    maxp = &(tp->maxstats);
+    opower = curp->s_power;
+
+    /* The right ring will let you regenerate while wearing bad armor */
+
+    if (off(*tp, CANCAST) ||
+        ((tp == &player) &&
+         (cur_armor && wear_ok(tp, cur_armor, NOMESSAGE) == FALSE) &&
+         !(is_wearing(R_WIZARD) || is_wearing(R_PIETY))))
+    {
+        tp->t_rest_pow = 0;
+        return;
+    }
+
+    tp->t_rest_pow++;
+
+    switch (tp->t_ctype)
+    {
+        case C_MAGICIAN:
+        case C_ILLUSION:
+            turns_quiet = 18 - curp->s_lvl / 2;
+            new_points = curp->s_lvl / 2;
+            break;
+        case C_CLERIC:
+        case C_DRUID:
+            turns_quiet = 24 - curp->s_lvl;
+            new_points = curp->s_lvl / 2 - 2;
+            break;
+        case C_THIEF:
+        case C_ASSASIN:
+        case C_NINJA:
+            turns_quiet = 32 - curp->s_lvl;
+            new_points = curp->s_lvl / 3 - 3;
+            break;
+        case C_FIGHTER:
+        case C_RANGER:
+        case C_PALADIN:
+            turns_quiet = 32 - curp->s_lvl;
+            new_points = curp->s_lvl / 3 - 4;
+            break;
+        case C_MONSTER:
+            turns_quiet = 24 - curp->s_lvl;
+            new_points = curp->s_lvl - 6;
+            break;
+        default:
+            return;
+    }
+
+    if (curp->s_lvl < 8)
+    {
+        if (tp->t_rest_pow > turns_quiet)
+            curp->s_power++;
+    }
+    else if (tp->t_rest_pow >= 15)
+        curp->s_power += rnd(new_points) + 1;
+
+    if (tp == &player && (is_wearing(R_WIZARD) || is_wearing(R_PIETY)))
+        curp->s_power += ring_value(R_WIZARD) + ring_value(R_PIETY);
+
+    curp->s_power = min(max(0, curp->s_power), maxp->s_power);
+
+    if (curp->s_power != opower)
+        tp->t_rest_pow = 0;
+
+    return;
+}
+
+/*
+    rollwand()
+        called to roll to see if a wandering monster starts up
+*/
+
+daemon
+rollwand(daemon_arg *arg)
+{
+    NOOP(arg);
+
+    if ((rnd(6) == 0) && (player.t_ctype != C_THIEF ||
+        (rnd(30) >= pstats.s_dext)))
+    {
+        wanderer();
+        kill_daemon(DAEMON_ROLLWAND);
+        light_fuse(FUSE_SWANDER, 0, WANDERTIME, BEFORE);
+    }
+
+    return;
+}
+
+/*
+    stomach()
+        digest the hero's food
+*/
+
+daemon
+stomach(daemon_arg *arg)
+{
+    int oldfood, old_hunger;
+    int amount;
+    int power_scale;
+
+    NOOP(arg);
+
+    old_hunger = hungry_state;
+
+    if (food_left <= 0)
+    {
+        /* the hero is fainting */
+
+        if (no_command || rnd(100) > 20)
+            return;
+
+        no_command = rnd(8) + 4;
+        running = FALSE;
+        count = 0;
+        hungry_state = F_FAINT;
+        feed_me(hungry_state);
+    }
+    else
+    {
+        oldfood = food_left;
+
+        amount = ring_eat(LEFT_1) + ring_eat(LEFT_2) +
+            ring_eat(LEFT_3) + ring_eat(LEFT_4) +
+            ring_eat(RIGHT_1) + ring_eat(RIGHT_2) +
+            ring_eat(RIGHT_3) + ring_eat(RIGHT_4) +
+            foodlev;
+
+        if (on(player, SUPEREAT))   /* artifact or regeneration munchies */
+            amount *= 2;
+
+        if (on(player, POWEREAT))  /* Used an artifact power */
+        {
+            amount += 40;
+            turn_off(player, POWEREAT);
+        }
+
+        power_scale = (on(player, POWERDEXT) + on(player, POWERSTR) +
+            on(player, POWERWISDOM) + on(player, POWERINTEL) +
+            on(player, POWERCONST) + 1);
+
+        food_left -= amount * power_scale;
+
+        if (food_left < MORETIME && oldfood >= MORETIME)
+        {
+            hungry_state = F_WEAK;
+            running = FALSE;
+            feed_me(hungry_state);
+        }
+        else if (food_left < 2 * MORETIME && oldfood >= 2 * MORETIME)
+        {
+            hungry_state = F_HUNGRY;
+            running = FALSE;
+            feed_me(hungry_state);
+        }
+    }
+
+    if (old_hunger != hungry_state)
+        updpack();
+
+    wghtchk(NULL);
+}
+
+
+/*
+    runners()
+        Make all the running monsters move. with monsters now fighting
+        each other, this routine have been enhanced and may need more work yet
+*/
+
+daemon
+runners(daemon_arg *arg)
+{
+    struct linked_list  *item;
+    struct thing        *tp;
+
+    NOOP(arg);
+
+    for (item = mlist; item != NULL; item = next_mons)
+    {
+        curr_mons = item;
+        next_mons = next(curr_mons);
+        tp        = THINGPTR(item);
+
+        if (on(*tp, ISHELD) && rnd(tp->t_stats.s_str +
+            tp->t_stats.s_lvl) > 10 + rnd(50))
+        {
+            turn_off(*tp, ISHELD);
+            turn_off(*tp, ISDISGUISE);
+            turn_on(*tp, ISRUN);
+            tp->t_ischasing = TRUE;
+            tp->t_chasee = &player;
+            tp->t_horde  = NULL;
+
+            if (tp->t_stats.s_hpt < rnd(tp->maxstats.s_hpt))
+                turn_on(*tp, ISFLEE);
+
+            if (cansee(tp->t_pos.y, tp->t_pos.x))
+                msg("The %s breaks free!", monsters[tp->t_index].m_name);
+        }
+
+        if (off(*tp, ISHELD) && on(*tp, ISRUN))
+        {
+            int flee = FALSE;
+
+            flee = on(*tp, ISFLEE) ||
+                     ( (tp->t_chasee == &player) &&
+                         on(player, ISINWALL) &&
+                         off(*tp, CANINWALL) && off(*tp, ISFAMILIAR) );
+
+            if (off(*tp, ISSLOW) || tp->t_turn)
+            {
+                daemon_arg targ;
+
+                targ.thingptr = tp;
+                doctor(&targ);
+                do_chase(tp, flee);
+            }
+
+            if (curr_mons && (on(*tp, ISHASTE) ||
+               ((on(*tp, CANFLY) || on(*tp, ISFAST)) &&
+               DISTANCE(hero, tp->t_pos) >= 4)))
+            {
+                daemon_arg targ;
+
+                targ.thingptr = tp;
+                doctor(&targ);
+                do_chase(tp, flee);
+            }
+
+            if (curr_mons)
+            {
+                tp->t_turn ^= TRUE;
+                tp->t_wasshot ^= FALSE; /* Not shot anymore */
+            }
+
+        }
+    }
+
+    curr_mons = next_mons = NULL;
+
+    return;
+}
+
+/*
+    swander()
+        called when it is time to start rolling for wandering monsters
+*/
+
+fuse
+swander(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    start_daemon(DAEMON_ROLLWAND, 0, BEFORE);
+    return;
+}
+
+/*
+    unconfuse
+        release the poor player from his confusion
+*/
+
+fuse
+unconfuse(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISHUH);
+    msg("You feel less confused now.");
+    return;
+}
+
+/*
+    unscent()
+        turn of extra smelling ability
+*/
+
+fuse
+unscent(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, CANSCENT);
+    msg("The smell of monsters goes away.");
+}
+
+/*
+    scent
+        give back the players sense of smell
+*/
+
+fuse
+scent(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISUNSMELL);
+    msg("You begin to smell the damp dungeon air again.");
+}
+
+
+/*
+    unhear
+        player doesn't have extra hearing any more
+*/
+
+fuse
+unhear(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, CANHEAR);
+    msg("The sounds of monsters fades away.");
+}
+
+/*
+    hear()
+        return the players sense of hearing
+*/
+
+fuse
+hear(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISDEAF);
+    msg("You can hear again.");
+}
+
+
+/*
+    unsee
+        He lost his see invisible power
+
+    Need to make monsters invisible again? This
+    was done in Rogue 5.2
+
+    for (th = mlist; th != NULL; th = next(th))
+        if (on(*th, ISINVIS) && see_monst(th))
+        {
+            move(th->t_pos.y, th->t_pos.x);
+            addch(th->t_oldch);
+        }
+*/
+
+fuse
+unsee(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    if (!is_wearing(R_SEEINVIS))
+    {
+        turn_off(player, CANSEE);
+        msg("The tingling feeling leaves your eyes.");
+    }
+}
+
+/*
+    unstink()
+        Remove to-hit handicap from player
+*/
+
+fuse
+unstink(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, HASSTINK);
+}
+
+/*
+    unclrhead
+        Player is no longer immune to confusion
+*/
+
+fuse
+unclrhead(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISCLEAR);
+    msg("The blue aura about your head fades away.");
+}
+
+/*
+    unphase()
+        Player can no longer walk through walls
+*/
+
+fuse
+unphase(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, CANINWALL);
+
+    msg("Your dizzy feeling leaves you.");
+
+    if (!step_ok(hero.y, hero.x, NOMONST, &player))
+        death(D_PETRIFY);
+}
+
+/*
+    sight()
+        He gets his sight back
+*/
+
+fuse
+sight(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    if (on(player, ISBLIND))
+    {
+        extinguish_fuse(FUSE_SIGHT);
+        turn_off(player, ISBLIND);
+        light(&hero);
+        msg("The veil of darkness lifts.");
+    }
+}
+
+/*
+    res_strength()
+        Restore player's strength
+*/
+
+fuse
+res_strength(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    if (lost_str)
+    {
+        chg_str(lost_str, FALSE, FALSE);
+        lost_str = 0;
+    }
+    else
+        pstats.s_str = max_stats.s_str + ring_value(R_ADDSTR) +
+            (on(player, POWERSTR) ? 10 : 0) +
+            (on(player, SUPERHERO) ? 10 : 0);
+
+    updpack();
+}
+
+/*
+    nohaste()
+        End the hasting
+*/
+
+fuse
+nohaste(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISHASTE);
+    msg("You feel yourself slowing down.");
+}
+
+/*
+    noslow()
+        End the slowing
+*/
+
+fuse
+noslow(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISSLOW);
+    msg("You feel yourself speeding up.");
+}
+
+/*
+    suffocate()
+        If this gets called, the player has suffocated
+*/
+
+fuse
+suffocate(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    death(D_SUFFOCATION);
+}
+
+/*
+    cure_disease()
+        daemon for curing the diseased
+*/
+
+fuse
+cure_disease(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, HASDISEASE);
+
+    if (off(player, HASINFEST))
+        msg("You begin to feel yourself improving again.");
+}
+
+/*
+    un_itch()
+        daemon for adding back dexterity
+*/
+
+fuse
+un_itch(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    if (lost_dext)
+    {
+        chg_dext(lost_dext, FALSE, FALSE);
+        lost_dext = 0;
+        turn_off(player, HASITCH);
+    }
+}
+
+/*
+    appear()
+        Become visible again
+*/
+
+fuse
+appear(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISINVIS);
+    PLAYER = VPLAYER;
+    msg("The tingling feeling leaves your body.");
+    light(&hero);
+}
+
+/*
+     unelectrify()
+       stop shooting off sparks
+*/
+
+fuse
+unelectrify(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISELECTRIC);
+    msg("The sparks and violet glow from your body fade away.");
+    light(&hero);
+}
+
+/*
+    unshero()
+        super heroism wears off, now do nasty effects
+*/
+
+fuse
+unshero(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    msg("Your feeling of invulnerability goes away.");
+    turn_off(player, SUPERHERO);
+    chg_str(-11, FALSE, FALSE);
+    chg_dext(-6, FALSE, FALSE);
+    food_left -= HEROTIME + rnd(HEROTIME);
+    no_command += 5 + rnd(5);
+    msg("You fall asleep.");
+}
+
+/*
+    unbhero()
+        blessed super heroism wears off, no bad effects
+*/
+
+fuse
+unbhero(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    msg("Your feeling of invincibility goes away.");
+    turn_off(player, SUPERHERO);
+    chg_str(-10, FALSE, FALSE);
+    chg_dext(-5, FALSE, FALSE);
+}
+
+/*
+    undisguise()
+        player stops looking like a monster
+*/
+
+fuse
+undisguise(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    msg("Your skin feels itchy for a moment.");
+    turn_off(player, ISDISGUISE);
+    PLAYER = VPLAYER;
+    light(&hero);
+}
+
+/*
+    unsummon()
+        Unsummon a monster
+*/
+
+void
+unsummon(fuse_arg *monny)
+{
+    struct linked_list *monst = monny->ll;
+    struct linked_list  *sum_monst = (struct linked_list *) monst;
+    struct thing    *tp = THINGPTR(sum_monst);
+    char    *mname = monsters[tp->t_index].m_name;
+
+    turn_off(*tp, WASSUMMONED);
+    turn_off(player, HASSUMMONED);
+    msg("Goodbye, master.");
+    msg("The summoned %s phases out of existence", mname);
+    killed(NULL, sum_monst, NOMESSAGE, NOPOINTS);
+    mons_summoned--;
+}
+
+/*
+    ungaze()
+        Turn off gaze reflection
+*/
+
+fuse
+ungaze(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    msg("The shiny particles swirl to the floor.");
+    turn_off(player, CANREFLECT);
+}
+
+/*
+    shero()
+        restore lost abilities from cursed potion of shero
+*/
+
+fuse
+shero(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    msg("You feel normal again.");
+    chg_str(2, FALSE, TRUE);
+    chg_dext(2, FALSE, TRUE);
+    turn_off(player, ISUNHERO);
+}
+
+/*
+    wghtchk()
+	    check that the pack weight is OK 
+*/
+
+fuse
+wghtchk(fuse_arg *arg)
+{
+    int dropchk, err = TRUE;
+    char    ch;
+
+    NOOP(arg);
+
+    inwhgt = TRUE;
+
+    if (pstats.s_pack > pstats.s_carry)
+    {
+        ch = CCHAR( mvwinch(stdscr, hero.y, hero.x) );
+
+        if ((ch != FLOOR && ch != PASSAGE))
+        {
+            extinguish_fuse(FUSE_WGHTCHK);
+            light_fuse(FUSE_WGHTCHK, (void *)TRUE, 1, AFTER);
+            inwhgt = FALSE;
+            return;
+        }
+
+        extinguish_fuse(FUSE_WGHTCHK);
+        msg("Your pack is too heavy for you.");
+
+        do
+        {
+            dropchk = drop(NULL);
+
+            if (dropchk == FALSE)
+            {
+                mpos = 0;
+                msg("You must drop something.");
+            }
+
+            if (dropchk == TRUE)
+                err = FALSE;
+
+        }
+        while (err);
+
+    }
+
+    inwhgt = FALSE;
+}
+
+
+/*
+    uncold()
+        He lost his cold resistance power
+*/
+
+fuse
+uncold(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, NOCOLD);
+
+    if (!is_wearing(R_COLDRESIST))
+        msg("You feel a slight chill in the air.");
+}
+
+/*
+    unhot()
+        He lost his fire resistance power
+*/
+
+fuse
+unhot(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, NOFIRE);
+
+    if (!is_wearing(R_FIRERESIST))
+        msg("You feel a flush of warmth.");
+}
+
+/*
+    unfly()
+        He stopped flying
+*/
+
+fuse
+unfly(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, CANFLY);
+
+    if (!is_wearing(R_LEVITATION))
+        msg("You float gently to the ground.");
+}
+
+/*
+    unbreathe()
+        He started needing oxygen
+*/
+
+fuse
+unbreathe(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, HASOXYGEN);
+
+    if (!is_wearing(R_BREATHE))
+        msg("You start huffing and puffing.");
+}
+
+/*
+    unregen()
+        He stops being regenerative
+*/
+
+fuse
+unregen(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, ISREGEN);
+
+    if (!is_wearing(R_REGEN))
+        msg("Your metabolism slows down.");
+}
+
+/*
+    unsupereat()
+        He stops being excessively hungry
+*/
+
+fuse
+unsupereat(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, SUPEREAT);
+    msg("You stop feeling so hungry.");
+}
+
+/*
+    unshield()
+        He stops having his AC helped by magic
+*/
+
+fuse
+unshield(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, HASSHIELD);
+    pstats.s_arm -= pstats.s_acmod;
+    pstats.s_acmod = 0;
+    msg("Your skin feels normal.");
+}
+
+/*
+    unmshield()
+        He stops ignoring thrown weapons
+*/
+
+fuse
+unmshield(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    turn_off(player, HASMSHIELD);
+    msg("The fog dissapates.");
+}
+
+/*
+    untrue()
+        He lost his true sight power
+*/
+
+void
+untruesee(fuse_arg *arg)
+{
+    NOOP(arg);
+
+    if (!is_wearing(R_TRUESEE))
+    {
+        turn_off(player, CANTRUESEE);
+        msg("Your sensory perceptions return to normal.");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/dict.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,1160 @@
+/*
+    dict.c
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1995 Herb Chong
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+/******************
+**  Change history:
+**
+**  (AK:04/03/95) - In dict_create, initialize hook for extensions to dictionary structure.
+**  (AK:04/04/95) - In dict_insert, only set any_ptr when a new string entry is created.
+**  (AK:04/17/95) - Added dict_union and dict_merge.
+**  (AK:04/18/95) - In dict_create, added code to create signature,
+**                  table of contents, and parameter array.
+**  (AK:04/18/95) - Revised dict_load, dict_save
+**  (AK:04/18/95) - Added dict_import
+**
+******************/
+
+static char sccsid[] = "%W% %G%";
+
+#include <stdlib.h>
+#include <string.h>
+#if !defined(OS2) && !defined(_WIN32)
+   #include <unistd.h>
+#endif
+
+#include "dict.h"
+#include "dictutil.h"
+
+#define BUFLEN      300
+
+/*************************************************************************
+*       Generic dictionary and hash table functions for word and
+*       string storage.
+*
+*       Implements generic dictionary management by using a
+*       hash table with direct chaining.  All terms are stored in a
+*       separate, unsorted, string table. When inserting into the
+*       hash table exceeds the current size, allocate a new hash table
+*       and rehash the old contents into the new table. When the string
+*       table fills, append more empty entries onto the old table and
+*       continue adding. Inserting a string that already exists increments
+*       the count for the string. Deleting a string only decrements the
+*       count to no less than 0. This will allow a 0 occurence string to be
+*       retrieved. To remove the 0 occurence string table entries, you must
+*       rebuild the dictionary. Merging from one dictionary to a new one will
+*       do this because it only copies nonzero occurences.
+*
+*       Assumptions:
+*               unsigned long >= 32 bits
+*               int >= 16 bits
+*               char == 8 bits
+*               number of entries < 2^28
+*************************************************************************/
+
+/*************************************************************************
+*       hash_string: calculate hash value for string, modified
+*       version of hashpjw() from the new Dragon book.
+*
+*       s - string to calculate hash for
+*
+*       Returns: hash value
+*************************************************************************/
+static unsigned long hash_string(const char *s)
+{
+        unsigned long h = 0;
+        unsigned long g = 0;
+
+        while (*s) {
+                h <<= 4;
+                h += *s++;
+                if ((g = h & 0xf0000000) != 0)
+                        h ^= g >> 24;
+        }
+        return h ^ g;
+}
+
+/*************************************************************************
+*       dict_create: create a dictionary and initialize its structures
+*
+*       toc_size - number of entries in table of contents ( >= 4 )
+*       initial_string_count - number of strings to hold as initial allocation
+*       initial_hash_chains - size of hash table, must be power of 4
+*       max_chain_length - max number of elements in hash chain before signalling growth
+*
+*       Returns: pointer to dictionary
+*               NULL - error creating dictionary
+*               non-NULL - sucessful creation
+*************************************************************************/
+
+DICTIONARY *dict_create(
+        const long toc_size,
+        const long initial_string_count,
+        const long initial_hash_chains,
+        const long max_chain_length )
+{
+        DICTIONARY      *dtemp = NULL;
+        STRING_ENTRY    *stemp = NULL;
+        long    *ltemp = NULL;
+        char    *sltemp;
+        long    i, j, tocsize;
+
+        /* check for a power of 4 in number of initial hash entries */
+        switch(initial_hash_chains) {
+        case 0x00000004:
+        case 0x00000010:
+        case 0x00000040:
+        case 0x00000100:
+        case 0x00000400:
+        case 0x00001000:
+        case 0x00004000:
+        case 0x00010000:
+        case 0x00040000:
+        case 0x00100000:
+        case 0x00400000:
+        case 0x01000000:
+        case 0x04000000:
+                break;
+        default:
+                return NULL;
+        }
+
+        /* Allocate the dictionary structure */
+        if ((dtemp = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL)
+                goto err_exit;
+
+        /* string count has to be within range */
+        j = initial_string_count;
+        if (j > 0x04000000)
+                return NULL;
+
+        /* force a reasonable value */
+        if (j < 100)
+                j = 100;
+
+        /* Allocate the string table and string array */
+        if ((stemp = (STRING_ENTRY *)malloc(sizeof(STRING_ENTRY) * j)) == NULL)
+                goto err_exit;
+        if ((sltemp = (char *)malloc(8 * j)) == NULL)
+                goto err_exit;
+
+        /* Allocate the hash table */
+        if ((ltemp = (long *)malloc(sizeof(long) * initial_hash_chains)) == NULL)
+                goto err_exit;
+
+        /* Allocate the parameter array */
+        if ( (dtemp->parm = (DICT_PARM_ENTRY *)malloc(14*sizeof(DICT_PARM_ENTRY))) == NULL)
+                goto err_exit;
+
+        /* Allocate the signature structure */
+        if ( (dtemp->sig=(DICT_SIG*)malloc(sizeof(DICT_SIG))) == NULL )
+                goto err_exit;
+
+        /* Allocate the Table of Contents */
+        tocsize = toc_size;
+        if ( tocsize < 4 )
+           tocsize = 4;
+        dtemp->sig->toc_size = tocsize;
+        if ( (dtemp->toc=(DICT_TOC_ENTRY*)malloc(dtemp->sig->toc_size*sizeof(DICT_TOC_ENTRY))) == NULL )
+                goto err_exit;
+
+        dtemp->check_value = DICT_VALIDATE;                     /* validation value */
+        dtemp->flags = DICT_NONE;                               /* no flags set */
+        dtemp->entry_count = 0;                                 /* nothing in either table */
+
+        dtemp->string_table = stemp;                            /* connect string table */
+        dtemp->string_max = j;                                  /* size of string table */
+        dtemp->scan_string_index = DICT_ENTRY_NONE;             /* not pointing to anything in scan */
+        dtemp->string_growth_count = 0;                         /* haven't grown any */
+
+        dtemp->string_array = sltemp;                           /* character array for strings */
+        dtemp->array_size = j * 8;                              /* how many bytes available */
+        dtemp->array_used = 0;                                  /* nothing used yet */
+        dtemp->array_growth_count = 0;                          /* haven't grown any */
+
+        /* force maximum hash chain length to a reasonable value */
+        i = max_chain_length;
+        if (i < 1 || i > 200)
+                i = 200;
+        dtemp->chains = ltemp;                                  /* connect hash table */
+        dtemp->longest_chain_length = 0;                        /* nothing in table yet */
+        dtemp->allowable_chain_length = i;                      /* chain length limit */
+        dtemp->table_size = initial_hash_chains;                /* size of hash table */
+        dtemp->hash_mask = initial_hash_chains - 1;             /* mask for mod() function */
+        dtemp->hash_growth_count = 0;                           /* haven't grown any */
+
+        /* string table is empty */
+        for (i = 0 ; i < j ; i++) {
+                dtemp->string_table[i].string_offset = DICT_ENTRY_NONE;
+                dtemp->string_table[i].count = 0;
+                dtemp->string_table[i].next = DICT_ENTRY_NONE;
+                dtemp->string_table[i].flags = 0;
+                dtemp->string_table[i].hash_value = 0;
+        }
+
+        /* no entries chained */
+        for (i = 0; i < initial_hash_chains; i++)
+                dtemp->chains[i] = DICT_ENTRY_NONE;
+
+        /* Initialize hook to extended dictionary structure  */
+        dtemp->ext = NULL;
+
+        /* Set up the parameter array */
+        if ( dict_set_parm_ids(dtemp) == FALSE )
+                goto err_exit;
+        if ( dict_set_parm_values(dtemp) == FALSE )
+                goto err_exit;
+
+        /* Set up the Table of Contents */
+        strcpy( dtemp->toc[0].id , "PARM" );
+        dtemp->toc[0].size = dtemp->sig->nparms * sizeof(DICT_PARM_ENTRY);
+        dtemp->toc[0].ptr = dtemp->parm;
+        dtemp->toc[0].type = 0;
+
+        strcpy( dtemp->toc[1].id , "HASH" );
+        dtemp->toc[1].size = dtemp->table_size * sizeof(long);
+        dtemp->toc[1].ptr = dtemp->chains;
+        dtemp->toc[1].type = 0;
+
+        strcpy( dtemp->toc[2].id , "STTB" );
+        dtemp->toc[2].size = dtemp->string_max * sizeof(char);
+        dtemp->toc[2].ptr = dtemp->string_table;
+        dtemp->toc[2].type = 0;
+
+        strcpy( dtemp->toc[3].id , "STAR" );
+        dtemp->toc[3].size = dtemp->array_size * sizeof(STRING_ENTRY);
+        dtemp->toc[3].ptr = dtemp->string_array;
+        dtemp->toc[3].type = 0;
+
+        /* Set up the signature */
+        dtemp->sig->check_value = DICT_VALIDATE;
+        dtemp->sig->toc_size = tocsize;
+        dtemp->sig->nparms = 14;
+
+        return dtemp;
+
+        /* error exit - saves duplicate code */
+err_exit:
+
+        dict_destroy( dtemp );
+        return NULL;
+}
+
+/*************************************************************************
+*       dict_destroy: discard a dictionary and its contents
+*               multiple calls to destroy the same dictionary is safe
+*
+*       dict - pointer to a dictionary to discard
+*
+*       Returns: status code
+*               TRUE - dictionary destroyed
+*               FALSE - error when destroying dictionary
+*       Note: Does free the dictionary structure itself.
+*************************************************************************/
+BOOLEANC dict_destroy(DICTIONARY *dict)
+{
+        int  i;
+
+        /* have to point to something */
+        if (dict == NULL)
+                return FALSE;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                return FALSE;
+
+        if ( (dict->sig==NULL) || (dict->toc==NULL) || (dict->parm==NULL) )
+                return FALSE;
+
+        /* Free the type=0 tables */
+        for ( i = 0 ; i < dict->sig->toc_size ; i++ ) {
+           if ( dict->toc[i].ptr != NULL && dict->toc[i].type == 0 )
+                free( dict->toc[i].ptr );
+        } /* endfor */
+
+        /* Free the Table of Contents and signature */
+        free( dict->toc );
+        free( dict->sig );
+
+        /* Free the dictionary structure itself */
+        free( dict );
+
+        return TRUE;
+}
+
+/*************************************************************************
+*       dict_insert: add entries into the dictionary, growing as necessary
+*
+*       dict - pointer to dictionary to insert into
+*       s - string to insert
+*       count - count of occurences of string
+*       flags - flag bits associated with the string
+*       number - pointer to long to return word number
+*
+*       Returns: pointer to new entry in dictionary
+*               NULL - error during insert
+*               non-NULL - pointer to inserted or updated entry
+*
+*       Notes: if the entry already exists, increment the count.
+*       If NULL is returned, the dictionary state can no longer
+*       be trusted. Terminating with an error code would be
+*       safest.
+*************************************************************************/
+STRING_ENTRY *dict_insert(DICTIONARY *dict, char *s,
+        const long occurences, const unsigned long flags,
+        void *any_ptr, long *number)
+{
+        unsigned long   hash_value;
+        long    hash_index;
+        long    string_index = -1;
+        long    he2;
+        int     chain_len;
+
+        /* have to point to something */
+        if (dict == NULL)
+                return FALSE;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                return FALSE;
+
+        /* must have a string */
+        if (s == NULL) {
+                *number = -1;
+                return FALSE;
+        }
+
+        /* must be non-NULL */
+        if (s[0] == '\0') {
+                *number = -1;
+                return FALSE;
+        }
+
+        /* figure out which chain it should go into */
+        hash_value = hash_string(s);
+        hash_index = hash_value & dict->hash_mask;
+
+        /* look for the entry in the chain */
+        for (he2 = dict->chains[hash_index], chain_len = 0; he2 != DICT_ENTRY_NONE;
+                        he2 = dict->string_table[he2].next, chain_len++) {
+                char    *sa = &dict->string_array[dict->string_table[he2].string_offset];
+
+                /* compare hash_value and then string, in that order, for speed */
+                if (dict->string_table[he2].hash_value == hash_value && !strcmp(s, sa)) {
+                        string_index = he2;
+                        break;
+                }
+        }
+
+        /* string wasn't found */
+        if (string_index < 0) {
+                /* make sure there is room in string entry table */
+                if (dict->entry_count + 1 > dict->string_max) {
+                        /* make new table 20% bigger */
+                        dict->string_max = (dict->string_max * 6) / 5;
+                        dict->string_table = (STRING_ENTRY *)realloc(dict->string_table,
+                                        sizeof(STRING_ENTRY) * dict->string_max);
+                        if (dict->string_table == NULL)
+                                return NULL;
+                        dict->string_growth_count++;
+                }
+                string_index = dict->entry_count;
+                dict->entry_count++;
+
+                /* make sure there is room in the string array */
+                if (dict->array_used + (long)strlen(s) + 1 > dict->array_size) {
+                        dict->array_size = (dict->array_size * 6) / 5;
+                        dict->string_array = (char *) realloc(dict->string_array,
+                                dict->array_size);
+                        if (dict->string_array == NULL)
+                                return NULL;
+                }
+
+                /* fill in starting values */
+                strcpy(&dict->string_array[dict->array_used], s);
+                dict->string_table[string_index].string_offset = dict->array_used;
+                dict->array_used += (long) strlen(s) + 1;
+                dict->string_table[string_index].count = 0 ;
+                dict->string_table[string_index].flags = DICT_NONE;
+                dict->string_table[string_index].any_ptr = any_ptr; /* (AK:04/04/95) */
+                dict->string_table[string_index].hash_value = hash_value;
+
+                /* hook entry at beginning of chain */
+                dict->string_table[string_index].next = dict->chains[hash_index];
+                dict->chains[hash_index] = string_index;
+
+                /* record chain lengths */
+                chain_len++;
+                if (chain_len > dict->longest_chain_length)
+                        dict->longest_chain_length = chain_len;
+
+                /* if a chain is too long */
+                if (chain_len > dict->allowable_chain_length) {
+                        long    new_size = dict->table_size * 4;
+                        long    new_mask = new_size - 1;
+                        long    *hetemp;
+                        long    i;
+                        int     longest_chain;
+
+                        if ((hetemp = (long *)malloc(sizeof(long) * new_size)) == NULL)
+                                return NULL;
+
+                        /* hash table chains are empty */
+                        for (i = 0; i < new_size; i++)
+                                hetemp[i] = DICT_ENTRY_NONE;
+
+                        /* reset all chains */
+                        for (i = 0; i < dict->entry_count; i++)
+                                dict->string_table[i].next = DICT_ENTRY_NONE;
+
+                        /* recreate hash table entries by reinserting all strings */
+                        for (i = 0; i < dict->entry_count; i++) {
+                                long    he = dict->string_table[i].hash_value & new_mask;
+
+                                dict->string_table[i].next = hetemp[he];
+                                hetemp[he] = i;
+                        }
+
+                        /* find longest chain length */
+                        for (i = 0, longest_chain = 0; i < new_size; i++) {
+                                int     len;
+                                long    cur;
+
+                                for (cur = hetemp[i], len = 0; cur != DICT_ENTRY_NONE;
+                                                cur = dict->string_table[cur].next)
+                                    len++;
+                                        ;
+                                if (len > longest_chain)
+                                        longest_chain = len;
+                        }
+
+                        /* delete old table and attach new one */
+                        free(dict->chains);
+                        dict->chains = hetemp;
+                        dict->longest_chain_length = longest_chain;
+                        dict->table_size = new_size;
+                        dict->hash_mask = new_mask;
+
+                        /* keep track of growth */
+                        dict->hash_growth_count++;
+                }
+        }
+
+        dict->string_table[string_index].count += occurences;
+        dict->string_table[string_index].flags |= flags;
+        *number = string_index;
+
+        return &dict->string_table[string_index];
+}
+
+/*************************************************************************
+*       dict_delete: deletes an entry from the dictionary
+*       (Actually, only decrements the entry's count)
+*
+*       dict - pointer to dictionary to delete
+*       s - string to find and delete
+*       count - count to decrement entry by
+*
+*       Returns: status code
+*               TRUE - entry has been deleted
+*               FALSE - entry wasn't found or error occured
+*************************************************************************/
+BOOLEANC dict_delete(const DICTIONARY *dict, const char *s, const long count)
+{
+        STRING_ENTRY    *se;
+        long    n;
+
+        /* find the string */
+        if ((se = dict_search(dict, s, &n)) == NULL)
+                return FALSE;
+
+        /* decrement count and make sure it stays valid */
+        se->count -= count;
+        if (se->count < 0)
+                se->count = 0;
+        return TRUE;
+}
+
+/*************************************************************************
+*       dict_search: look for entries in the dictionary
+*
+*       dict - pointer to dictionary to search
+*       s - string to search for
+*       number - pointer to long to return string number
+*
+*       Returns: pointer to string entry
+*               NULL - entry not found
+*               non-NULL - pointer to string entry
+*************************************************************************/
+STRING_ENTRY *dict_search(const DICTIONARY *dict, const char *s,
+        long *number)
+{
+        unsigned long   hash_value;
+        long    hash_index;
+        long    string_index = -1;
+        long    he;
+
+        /* have to point to something */
+        if (dict == NULL)
+                return NULL;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                return NULL;
+
+        /* must have a string */
+        if (s == NULL) {
+                *number = -1;
+                return NULL;
+        }
+
+        /* must be non-NULL */
+        if (s[0] == '\0') {
+                *number = -1;
+                return FALSE;
+        }
+
+        /* figure out which chain it should be in */
+        hash_value = hash_string(s);
+        hash_index = hash_value & dict->hash_mask;
+
+        /* look for the entry in the chain */
+        for (he = dict->chains[hash_index]; he != DICT_ENTRY_NONE;
+                        he = dict->string_table[he].next) {
+                char    *sa = (char*)(&dict->string_array[dict->string_table[he].string_offset]);
+
+                /* compare hash_value and then string, in that order, for speed */
+                if (dict->string_table[he].hash_value == hash_value && !strcmp(s, sa)) {
+                        string_index = he;
+                        break;
+                }
+        }
+        if (string_index < 0) {
+                *number = -1;
+                return NULL;
+        }
+
+        *number = string_index;
+        return dict->string_table+string_index;
+}
+
+/*************************************************************************
+*       dict_union: merges contents of 2 dictionaries into
+*       a third
+*
+*       dict1 - pointer to dictionary 1
+*       dict2 - pointer to dictionary 2
+*
+*       Returns: dictionary pointer
+*               NULL - failed to merge
+*               non-NULL - pointer to new dictionary
+*
+*       Notes: entries of the same string have their counts
+*       added and their flags ORed together.
+*************************************************************************/
+DICTIONARY *dict_union( const DICTIONARY *dict1 , const DICTIONARY *dict2 )
+{
+        DICTIONARY    *dict = NULL;
+        STRING_ENTRY  *se, *se2;
+        long          initial_string_count, initial_hash_chains, max_chain_length;
+        long          i, string_index;
+
+        /***********
+        **  Initialize the new dictionary.
+        ***********/
+
+        if ( dict1==NULL || dict2==NULL )
+                goto err_exit;
+        if ((dict=(DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL)
+                goto err_exit;
+
+        initial_string_count = dict1->string_max;
+        initial_hash_chains = dict1->table_size;
+        max_chain_length = dict1->allowable_chain_length;
+        dict = dict_create( 4,
+                            initial_string_count,
+                            initial_hash_chains,
+                            max_chain_length );
+
+        /***********
+        **  Copy the entries from dict1 into the new dictionary.
+        ***********/
+
+        for ( i = 0 ; i < dict1->entry_count ; i++ ) {
+                se = (STRING_ENTRY*)&dict1->string_table[i];
+                if ( se->count > 0 ) {
+                        se2 = dict_insert(
+                                  dict,
+                                  dict1->string_array+se->string_offset,
+                                  se->count,
+                                  se->flags,
+                                  NULL,
+                                  &string_index );
+                        if ( se2 == NULL )
+                                goto err_exit;
+                } /* endif */
+        } /* endfor */
+
+        /*  Merge the entries from dict2 into the new dictionary. */
+        if ( dict_merge(dict,dict2,FALSE) == FALSE )
+                goto err_exit;
+
+        /*  Success. Return a pointer to the new dictionary.  */
+        return( dict );
+
+        /*  Failure. Ignominiously erase our tracks and return NULL. */
+err_exit:
+        dict_destroy( dict );
+        return NULL;
+}
+
+/*************************************************************************
+*       dict_merge: merges the contents of a dictionary into
+*       another one, updating the contents of the destination
+*
+*       dst - dictionary to update
+*       src - dictionary to add
+*       move - boolean flag
+*               TRUE - move to dest
+*               FALSE - copy to dest
+*
+*       Returns: status code
+*               TRUE - merge completed
+*               FALSE - error on merge
+*
+*       Notes: entries of the same string have their counts
+*       added. At the end of a move, src is empty. If there
+*       is an error during a move, the contents of both
+*       dictionaries cannot be trusted.
+*************************************************************************/
+BOOLEANC dict_merge(const DICTIONARY *dst, const DICTIONARY *src, const BOOLEANC move)
+{
+        STRING_ENTRY  *se, *se2;
+        DICTIONARY    *dict1, *dict2;
+        long          i, string_index, index;
+
+        dict1 = (DICTIONARY*)src;
+        dict2 = (DICTIONARY*)dst;
+
+        /***********
+        **  Copy the dictionary entries into the new dictionary.
+        ***********/
+
+        for ( i = 0 ; i < dict1->entry_count ; i++ ) {
+                se = (STRING_ENTRY*)&dict1->string_table[i];
+                if ( se->count > 0 ) {
+                        se2 = dict_insert(
+                                  dict2,
+                                  dict1->string_array+se->string_offset,
+                                  se->count,
+                                  se->flags,
+                                  NULL,
+                                  &string_index );
+                        if ( se2 == NULL )
+                                goto err_exit;
+                } /* endif */
+        } /* endfor */
+
+        /***********
+        **  Set up the dictionary parameter vector.
+        ***********/
+
+        if ( dict_set_parm_values(dict2) == FALSE )
+                return( FALSE );
+
+        /***********
+        **  Update the table of contents.
+        **     PARM HASH STTB STAR
+        ***********/
+
+        if ( (index=dict_toc_index(dict2,"HASH")) == -1)
+                goto err_exit;
+        dict2->toc[index].size = dict2->table_size * sizeof(long);
+        dict2->toc[index].ptr = dict2->chains;
+
+        if ( (index=dict_toc_index(dict2,"STTB")) == -1)
+                goto err_exit;
+        dict2->toc[index].size = dict2->string_max * sizeof(char);
+        dict2->toc[index].ptr = dict2->string_table;
+
+        if ( (index=dict_toc_index(dict2,"STAR")) == -1)
+                goto err_exit;
+        dict2->toc[index].size = dict2->array_size * sizeof(STRING_ENTRY);
+        dict2->toc[index].ptr = dict2->string_array;
+
+        /***********
+        **  Update the signature
+        ***********/
+
+        dict2->sig->checksum =
+                compute_checksum( 4*sizeof(DICT_TOC_ENTRY) , (char*)(dict2->toc) );
+
+        /***********
+        **  If this is a move, destroy the source dictionary
+        ***********/
+
+        if ( move == TRUE )
+                if ( dict_destroy(dict1) == FALSE )
+                        goto err_exit;
+
+        /***********
+        **  Success
+        ***********/
+
+        return( TRUE );
+
+        /***********
+        **  Failure
+        ***********/
+
+err_exit:
+        dict_destroy( dict2 );
+        return FALSE;
+}
+
+/*************************************************************************
+*       dict_string_by_number: return string pointer for
+*       a given string number
+*
+*       number - string number
+*
+*       Returns: pointer to string entry
+*               NULL - entry not found
+*               non-NULL - pointer to string entry
+*************************************************************************/
+STRING_ENTRY *dict_string_by_number(const DICTIONARY *dict, const long number)
+{
+        if (dict == NULL)
+                return NULL;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                return NULL;
+
+        /* string number has to be within range */
+        if (number < 0 || number >= dict->entry_count)
+                return NULL;
+
+        return dict->string_table+number;
+}
+
+/*************************************************************************
+*       dict_scan_begin: begin a scan of the dictionary
+*
+*       dict - pointer to dictionary to scan
+*
+*       Returns: status code
+*               TRUE - scan initialized
+*               FALSE - error
+*
+*       Notes: if a scan is already in progress, it is
+*       abandoned and the scan is reset to the
+*       beginning.
+*************************************************************************/
+BOOLEANC dict_scan_begin(DICTIONARY *dict)
+{
+        /* have to point to something */
+        if (dict == NULL)
+                return FALSE;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                return FALSE;
+
+        /* point to first entry in string table */
+        dict->scan_string_index = 0;
+        return TRUE;
+}
+
+/*************************************************************************
+*       dict_scan_next: get the next entry in a scan
+*
+*       dict - pointer to dictionary to continue scanning
+*
+*       Returns: pointer to string entry
+*               NULL - no more entries
+*               non-NULL - next string entry
+*************************************************************************/
+STRING_ENTRY *dict_scan_next(DICTIONARY *dict)
+{
+        /* have to point to something */
+        if (dict == NULL)
+                return NULL;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                return NULL;
+
+        /* scan index has to be within range */
+        if (dict->scan_string_index < 0
+                        || dict->scan_string_index > dict->entry_count)
+                return NULL;
+
+        /* for first non-empty table entry */
+        while (dict->scan_string_index < dict->entry_count
+                        && dict->string_table[dict->scan_string_index].count == 0)
+                dict->scan_string_index++;
+
+        /* past end of table? */
+        if (dict->scan_string_index >= dict->entry_count)
+                return NULL;
+
+        return &dict->string_table[dict->scan_string_index++];
+}
+
+
+/*************************************************************************
+*       dict_load - load a compiled dictionary into memory
+*                   creates a new dictionary
+*
+*       fname - fully qualified file name
+*
+*       Returns: pointer to created dictionary structure
+*                (NULL on failure)
+*************************************************************************/
+DICTIONARY *dict_load(const char *fname)
+{
+        DICTIONARY       *dict = NULL;
+        int              code, index;
+        FILE             *fi;
+        int              ntoc;
+
+        if ( (fi=fopen((char*)fname,"rb")) == NULL ) {
+                signal_error( "dict_load: could not open file" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+
+        if ((dict = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) {
+                /* signal_error( "dict_load: alloc failed" , "" , 0 ); */
+                goto err_exit;
+        } /* endif */
+
+        /* Read the dictionary signature record */
+        if ((dict->sig = (DICT_SIG *)malloc(sizeof(DICT_SIG))) == NULL) {
+                goto err_exit;
+        } /* endif */
+        code = block_read( fi , (char*)(dict->sig) , sizeof(DICT_SIG) , 0 );
+        if ( code == -1 ) {
+                signal_error( "dict_load: could not read signature" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+
+        if ( dict->sig->check_value != DICT_VALIDATE ) {
+                signal_error( "dict_load: could not validate file" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+        dict->check_value = dict->sig->check_value;
+
+        /* Read the dictionary Table of Contents */
+        ntoc = dict->sig->toc_size;
+        dict->toc = (DICT_TOC_ENTRY *) malloc( ntoc * sizeof(DICT_TOC_ENTRY) );
+        if ( dict->toc == NULL ) {
+                signal_error( "dict_load: alloc of TOC failed" , "" , 0 );
+                goto err_exit;
+        } /* endif */
+        code = block_read( fi ,
+                           (char*)(dict->toc) ,
+                           ntoc * sizeof(DICT_TOC_ENTRY) ,
+                           sizeof(DICT_SIG) );
+        if ( code == -1 ) {
+                signal_error( "dict_load: could not read Table of Contents" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+
+        /* Read the dictionary parameters */
+        dict->parm = (DICT_PARM_ENTRY *)
+                         dict_load_block( dict , "PARM" , fi , NULL );
+        if ( dict->parm == NULL ) {
+                signal_error( "dict_load: could not load parameter table" , "" , 0 );
+                goto err_exit;
+        } /* endif */
+        index = dict_toc_index( dict , "PARM" );
+        dict->sig->nparms = dict->toc[index].size / sizeof(DICT_PARM_ENTRY);
+
+        /* Set the parameter values in the dictionary structure */
+        if ( dict_set_parm_variables(dict) == FALSE )
+                goto err_exit;
+
+        /* Load the string array */
+        dict->string_array = (char *)
+                dict_load_block( dict , "STAR" , fi , NULL );
+        if ( dict->string_array == NULL ) {
+                signal_error( "dict_load: could not load string array" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+
+        /* Load the string table */
+        dict->string_table = (STRING_ENTRY *)
+                dict_load_block( dict , "STTB" , fi , NULL );
+        if ( dict->string_table == NULL ) {
+                signal_error( "dict_load: could not load string table" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+
+        /* Load the hash table */
+        dict->chains = (long *)
+                dict_load_block( dict , "HASH" , fi , NULL );
+        if ( dict->chains == NULL ) {
+                signal_error( "dict_load: could not load hash table" , (char*)fname , 0 );
+                goto err_exit;
+        } /* endif */
+
+        /*  Initialize the hook for dictionary extensions  */
+        dict->ext = NULL;
+
+        /*  Success  */
+        fclose( fi );
+        return( dict );
+
+        /*  Failure  */
+err_exit:
+        if ( fi != NULL )
+                fclose( fi );
+        dict_destroy( dict );
+        return NULL;
+}
+
+
+/*************************************************************************
+*       dict_save - save a dictionary from memory into a file
+*
+*       dict - pointer to dictionary to save
+*       fname - full qualified file name prefix of dictionary
+*
+*       Returns: status code
+*               TRUE - dictionary was saved sucessfully
+*               FALSE - error during save
+*************************************************************************/
+BOOLEANC dict_save( DICTIONARY *dict, const char *fname )
+{
+        int   index, ret_code;
+        FILE  *fo = NULL;
+
+        /* Have to be pointing at a valid dictionary */
+        if ( dict == NULL || dict->sig->check_value != DICT_VALIDATE )
+                goto err_exit;
+
+        /* Open the file for output */
+        if ( (fo=fopen((char*)fname,"wb")) == NULL )
+                goto err_exit;
+
+        /*  Make the table of contents entries current  */
+        /*  Note: This will not be necessary once the data is stored in EVECTORs  */
+
+        if ( (index=dict_toc_index(dict,"PARM")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->sig->nparms * sizeof(DICT_PARM_ENTRY);
+        dict->toc[index].ptr = dict->parm;
+
+        if ( (index=dict_toc_index(dict,"STAR")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->array_size * sizeof(char);
+        dict->toc[index].ptr = dict->string_array;
+
+        if ( (index=dict_toc_index(dict,"STTB")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->string_max * sizeof(STRING_ENTRY);
+        dict->toc[index].ptr = dict->string_table;
+
+        if ( (index=dict_toc_index(dict,"HASH")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->table_size * sizeof(long);
+        dict->toc[index].ptr = dict->chains;
+
+        /*  Reset the TOC offsets and checksums for ALL tables  */
+        /*  (not just type=0 tables)                            */
+        dict_reset_toc_offsets( dict );
+
+        /* Set the dictionary parm structure from the parameter values */
+        if ( dict_set_parm_values(dict) == FALSE )
+                goto err_exit;
+
+        /* Save the signature */
+        dict->sig->checksum = compute_checksum( sizeof(DICT_SIG) , (char*)(dict->sig) );
+        ret_code = block_write( fo ,
+                                (char*)dict->sig ,
+                                sizeof(DICT_SIG) );
+        if ( ret_code == -1 )
+                goto err_exit;
+
+        /* Save the table of contents */
+        ret_code = block_write( fo,
+                                (char*)dict->toc,
+                                dict->sig->toc_size * sizeof(DICT_TOC_ENTRY) );
+        if ( ret_code == -1 )
+                goto err_exit;
+
+        /* Save the tables */
+        /* For now, only save type=0 tables */
+        for ( index = 0 ; index < dict->sig->toc_size ; index++ ) {
+                if ( dict->toc[index].type == 0 ) {  /* Ordinary table */
+                        ret_code = dict_save_block( dict , dict->toc[index].id , fo );
+                        if ( ret_code == FALSE )
+                                goto err_exit;
+                     } /* endif */
+        } /* endfor */
+
+        /*  Success  */
+        fclose( fo );
+        return TRUE;
+
+        /*  Failure  */
+err_exit:
+        if ( fo != NULL )
+                fclose( fo );
+        return FALSE;
+}
+
+
+/*************************************************************************
+*       dict_import: read in an ASCII dictionary.
+*
+*       dict_fname - name of dictionary file
+*       parameters to create a DICTIONARY structure (see dict_create)
+*
+*       Returns: pointer to created DICTIONARY structure
+*                (NULL on failure)
+*
+*************************************************************************/
+DICTIONARY *dict_import( const char *dict_fname ,
+                         const long initial_string_count ,
+                         const long initial_hash_entries ,
+                         const long max_chain_length )
+{
+        DICTIONARY     *dict;
+        char           buffer[BUFLEN], ch;
+        int            index, c, c0;
+        long           number;
+        FILE           *fi = NULL;
+
+        /***********
+        **  Dictionary setup.
+        ***********/
+
+        dict = dict_create( 4,
+                            initial_string_count ,
+                            initial_hash_entries ,
+                            max_chain_length );
+        if ( dict == NULL )
+           goto err_exit;
+
+        /***********
+        **  Read the dictionary file
+        **  Each line should have one word or a string delimited by '|'
+        ***********/
+
+        if ( (fi=fopen(dict_fname,"r")) == NULL )
+                goto err_exit;
+        while( fgets(buffer,BUFLEN,fi) != NULL ) {
+                c0 = 0;
+                   /*  Skip to non-blank  */
+                while ( (c0<BUFLEN-2) && (buffer[c0]==' ') ) ++c0;
+                if ( buffer[c0] == '|' ) {
+                        c = ++c0;
+                        ch = '|';
+                } else {
+                        c = c0;
+                        ch = ' ';
+                } /* endif */
+                   /*  Scan to blank or matching '|' */
+                while ( (c<BUFLEN-1) && (buffer[c]!='\0') &&
+                        (buffer[c]!='\n') && (buffer[c]!=ch)  )
+                        ++c;
+                buffer[c] = '\0';
+                   /*  Insert the word  */
+                if ( dict_insert(dict,buffer+c0,1,0,NULL,&number) == NULL )
+                        goto err_exit;
+        } /* endwhile */
+
+        /***********
+        **  Fill in the dictionary parameter vector.
+        ***********/
+
+        if ( dict_set_parm_values(dict) == FALSE )
+                goto err_exit;
+
+        /***********
+        **  Update the table of contents for HASH STTB STAR
+        ***********/
+
+        if ( (index=dict_toc_index(dict,"HASH")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->table_size * sizeof(long);
+
+        if ( (index=dict_toc_index(dict,"STTB")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->string_max * sizeof(char);
+
+        if ( (index=dict_toc_index(dict,"STAR")) == -1 )
+                goto err_exit;
+        dict->toc[index].size = dict->array_size * sizeof(STRING_ENTRY);
+
+           /*  Success. Return a pointer to the new dictionary.  */
+        fclose(fi);
+        return( dict );
+
+           /*  Failure. Ignominiously erase our tracks and return NULL.  */
+err_exit:
+        if ( fi != NULL )
+           fclose(fi);
+        dict_destroy( dict );
+        return NULL;
+}
+
+/*************************************************************************
+*       dict_export - save an extended dictionary from memory into
+*                     an ASCII file
+*
+*       dict - pointer to dictionary to save
+*       fname - full qualified file name prefix of dictionary
+*
+*       Returns: status code
+*               TRUE - dictionary was saved sucessfully
+*               FALSE - error during save
+*
+*************************************************************************/
+BOOLEANC dict_export( DICTIONARY *dict , const char *fname )
+{
+        FILE          *fp = NULL;
+        STRING_ENTRY  *se;
+        int       i;
+
+        /* have to point to something */
+        if (dict == NULL)
+                goto err_exit;
+
+        /* check value has to be OK */
+        if (dict->check_value != DICT_VALIDATE)
+                goto err_exit;
+
+        /* must have a filename */
+        if (fname == NULL)
+                goto err_exit;
+
+        fp = fopen( (char*)fname , "w" );
+        if ( fp == NULL )
+                goto err_exit;
+
+        for ( i = 0 ; i < dict->entry_count ; i++ ) {
+           se = &dict->string_table[i];
+           fprintf( fp , "|%s|\n" , dict->string_array + se->string_offset );
+        } /* endfor */
+        fclose( fp );
+
+        /*  Success.  */
+        fclose(fp);
+        return TRUE;
+
+        /*  Failure.  */
+err_exit:
+        if ( fp != NULL )
+           fclose(fp);
+        dict_destroy( dict );
+        return FALSE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/dict.h	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,216 @@
+/*
+    dict.h
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1995 Herb Chong
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+/*-----------------
+  Change history:
+  (AK:04/03/95) - Added hook for extensions to dictionary structure.
+  (AK:04/04/95) - Added dictionary signature, table of contents and parameter
+                  structure defintions and fields in DICTIONARY structure
+-------------------*/
+
+#ifndef dict_h_included
+#define dict_h_included
+
+static char dict_sccsid[] = "%W% %G%";
+
+#ifndef TRUE
+#define TRUE    1
+#define FALSE   0
+#endif
+
+#define DICT_NONE       0x00000000              /* no flags set */
+#define DICT_VALIDATE   0xdeadbeef              /* look for this in every dictionary structure */
+#define DICT_ENTRY_NONE -1                      /* index of invalid entry */
+
+typedef unsigned char BOOLEANC;
+
+/*------------------
+  string table entry
+  ------------------*/
+typedef struct string_entry {
+        long    string_offset;       /* offset in string array */
+        long    count;               /* number of occurrences of string */
+        long    next;                /* offset of next in hash chain */
+        void    *any_ptr;            /* free pointer */
+        unsigned long flags;         /* user definable flag value for string */
+        unsigned long hash_value;    /* hash value of string using hash function */
+} STRING_ENTRY;
+
+/*--------------------
+  Dictionary signature (AK:04/04/95)
+  --------------------*/
+typedef struct dict_sig_ {      /*  Dictionary signature               */
+   unsigned long  check_value;  /*  0xdeadbeef                         */
+   int            toc_size;     /*  # of entries in Table of Contents  */
+   long           nparms;       /*  # of parameters                    */
+   unsigned long  checksum;     /*  Checksum for TOC                   */
+} DICT_SIG;
+
+/*----------------------------------
+  Dictionary table of contents entry (AK:04/04/95)
+  ----------------------------------*/
+typedef struct TOC_entry_ {     /*  Dictionary table of contents entry  */
+   char           id[5];        /*  Field identifier:                   */
+   long           offset;       /*  Offset (bytes) of entry in file     */
+   long           size;         /*  Size (bytes) of entry               */
+   void           *ptr;         /*  Where entry is stored in memory     */
+   unsigned long  checksum;     /*  Checksum of entry                   */
+   int            type;         /*  0=ordinary ; 1=EVECTOR ; 2=NULL     */
+} DICT_TOC_ENTRY;
+
+/*--------------------------------
+  Dictionary parameter table entry (AK:04/04/95)
+  --------------------------------*/
+typedef struct dict_parm_entry_ {   /*  Dictionary parameter table entry  */
+   char           id[13];           /*  Parameter identifier              */
+   unsigned long  value;            /*  Parameter value                   */
+} DICT_PARM_ENTRY;
+
+
+/*---------------------------
+  Hash dictionary information
+  ---------------------------*/
+typedef struct dictionary {
+        unsigned long   check_value;            /* check validation value */
+        unsigned long   flags;                  /* flag values */
+        long    entry_count;                    /* number of used entries in each table */
+
+        char    *string_array;                  /* storage for strings */
+        long    array_size;                     /* number of bytes allocated */
+        long    array_used;                     /* number of bytes occupied */
+        int     array_growth_count;             /* number of times grown */
+
+        STRING_ENTRY    *string_table;          /* string table */
+        long    string_max;                     /* max number of entries in string table */
+        long    scan_string_index;              /* current index into string table for scan */
+        int     string_growth_count;            /* number of times had to grow string table */
+
+        long            *chains;                /* vector of array indices to hash entries */
+        int     longest_chain_length;           /* longest chain length in hash table */
+        int     allowable_chain_length;         /* chain lengths always < this */
+        long    table_size;                     /* number of elements in hash entries vector */
+        unsigned long   hash_mask;              /* mask for doing mod() function */
+        int     hash_growth_count;              /* number of times had to grow hash table */
+
+        void    *ext;                           /* Hook for extensions to the dictionary (AK:04/03/95) */
+
+        DICT_SIG         *sig;                  /* Signature (AK:04/04/95) */
+        DICT_TOC_ENTRY   *toc;                  /* Table of contents (AK:04/04/95) */
+        DICT_PARM_ENTRY  *parm;                 /* Parameters (AK:04/04/95) */
+
+} DICTIONARY;
+
+/*--------------------------------------------------------------
+  dict_create: create a dictionary and initialize its structures
+  --------------------------------------------------------------*/
+extern DICTIONARY *dict_create(
+        const long  toc_size,
+        const long  initial_string_count,
+        const long  initial_hash_entries,
+        const long  max_chain_length );
+
+/*-------------------------------------------------
+  dict_delete: deletes an entry from the dictionary
+  -------------------------------------------------*/
+extern BOOLEANC dict_delete(
+        const DICTIONARY  *dict,
+        const char        *s,
+        const long        count );
+
+/*---------------------------------------------------
+  dict_destroy: discard a dictionary and its contents
+  ---------------------------------------------------*/
+extern BOOLEANC dict_destroy(
+        DICTIONARY  *dict );
+
+/*------------------------------------------------
+  dict_export: write a dictionary to an ASCII file
+  ------------------------------------------------*/
+extern BOOLEANC dict_export(
+        DICTIONARY  *dict,
+        const char  *fname );
+
+/*-------------------------------------------------
+  dict_import: read a dictionary from an ASCII file
+  -------------------------------------------------*/
+extern DICTIONARY *dict_import(
+        const char  *dict_fname,
+        const long  initial_string_count,
+        const long  initial_hash_entries,
+        const long  max_chain_length );
+
+/*------------------------------------------------------------------
+  dict_insert: add entries into the dictionary, growing as necessary
+  ------------------------------------------------------------------*/
+extern STRING_ENTRY *dict_insert(
+        DICTIONARY           *dict,
+        char                 *s,
+        const long           occurences,
+        const unsigned long  flags,
+        void                 *any_ptr,
+        long                 *number );
+
+/*----------------------------------------------------
+  dict_load: read a dictionary from a file into memory
+  ----------------------------------------------------*/
+extern DICTIONARY *dict_load(
+        const char  *fname );
+
+/*-----------------------------------------------------------------
+  dict_merge: merges the contents of a dictionary into another one,
+  updating the contents of the destination
+  -----------------------------------------------------------------*/
+extern BOOLEANC dict_merge(
+        const DICTIONARY  *dst,
+        const DICTIONARY  *src,
+        const BOOLEANC    move );
+
+/*----------------------------------------------------
+  dict_save: save a dictionary from memory into a file
+  ----------------------------------------------------*/
+extern BOOLEANC dict_save(
+        DICTIONARY  *dict,
+        const char  *fname );
+
+/*-----------------------------------------------
+  dict_scan_begin: begin a scan of the dictionary
+  -----------------------------------------------*/
+extern BOOLEANC dict_scan_begin(
+        DICTIONARY  *dict );
+
+/*--------------------------------------------
+  dict_scan_next: get the next entry in a scan
+  --------------------------------------------*/
+extern STRING_ENTRY *dict_scan_next(
+        DICTIONARY  *dict );
+
+/*-----------------------------------------------
+  dict_search: look for entries in the dictionary
+  -----------------------------------------------*/
+extern STRING_ENTRY *dict_search(
+        const DICTIONARY  *dict,
+        const char        *s,
+        long              *number );
+
+/*----------------------------------------------------------------------
+  dict_string_by_number: return string pointer for a given string number
+  ----------------------------------------------------------------------*/
+extern STRING_ENTRY *dict_string_by_number(
+        const DICTIONARY  *dict,
+        const long        number );
+
+/*----------------------------------------------------------
+  dict_union: merges contents of 2 dictionaries into a third
+  ----------------------------------------------------------*/
+extern DICTIONARY *dict_union(
+        const DICTIONARY  *dict1,
+        const DICTIONARY  *dict2 );
+
+#endif  /* dict_h_included */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/dictutil.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,438 @@
+/*
+    dictutil.c
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1995 Herb Chong
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+/*************************************************************************
+**   Utilities for Dictionary Maintenence Functions
+*************************************************************************/
+
+static char sccsid[] = "%W% %G%";
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#if !defined(OS2) && !defined(_WIN32)
+   #include <unistd.h>
+#else
+   #include <io.h>
+   #include <fcntl.h>
+#endif
+
+#include "dict.h"
+#include "dictutil.h"
+#include "rogue.h"
+
+int   trace;
+FILE  *ft;
+
+
+/***********
+**  Read 'count' characters into 'buffer' at 'offset' in a binary file
+**  Return 0 on success; -1 on failure;
+***********/
+
+int block_read( FILE *fi , char *buffer , size_t count , long offset )
+{
+   if ( fseek(fi,offset,SEEK_SET) == -1 )
+      return( -1 );
+
+   if ( fread(buffer,1,count,fi) != count )
+      return( -1 );
+   return( 0 );
+}
+
+/***********
+**  Write 'count' characters from 'buffer' to a binary file.
+**  Return -1 on failure; 0 on success.
+***********/
+
+int block_write( FILE *fo , char *buffer , size_t count )
+{
+   if ( fwrite(buffer,1,count,fo) != count )
+      return( -1 );
+   return( 0 );
+}
+
+/***********
+**  Load a dictionary table entry with id TOC_id into memory pointed to by block.
+**  Update the dictionary TOC.
+**  If *block=NULL, allocate the block of memory.
+**  Return 0 on success; -1 on failure.
+**  Set dt_entry->ptr to where the block is stored.
+***********/
+
+void *dict_load_block( DICTIONARY *dict , char *toc_id ,
+                          FILE *fi , void *block )
+{  DICT_TOC_ENTRY  *dt_entry;
+   static void     *ptr;
+   int             index, ret_code;
+
+   index = dict_toc_index( dict , toc_id );
+   if ( index != -1 ) {  /* Found the id */
+      dt_entry = &(dict->toc[index]);
+   } else {
+      signal_error( "dict_load_block: could not find TOC_id" , toc_id , 1 );
+      return( NULL );
+   } /* endif */
+
+   if ( block == NULL ) {
+      ptr = malloc( dt_entry->size );
+      if ( trace > 3 ) {
+         fprintf( ft , "\ndict_load_block allocates %lx bytes at location %p\n" ,
+                   dt_entry->size , ptr );
+      } /* endif */
+   } else {
+      ptr = block;
+      if ( trace > 3 ) {
+         fprintf( ft , "\ndict_load_block uses memory at location %p\n" , ptr );
+      } /* endif */
+   } /* endif */
+   if ( ptr == NULL ) {
+      signal_error( "dict_load_block: alloc failed " , toc_id , 1 );
+      return( NULL );
+   } /* endif */
+
+   ret_code = block_read( fi ,
+                          (char*)ptr ,
+                          dt_entry->size ,
+                          dt_entry->offset );
+   if ( ret_code == -1 )
+      return( NULL );
+
+   if ( dt_entry->checksum !=
+        compute_checksum( dt_entry->size , (char*)ptr ) ) {
+      signal_error( "dict_load_block: invalid checksum ", toc_id, 1);
+      return( NULL );
+   } /* endif */
+
+   dt_entry->ptr = ptr;
+
+   if ( trace > 3 ) {
+      fprintf( ft , "\nLoaded block\nTOC entry:    id:%s  offset:%lx  size:%lx  ptr:%p  checksum:%lx  type:%d\n" ,
+               dict->toc[index].id , dict->toc[index].offset ,
+               dict->toc[index].size , dict->toc[index].ptr ,
+               dict->toc[index].checksum , dict->toc[index].type );
+   } /* endif */
+
+   return( ptr );
+}
+
+/***********
+**  Save a dictionary table entry.
+**  Update the dictionary TOC entry offset and checksum fields.
+**  Return 0 on success, -1 on failure.
+**  Note: It is assumed that the size and pointer fields in TOC entry are
+**        already up to date; i.e., that they are consistent with the current
+**        location and size of the block being written. This is essential
+**        because the table of contents must have already been written
+**        into the file.
+***********/
+
+BOOLEANC dict_save_block( DICTIONARY *dict , char *toc_id , FILE *fo )
+{  DICT_TOC_ENTRY   *dt_entry;
+   int              index, ret_code;
+   char             *block;
+
+   index = dict_toc_index( dict , toc_id );
+   if ( index == -1 ) {
+      signal_error( "dict_save_block: id not found " , toc_id , 1 );
+      return( FALSE );
+   } /* endif */
+   dt_entry = &(dict->toc[index]);
+   block = (char*)(dt_entry->ptr);
+
+   if ( block == NULL ) {
+      signal_error( "dict_save_block: NULL block " , toc_id , 1 );
+      return( FALSE );
+   } /* endif */
+
+   /* dt_entry->offset = fseek( fo , 0 , SEEK_END ); */
+   dt_entry->checksum = compute_checksum( dt_entry->size , block );
+   ret_code = block_write( fo , dt_entry->ptr , dt_entry->size );
+   if ( ret_code == -1 ) {
+      signal_error( "dict_save_block: block_write failed " , toc_id , 1 );
+      return( FALSE );
+   } /* endif */
+
+   if ( trace > 3 ) {
+      fprintf( ft , "\nStored block\nTOC entry:           id:%s  offset:%lx  size:%lx  ptr:%p  checksum:%lx  type:%d\n" ,
+               dict->toc[index].id , dict->toc[index].offset ,
+               dict->toc[index].size , dict->toc[index].ptr ,
+               dict->toc[index].checksum , dict->toc[index].type );
+   } /* endif */
+
+   return( TRUE );
+}
+
+/***********
+**  Look up and id in the table of contents.
+**  Return its index (-1 on failure).
+***********/
+
+int dict_toc_index( DICTIONARY *dict , char *toc_id )
+{  int index;
+
+   for ( index = 0 ; index < dict->sig->toc_size ; index++ ) {
+           if ( strcmp(dict->toc[index].id,toc_id) == 0 )
+                   return( index );
+   } /* endfor */
+
+   return( -1 );
+}
+
+/***********
+**  Compute a block checksum.
+**  (Currently just returns 0.)
+***********/
+
+unsigned long compute_checksum( size_t size , char *block )
+{
+    NOOP(size);
+    NOOP(block);
+    return( 0 );
+}
+
+/***********
+**  Create a dictionary paramter entry.
+***********/
+
+DICT_PARM_ENTRY *dict_make_parm_entry( char *id , unsigned long value )
+{  static DICT_PARM_ENTRY  *entry;
+
+   entry = (DICT_PARM_ENTRY *) malloc( sizeof(DICT_PARM_ENTRY) );
+   if ( entry == NULL )
+      return(NULL);
+
+   strncpy( entry->id , id , 13 );
+   entry->value = value;
+
+   return( entry );
+}
+
+/***********
+**  Look up and id in the parameter array.
+**  Return its index (-1 on failure).
+***********/
+
+int dict_parm_index( DICTIONARY *dict , char *parm_id )
+{  long index;
+
+   for ( index = 0 ; index < dict->sig->nparms ; index++ ) {
+           if ( strcmp( dict->parm[index].id , parm_id ) == 0 )
+                   return( (int) index );
+   } /* endfor */
+
+   return( -1 );
+}
+
+/***********
+**  Reset table of contents offsets and checksums
+**  in preparation for dict_save().
+***********/
+
+BOOLEANC dict_reset_toc_offsets( DICTIONARY *dict )
+{  int  i;
+   long offset;
+
+   offset = sizeof(DICT_SIG)
+          + dict->sig->toc_size * sizeof(DICT_TOC_ENTRY);
+   for ( i = 0 ; i < dict->sig->toc_size ; i++ ) {
+      dict->toc[i].offset = offset;
+      offset += dict->toc[i].size;
+      dict->toc[i].checksum =
+         compute_checksum( dict->toc[i].size , dict->toc[i].ptr );
+   } /* endfor */
+
+   return( TRUE );
+}
+
+/***********
+**  Load the names of the dictionary parameters.
+**  14 parms
+***********/
+
+BOOLEANC dict_set_parm_ids( DICTIONARY *dict )
+{
+   if ( dict==NULL || dict->sig == NULL ) {
+      signal_error( "dict_set_parm_ids: Allocate dict and signature first." , "" , 0 );
+      return( FALSE );
+   }
+   dict->sig->nparms = 14;
+   strcpy( dict->parm[0].id , "FLAGS_______" );
+   strcpy( dict->parm[1].id , "ENTRY_COUNT_" );
+   strcpy( dict->parm[2].id , "ARRAY_SIZE__" );
+   strcpy( dict->parm[3].id , "ARRAY_USED__" );
+   strcpy( dict->parm[4].id , "ARR_GROW_CT_" );
+   strcpy( dict->parm[5].id , "STRING_MAX__" );
+   strcpy( dict->parm[6].id , "STR_GROW_CT_" );
+   strcpy( dict->parm[7].id , "LONG_CHAIN__" );
+   strcpy( dict->parm[8].id , "ALLOW_CHAIN_" );
+   strcpy( dict->parm[9].id , "HASH_TAB_SIZ" );
+   strcpy( dict->parm[10].id , "HASH_MASK___" );
+   strcpy( dict->parm[11].id , "HASH_GROW_CT" );
+   strcpy( dict->parm[12].id , "CHECK_VALUE_" );
+   strcpy( dict->parm[13].id , "SCAN_STR_IX_" );
+
+   return( TRUE );
+}
+
+/***********
+**  Set the dictionary parm structure from the values in the dict structure.
+**  14 parms
+***********/
+
+BOOLEANC dict_set_parm_values( DICTIONARY *dict )
+{  int  index;
+
+   if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->flags;
+
+   if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->entry_count;
+
+   if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->array_size;
+
+   if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->array_used;
+
+   if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->array_growth_count;
+
+   if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->string_max;
+
+   if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->string_growth_count;
+
+   if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->longest_chain_length;
+
+   if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->allowable_chain_length;
+
+   if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->table_size;
+
+   if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->hash_mask;
+
+   if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->hash_growth_count;
+
+   if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->check_value;
+
+   if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 )
+      return( FALSE );
+   dict->parm[index].value = (unsigned long)dict->scan_string_index;
+
+   return( TRUE );
+}
+
+
+/***********
+**  Set the values in the dict structure from the dictionary parm structure.
+**  14 parms
+***********/
+
+BOOLEANC dict_set_parm_variables( DICTIONARY *dict )
+{  int  index;
+
+   if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 )
+      return( FALSE );
+   dict->flags = (unsigned long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 )
+      return( FALSE );
+   dict->entry_count = (long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 )
+      return( FALSE );
+   dict->array_size = (long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 )
+      return( FALSE );
+   dict->array_used = (long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 )
+      return( FALSE );
+   dict->array_growth_count = (int)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 )
+      return( FALSE );
+   dict->string_max = (long)dict->parm[index].value ;
+
+   if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 )
+      return( FALSE );
+   dict->string_growth_count = (int)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 )
+      return( FALSE );
+   dict->longest_chain_length = (int)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 )
+      return( FALSE );
+   dict->allowable_chain_length = (int)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 )
+      return( FALSE );
+   dict->table_size = (long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 )
+      return( FALSE );
+   dict->hash_mask = (unsigned long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 )
+      return( FALSE );
+   dict->hash_growth_count = (int)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 )
+      return( FALSE );
+   dict->check_value = (unsigned long)dict->parm[index].value;
+
+   if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 )
+      return( FALSE );
+   dict->scan_string_index = (long)dict->parm[index].value;
+
+   return( TRUE );
+}
+
+/***********
+**  If trace (global) > 0 , signal an error
+**  If severity > 0 , abort
+***********/
+
+void signal_error( char *header , char *message , int severity )
+{
+  FILE *fpe;
+
+  if ( trace > 0 ) {
+     printf( "%s: %s\n" , header , message );
+     fpe = fopen( "ERROR.FIL" , "a" );
+     fprintf( fpe , "\n%s: %s\n" , header , message );
+     fclose( fpe );
+  } /* endif */
+
+  if ( severity > 0 )
+     abort();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/dictutil.h	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,116 @@
+/*
+    dictutil.h
+
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1995 Herb Chong
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+/****************************************************************/
+/*     HEADERS FOR DICTIONARY MAINTENENCE ROUTINE UTILITIES     */
+/****************************************************************/
+
+#ifndef dictutil_h_included
+#define dictutil_h_included
+
+static char dictutil_sccsid[] = "%W% %G%";
+
+#include <stdio.h>
+#include "dict.h"
+
+/*-------------------------------
+  Binary read of a block of bytes
+  -------------------------------*/
+extern int block_read(
+        FILE  *fi,
+        char  *buffer,
+        size_t  count,
+        long  offset );
+
+/*--------------------------------
+  Binary write of a block of bytes
+  --------------------------------*/
+extern int block_write(
+        FILE  *fo,
+        char  *buffer,
+        size_t  count );
+
+/*--------------------------------------
+  Compute a checksum of a block of bytes
+  --------------------------------------*/
+extern unsigned long compute_checksum(
+        size_t size,
+        char  *block );
+
+/*-----------------------------------------------------------------
+  Load a block of bytes from a compiled dictionary file into memory
+  -----------------------------------------------------------------*/
+extern void *dict_load_block(
+        DICTIONARY  *dict,
+        char        *toc_id,
+        FILE        *fi,
+        void        *block );
+
+/*-----------------------------------
+  Create a dictionary parameter entry
+  -----------------------------------*/
+extern DICT_PARM_ENTRY *dict_make_parm_entry(
+        char *id,
+        unsigned long value );
+
+/*------------------------------------
+  Look up an id in the parameter array
+  ------------------------------------*/
+extern int dict_parm_index(
+        DICTIONARY  *dict,
+        char        *parm_id );
+
+/*-------------------------------
+  Reset table of contents offsets
+  -------------------------------*/
+extern BOOLEANC dict_reset_toc_offsets(
+        DICTIONARY  *dict );
+
+/*-----------------------------------------------------------------
+  Save a block of bytes from memory into a compiled dictionary file
+  -----------------------------------------------------------------*/
+extern BOOLEANC dict_save_block(
+        DICTIONARY  *dict,
+        char        *toc_id,
+        FILE        *fo );
+
+/*--------------------------------------------------------------------
+  Set the dictionary parm values from the values in the dict structure
+  --------------------------------------------------------------------*/
+extern BOOLEANC dict_set_parm_values(
+        DICTIONARY  *dict );
+
+/*--------------------------------------------------------------------
+  Set the values in the dict structure from the dictionary parm values
+  --------------------------------------------------------------------*/
+extern BOOLEANC dict_set_parm_variables(
+        DICTIONARY  *dict );
+
+/*---------------------------
+  Set the dictionary parm ids
+  ---------------------------*/
+extern BOOLEANC dict_set_parm_ids(
+        DICTIONARY *dict );
+
+/*--------------------------------------
+  Look up an id in the table of contents
+  --------------------------------------*/
+extern int dict_toc_index(
+        DICTIONARY  *dict,
+        char        *toc_id );
+
+/*------------------------------------
+  Record and error and abort if needed
+  ------------------------------------*/
+extern void signal_error(
+	char *header,
+	char *message,
+	int severity );
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/encumb.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,185 @@
+/*
+    encumb.c - Stuff to do with encumberance
+ 
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include "rogue.h"
+
+/*
+    updpack()
+        Update his pack weight and adjust fooduse accordingly
+*/
+
+void
+updpack(void)
+{
+    int curcarry = packweight();
+
+    pstats.s_carry = totalenc();    /* update max encumb */
+
+    if (is_carrying(TR_PURSE))
+        pstats.s_carry += 1000;
+
+    foodlev = 0;
+
+    switch ((curcarry * 5) / pstats.s_carry)   /* % of total capacity */
+    {
+        case 5:     /* 100 % */
+            foodlev++;
+
+        case 4:     /* 80 % */
+            if (rnd(100) < 80)
+                foodlev++;
+
+        case 3:     /* 60 % */
+            if (rnd(100) < 60)
+                foodlev++;
+
+        case 2:     /* 40 % */
+            if (rnd(100) < 40)
+                foodlev++;
+
+        case 1:     /* 20 % */
+            if (rnd(100) < 20)
+                foodlev++;
+
+        case 0:     /* 0 % */
+            foodlev++;
+    }
+
+    pstats.s_pack = curcarry;   /* update pack weight */
+
+    if (is_carrying(TR_PURSE))  /* makes pack lighter */
+        foodlev--;
+}
+
+
+/*
+    packweight()
+        Get the total weight of the hero's pack
+*/
+
+int
+packweight(void)
+{
+    struct linked_list  *pc;
+    int weight = 0;
+
+    for (pc = pack; pc != NULL; pc = next(pc))
+    {
+        struct object   *obj = OBJPTR(pc);
+
+        weight += itemweight(obj) * obj->o_count;
+    }
+
+    if (weight < 0)     /* caused by artifacts or blessed items */
+        weight = 0;
+
+    return (weight);
+}
+
+
+/*
+    itemweight()
+        Get the weight of an object
+*/
+
+int
+itemweight(struct object *wh)
+{
+    int weight = wh->o_weight;  /* get base weight */
+    int ac;
+
+    switch (wh->o_type)
+    {
+        case ARMOR:  /* 10% for each plus or minus*/
+            ac = armors[wh->o_which].a_class - wh->o_ac;
+            weight *= (10 - ac) / 10;
+            break;
+
+        case WEAPON:
+            if ((wh->o_hplus + wh->o_dplus) > 0)
+                weight /= 2;
+    }
+
+    if (wh->o_flags & ISCURSED)
+        weight += weight / 2;   /* +50% for cursed */
+    else if (wh->o_flags & ISBLESSED)
+        weight -= weight / 5;   /* -20% for blessed */
+
+    if (weight < 0)
+        weight = 0;
+
+    return (weight);
+}
+
+
+/*
+    playenc()
+        Get hero's carrying ability above norm 50 units per point of STR
+        over 10, 300 units per plus on R_CARRYING 1000 units for TR_PURSE
+*/
+
+int
+playenc(void)
+{
+    int ret_val = (pstats.s_str - 10) * 50;
+
+    if (is_wearing(R_CARRYING))
+        ret_val += ring_value(R_CARRYING) * 300;
+
+    return (ret_val);
+}
+
+
+/*
+    totalenc()
+        Get total weight that the hero can carry
+*/
+
+int
+totalenc(void)
+{
+    int wtotal = 1400 + playenc();
+
+    switch (hungry_state)
+    {
+        case F_OK:
+        case F_HUNGRY: /* no change */
+            break;
+
+        case F_WEAK:
+            wtotal -= wtotal / 4;  /* 25% off weak */
+            break;
+
+        case F_FAINT:
+            wtotal /= 2;    /* 50% off faint */
+            break;
+    }
+
+    return (wtotal);
+}
+
+
+/*
+    hitweight()
+        Gets the fighting ability according to current weight This
+        returns a  +2 hit for very light pack weight, +1 hit
+        for light pack weight, 0 hit for medium pack weight, -1 hit for heavy
+        pack weight, -2 hit for very heavy pack weight
+*/
+
+int
+hitweight(void)
+{
+    return(3 - foodlev);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/fight.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,2175 @@
+/*
+    fight.c - All the fighting gets done here
+ 
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    All rights reserved.
+
+    Based on "Rogue: Exploring the Dungeons of Doom"
+    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+    All rights reserved.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "rogue.h"
+
+/*
+ * This are the beginning experience levels for all players all further
+ * experience levels are computed by multiplying by 2
+ */
+
+static long e_levels[10] =
+{
+    143L,           /* Fighter     */
+    182L,           /* Paladin     */
+    169L,           /* Ranger      */
+    127L,           /* Cleric      */
+    154L,           /* Druid       */
+    185L,           /* Magician    */
+    169L,           /* Illusionist */
+    112L,           /* Thief       */
+    126L,           /* Assasin     */
+    319L            /* Ninja       */
+};
+
+static struct matrix att_mat[11] =
+{
+    /* Base, Max_lvl, Factor, Offset, Range */
+
+    {  10,     17,      2,      1,      2     },  /* fi */
+    {  10,     17,      2,      1,      2     },  /* pa */
+    {  10,     17,      2,      1,      2     },  /* ra */
+    {  10,     19,      2,      1,      3     },  /* cl */
+    {  10,     19,      2,      1,      3     },  /* dr */
+    {   9,     21,      2,      1,      5     },  /* mu */
+    {   9,     21,      2,      1,      5     },  /* il */
+    {  10,     21,      2,      1,      4     },  /* th */
+    {  10,     21,      2,      1,      4     },  /* as */
+    {  10,     21,      2,      1,      4     },  /* nj */
+    {   7,     25,      1,      0,      2     }   /* mn */
+};
+
+void
+do_fight(coord dir, int tothedeath)
+{
+    int x,y;
+
+    x = dir.x;
+    y = dir.y;
+
+    if (!tothedeath && pstats.s_hpt < max_stats.s_hpt / 3)
+    {
+        msg("That's not wise.");
+
+        after = fighting = FALSE;
+        return;
+    }
+
+    if (isalpha(CCHAR(winat(hero.y + y, hero.x + x))))
+    {
+        after = fighting = TRUE;
+        do_move(y, x);
+    }
+    else
+    {
+        if (fighting == FALSE)
+            msg("Nothing there.");
+
+        after = fighting = FALSE;
+    }
+
+    return;
+}
+
+/*
+    fight()
+        The player attacks the monster.
+*/
+
+int
+fight(coord *mp, struct object *weap, int thrown)
+{
+    struct thing    *tp;
+    struct linked_list  *item;
+    int    did_hit = TRUE;
+    char    *mname;
+
+    /* Find the monster we want to fight */
+
+    if ((item = find_mons(mp->y, mp->x)) == NULL)
+    {
+        debug("Fight what @ %d,%d", mp->y, mp->x);
+        return 0;
+    }
+
+    tp = THINGPTR(item);
+
+    mname = (on(player, ISBLIND)) ? "it" : monsters[tp->t_index].m_name;
+
+    /* Since we are fighting, things are not quiet so no healing takes place */
+
+    player.t_rest_hpt = player.t_rest_pow = 0;
+    tp->t_rest_hpt = tp->t_rest_pow = 0;
+
+    /*  Let him know it was really a mimic (if it was one). */
+
+    if (off(player, ISBLIND))
+    {
+        if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise))
+        {
+            msg("Wait! That's a %s!", mname);
+            turn_off(*tp, ISDISGUISE);
+            did_hit = thrown;
+        }
+
+        if (on(*tp, CANSURPRISE))
+        {
+            turn_off(*tp, CANSURPRISE);
+            if ((player.t_ctype == C_RANGER && rnd(6) != 0) ||
+                (player.t_ctype == C_NINJA && rnd(pstats.s_lvl / 2)
+                 != 0))
+                msg("You notice a %s trying to hide!", mname);
+            else
+            {
+                msg("Wait! There's a %s!", mname);
+                did_hit = thrown;
+            }
+        }
+    }
+
+    /* Protection from Normal Missiles */
+
+    if (thrown && on(*tp, HASMSHIELD))
+    {
+        msg("The %s slows as it approaches %s.",
+            weaps[weap->o_which].w_name, mname);
+
+        did_hit = FALSE;
+    }
+
+    if (did_hit)
+    {
+        did_hit = FALSE;
+
+        if (!can_blink(tp) &&
+            (off(*tp, MAGICHIT) || (weap != NULL &&
+                   (weap->o_hplus > 0 || weap->o_dplus > 0))) &&
+            (off(*tp, BMAGICHIT) || (weap != NULL &&
+                   (weap->o_hplus > 2 || weap->o_dplus > 2))) &&
+            roll_em(&player, tp, weap, thrown, cur_weapon))
+        {
+            did_hit = TRUE;
+            tp->t_wasshot = TRUE;
+
+            if (thrown)
+            {
+                if (weap != NULL && weap->o_type == WEAPON
+                    && weap->o_which == GRENADE)
+                {
+                    hearmsg("BOOOM!");
+                    aggravate();
+                }
+
+                thunk(weap, mname);
+            }
+            else
+                hit(mname);
+
+            /* hitting a friendly monster is curtains */
+
+            if (on(*tp, ISFRIENDLY))
+            {
+                turn_off(*tp, ISFRIENDLY);
+                turn_on(*tp, ISMEAN);
+            }
+
+            /* Charmed monsters become uncharmed */
+
+            if (on(*tp, ISCHARMED))
+            {
+                turn_off(*tp, ISCHARMED);
+                turn_on(*tp, ISMEAN);
+            }
+
+            /*
+             * If the player hit a rust monster, he better have a
+             * + weapon
+            */
+
+            if (on(*tp, CANRUST))
+            {
+                if (!thrown && (weap != NULL) &&
+                    (weap->o_flags & ISMETAL) &&
+                    !(weap->o_flags & ISPROT) &&
+                    !(weap->o_flags & ISSILVER) &&
+                (weap->o_hplus < 1) && (weap->o_dplus < 1))
+                {
+                    if (rnd(100) < 50)
+                        weap->o_hplus--;
+                    else
+                        weap->o_dplus--;
+
+                    msg("Your %s weakens!", weaps[weap->o_which].w_name);
+                }
+                else if (!thrown && weap != NULL && (weap->o_flags & ISMETAL))
+                    msg("The rust vanishes from your %s!",
+                        weaps[weap->o_which].w_name);
+            }
+
+            /* flammable monsters die from burning weapons */
+
+            if (thrown && on(*tp, CANBBURN) &&
+                (weap->o_flags & CANBURN) &&
+                !save_throw(VS_WAND, tp))
+            {
+                msg("The %s vanishes in a ball of flame.",
+                    monsters[tp->t_index].m_name);
+
+                tp->t_stats.s_hpt = 0;
+            }
+
+            /* spores explode and infest hero  */
+
+            if (on(*tp, CANSPORE))
+            {
+                msg("The %s explodes in a cloud of dust.",
+                    monsters[tp->t_index].m_name);
+
+                if (is_wearing(R_HEALTH) ||
+                    player.t_ctype == C_PALADIN ||
+                    (player.t_ctype == C_NINJA && pstats.s_lvl
+                     > 6) ||
+                    thrown && rnd(50) > 0 ||
+                    rnd(20) > 0)
+                {
+                    msg("The dust makes it hard to breath.");
+                }
+                else
+                {
+                    msg("You have contracted a parasitic infestation!");
+
+                    infest_dam++;
+                    turn_on(player, HASINFEST);
+                }
+
+                tp->t_stats.s_hpt = 0;
+            }
+
+            /* fireproof monsters laugh at you when burning weapon hits */
+
+            if (thrown && on(*tp, NOFIRE) && (weap->o_flags & CANBURN))
+                msg("The %s laughs as the %s bounces.",
+                    monsters[tp->t_index].m_name,
+                    weaps[weap->o_which].w_name);
+
+            /* sharp weapons have no effect on NOSHARP monsters */
+
+            if (on(*tp, NOSHARP) && (weap != NULL) &&
+                (weap->o_flags & ISSHARP))
+            {
+                msg("The %s has no effect on the %s!",
+                    weaps[weap->o_which].w_name,
+                    monsters[tp->t_index].m_name);
+
+                fighting = FALSE;
+            }
+
+            /* metal weapons pass through NOMETAL monsters */
+
+            if (on(*tp, NOMETAL) && (weap != NULL) &&
+                (weap->o_flags & ISMETAL))
+            {
+                msg("The %s passes through the %s!",
+                    weaps[weap->o_which].w_name,
+                    monsters[tp->t_index].m_name);
+
+                fighting = FALSE;
+            }
+
+            /*
+             * If the player hit something that shrieks, wake the
+             * dungeon
+             */
+
+            if (on(*tp, CANSHRIEK))
+            {
+                turn_off(*tp, CANSHRIEK);
+
+                if (on(player, CANHEAR))
+                {
+                    msg("You are stunned by the %s's shriek.", mname);
+                    no_command += 4 + rnd(8);
+                }
+                else if (off(player, ISDEAF))
+                    msg("The %s emits a piercing shriek.", mname);
+                else
+                    msg("The %s seems to be trying to make some noise.", mname);
+
+                aggravate();
+
+                if (rnd(wizard ? 3 : 39) == 0 && cur_armor
+                    != NULL
+                    && cur_armor->o_which == CRYSTAL_ARMOR)
+                {
+                    struct linked_list  *itm;
+                    struct object   *obj;
+
+                    for (itm = pack; itm != NULL; itm = next(itm))
+                    {
+                        obj = OBJPTR(itm);
+
+                        if (obj == cur_armor)
+                            break;
+                    }
+
+                    if (itm == NULL)
+                        debug("Can't find crystalline armor being worn.");
+                    else
+                    {
+                        msg("Your armor shatters from the shriek.");
+                        cur_armor = NULL;
+                        del_pack(itm);
+                    }
+                }
+            }
+
+            /*
+             * If the player hit something that can surprise, it
+             * can't now
+             */
+
+            if (on(*tp, CANSURPRISE))
+                turn_off(*tp, CANSURPRISE);
+
+            /*
+             * If the player hit something that can summon, it
+             * will try to
+             */
+
+            summon_help(tp, NOFORCE);
+
+            /* Can the player confuse? */
+
+            if (on(player, CANHUH) && !thrown)
+            {
+                seemsg("Your hands stop glowing red!");
+                seemsg("The %s appears confused.", mname);
+                turn_on(*tp, ISHUH);
+                turn_off(player, CANHUH);
+            }
+
+            /* Merchants just disappear if hit */
+
+            /*
+             * increases prices and curses objects from now on
+             * though
+             */
+
+            if (on(*tp, CANSELL))
+            {
+                msg("The %s disappears with his wares with a BOOM and a flash.",
+                     mname);
+                killed(NULL, item, NOMESSAGE, NOPOINTS);
+                aggravate();
+                luck++;
+            }
+            else if (tp->t_stats.s_hpt <= 0)
+                killed(&player, item, MESSAGE, POINTS);
+
+            /*
+             * If the monster is fairly intelligent and about to
+             * die, it may turn tail and run.
+             */
+
+            else if ((tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt / 10)) &&
+                 (rnd(25) < tp->t_stats.s_intel))
+            {
+                turn_on(*tp, ISFLEE);
+
+                /* If monster was suffocating, stop it */
+
+                if (on(*tp, DIDSUFFOCATE))
+                {
+                    turn_off(*tp, DIDSUFFOCATE);
+                    extinguish_fuse(FUSE_SUFFOCATE);
+                }
+
+                /* If monster held us, stop it */
+
+                if (on(*tp, DIDHOLD) && (--hold_count == 0))
+                    turn_off(player, ISHELD);
+
+                turn_off(*tp, DIDHOLD);
+
+                if (on(*tp, CANTELEPORT))
+                {
+                    int rm;
+
+                    /*
+                     * Erase the monster from the old
+                     * position
+                     */
+
+                    if (isalpha(mvwinch(cw, tp->t_pos.y, tp->t_pos.x)))
+                        mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
+
+                    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' ');
+
+                    /* Get a new position */
+
+                    do
+                    {
+                        rm = rnd_room();
+                        rnd_pos(&rooms[rm], &tp->t_pos);
+                    }
+                    while (winat(tp->t_pos.y, tp->t_pos.x) != FLOOR);
+
+                    /* Put it there */
+
+                    mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type);
+                    tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) );
+                    seemsg("The %s seems to have disappeared!", mname);
+                }
+            }
+        }
+        else if (thrown)
+            bounce(weap, mname);
+        else
+            miss(mname);
+    }
+
+    if (curr_mons)
+        chase_it(mp,&player);   /* after so that backstabbing can happen */
+
+    count = 0;
+
+    return(did_hit);
+}
+
+/*
+    attack()
+        The monster attacks the player
+*/
+
+int
+attack(struct thing *mp, struct object *weapon, int thrown)
+{
+    char    *mname;
+    int    did_hit = FALSE;
+
+    /* If the monster is in a wall, it cannot attack */
+
+    if (on(*mp, ISINWALL))
+        return (FALSE);
+
+    /* If two monsters start to gang up on our hero, stop fight mode */
+
+    if (fighting)
+    {
+        if (beast == NULL)
+            beast = mp;
+        else if (beast != mp)
+            fighting = FALSE;
+    }
+
+    /*
+       Since this is an attack, stop running and any healing that was
+       going on at the time.
+    */
+
+    running = FALSE;
+    player.t_rest_hpt = player.t_rest_pow = 0;
+    mp->t_rest_hpt = mp->t_rest_pow = 0;
+
+    if (on(*mp, ISDISGUISE) && off(player, ISBLIND))
+        turn_off(*mp, ISDISGUISE);
+    mname = on(player, ISBLIND) ? "the monster" :
+        monsters[mp->t_index].m_name;
+
+    if (roll_em(mp, &player, weapon, thrown, wield_weap(weapon, mp)) &&
+        (!thrown || off(player, HASMSHIELD)))
+    {
+        did_hit = TRUE;
+
+        m_thunk(weapon, mname);
+
+        if (pstats.s_hpt <= 0)
+        {
+            death(mp->t_index); /* Bye bye life ... */
+            return TRUE;
+        }
+
+        /* surprising monsters appear after they shoot at you */
+
+        if (thrown && on(*mp, CANSURPRISE))
+            turn_off(*mp, CANSURPRISE);
+        else if (!thrown)
+        {
+
+            /*
+               If a vampire hits, it may take half your hit
+               points
+            */
+
+            if ( on(*mp, CANSUCK) && !save(VS_MAGIC) )
+            {
+                if (pstats.s_hpt == 1)
+                {
+                    death(mp->t_index);
+                    return TRUE;
+                }
+                else
+                {
+                    pstats.s_hpt /= 2;
+                    msg("You feel your life force being drawn from you.");
+                }
+            }
+
+            /*
+               strong monsters can shatter or gong crystalline
+               armor
+            */
+
+            if (cur_armor != NULL && cur_armor->o_which == CRYSTAL_ARMOR)
+            {
+                if (rnd(mp->t_stats.s_str + (cur_armor->o_ac / 2)) > 20)
+                {
+                    struct linked_list  *item;
+                    struct object   *obj;
+
+                    for (item = pack; item != NULL; item = next(item))
+                    {
+                        obj = OBJPTR(item);
+
+                        if (obj == cur_armor)
+                            break;
+                    }
+
+                    if (item == NULL)
+                        debug("Can't find crystalline armor being worn.");
+                    else
+                    {
+                        msg("Your armor is shattered by the blow.");
+                        cur_armor = NULL;
+                        del_pack(item);
+                    }
+                }
+                else if (rnd(mp->t_stats.s_str) > 15)
+                {
+                    msg("Your armor rings from the blow.");
+                    aggravate();
+                }
+            }
+
+            /* Stinking monsters reduce the player's strength */
+
+            if (on(*mp, CANSTINK))
+            {
+                turn_off(*mp, CANSTINK);
+
+                if (player.t_ctype != C_PALADIN
+                    && !(player.t_ctype == C_NINJA && pstats.s_lvl > 12)
+                    && !save(VS_POISON))
+                {
+                    if (on(player, CANSCENT))
+                    {
+                        msg("You pass out from the stench of the %s.", mname);
+                        no_command += 4 + rnd(8);
+                    }
+                    else if (off(player, ISUNSMELL))
+                        msg("The stench of the %s sickens you.", mname);
+
+                    if (on(player, HASSTINK))
+                        lengthen_fuse(FUSE_UNSTINK, STINKTIME);
+                    else
+                    {
+                        turn_on(player, HASSTINK);
+                        light_fuse(FUSE_UNSTINK, 0, STINKTIME, AFTER);
+                    }
+                }
+            }
+
+            /* chilling monster reduces strength permanently */
+
+            if (on(*mp, CANCHILL) &&
+                (cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR))
+            {
+                msg("You cringe at the %s's chilling touch.", mname);
+
+                if (!is_wearing(R_SUSABILITY))
+                {
+                    chg_str(-1, FALSE, TRUE);
+
+                    if (lost_str == 0)
+                        light_fuse(FUSE_RES_STRENGTH, 0, CHILLTIME, AFTER);
+                    else
+                        lengthen_fuse(FUSE_RES_STRENGTH, CHILLTIME);
+                }
+            }
+
+            /* itching monsters reduce dexterity (temporarily) */
+
+            if (on(*mp, CANITCH) && player.t_ctype != C_PALADIN
+                && !(player.t_ctype == C_NINJA && pstats.s_lvl > 12)
+                && !save(VS_POISON))
+            {
+                msg("The claws of the %s scratch you!", mname);
+
+                if (is_wearing(R_SUSABILITY))
+                    msg("The scratch has no effect.");
+                else
+                {
+                    msg("You feel a burning itch.");
+                    turn_on(player, HASITCH);
+                    chg_dext(-1, FALSE, TRUE);
+                    light_fuse(FUSE_UNITCH, 0, roll(4, 6), AFTER);
+                }
+            }
+
+            /* a hugging monster may SQUEEEEEEEZE */
+
+            if (on(*mp, CANHUG) &&
+                (cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR))
+            {
+                if (roll(1, 20) >= 18 || roll(1, 20) >= 18)
+                {
+                    msg("The %s squeezes you against itself.", mname);
+
+                    if ((pstats.s_hpt -= roll(2, 8)) <= 0)
+                    {
+                        death(mp->t_index);
+                        return TRUE;
+                    }
+                }
+            }
+
+            /* a trampling monster may step on the player */
+
+            if (on(*mp, CANTRAMPLE))
+            {
+                if (roll(1, 20) >= 16 || roll(1, 20) >= 16)
+                {
+                    msg("The %s steps on you.", mname);
+
+                    if ((pstats.s_hpt -= roll(3, mp->t_stats.s_lvl)) <= 0)
+                    {
+                        death(mp->t_index);
+                        return TRUE;
+                    }
+                }
+            }
+
+            /* a disease-carrying monster may transmit the disease */
+
+            if (on(*mp, CANDISEASE) &&
+                (rnd(pstats.s_const) < mp->t_stats.s_lvl) &&
+                off(player, HASDISEASE))
+            {
+
+                if (is_wearing(R_HEALTH)
+                    || (player.t_ctype == C_PALADIN)
+                    || (player.t_ctype == C_NINJA &&
+                    pstats.s_lvl > 6))
+                    msg("The wound heals quickly.");
+                else
+                {
+                    turn_on(player, HASDISEASE);
+                    light_fuse(FUSE_CURE_DISEASE,0,roll(4,4) * SICKTIME, AFTER);
+                    msg("You have contracted a disease!");
+                }
+            }
+
+            /* a rust monster will weaken your armor */
+
+           if (on(*mp, CANRUST))
+           {
+                if (cur_armor != NULL &&
+                    cur_armor->o_which != SOFT_LEATHER &&
+                    cur_armor->o_which != HEAVY_LEATHER &&
+                    cur_armor->o_which != CUIRBOLILLI &&
+                    cur_armor->o_which != PADDED_ARMOR &&
+                    cur_armor->o_which != CRYSTAL_ARMOR &&
+                    cur_armor->o_which != MITHRIL &&
+                    !(cur_armor->o_flags & ISPROT) &&
+                    cur_armor->o_ac < pstats.s_arm + 1)
+                {
+                    msg("Your armor weakens!");
+                    cur_armor->o_ac++;
+                }
+                else if (cur_armor != NULL &&
+                     (cur_armor->o_flags & ISPROT) &&
+                    cur_armor->o_which != SOFT_LEATHER &&
+                     cur_armor->o_which != HEAVY_LEATHER &&
+                     cur_armor->o_which != CUIRBOLILLI &&
+                    cur_armor->o_which != PADDED_ARMOR &&
+                     cur_armor->o_which != CRYSTAL_ARMOR &&
+                     cur_armor->o_which != MITHRIL)
+                    msg("The rust vanishes instantly!");
+            }
+
+            /* If a surprising monster hit you, you can see it now */
+
+            if (on(*mp, CANSURPRISE))
+                turn_off(*mp, CANSURPRISE);
+
+            /* an infesting monster will give you a parasite or rot */
+
+            if (on(*mp, CANINFEST) && rnd(pstats.s_const) < mp->t_stats.s_lvl)
+            {
+                if (is_wearing(R_HEALTH) || (player.t_ctype == C_PALADIN)
+                    || (player.t_ctype == C_NINJA && pstats.s_lvl > 6))
+                    msg("The wound quickly heals.");
+                else
+                {
+                    turn_off(*mp, CANINFEST);
+                    msg("You have contracted a parasitic infestation!");
+                    infest_dam++;
+                    turn_on(player, HASINFEST);
+                }
+            }
+
+            /* Some monsters have poisonous bites */
+
+            if (on(*mp, CANPOISON) && !save(VS_POISON))
+            {
+                if (is_wearing(R_SUSABILITY) || (player.t_ctype == C_PALADIN)
+                    || (player.t_ctype == C_NINJA && pstats.s_lvl > 12))
+                    msg("The sting has no effect on you!");
+                else
+                {
+                    chg_str(-1, FALSE, FALSE);
+                    msg("You feel a sting in your arm and now feel weaker.");
+                }
+            }
+
+            /* a hideous monster may cause fear by touching */
+
+            if (on(*mp, TOUCHFEAR))
+            {
+                turn_off(*mp, TOUCHFEAR);
+
+                if (!save(VS_WAND)&&!(on(player,ISFLEE)&&(player.t_chasee==mp)))
+                {
+                    if (off(player, SUPERHERO)
+                         && (player.t_ctype != C_PALADIN))
+                    {
+                        turn_on(player, ISFLEE);
+                        player.t_ischasing = FALSE;
+                        player.t_chasee = mp;
+                        msg("The %s's touch terrifies you.", mname);
+                    }
+                    else
+                        msg("The %s's touch feels cold and clammy.", mname);
+                }
+            }
+
+            /* some monsters will suffocate our hero */
+
+            if (on(*mp, CANSUFFOCATE) && (rnd(100) < 15) &&
+                (find_slot(FUSE_SUFFOCATE, FUSE) == NULL))
+            {
+                turn_on(*mp, DIDSUFFOCATE);
+                msg("The %s is beginning to suffocate you.",
+                    mname);
+                light_fuse(FUSE_SUFFOCATE, 0, roll(4, 2), AFTER);
+            }
+
+            /* don't look now, you will get turned to stone */
+
+            if (on(*mp, TOUCHSTONE))
+            {
+                turn_off(*mp, TOUCHSTONE);
+
+                if (on(player, CANINWALL))
+                    msg("The %s's touch has no effect.", mname);
+                else
+                {
+                    if (!save(VS_PETRIFICATION) && rnd(100) < 3)
+                    {
+                        msg("Your body begins to solidify.");
+                        msg("You are turned to stone !!! --More--");
+                        wait_for(' ');
+                        death(D_PETRIFY);
+                        return TRUE;
+                    }
+                    else
+                    {
+                        msg("The %s's touch stiffens your limbs.", mname);
+                        no_command = rnd(STONETIME) + 2;
+                    }
+                }
+            }
+
+            /* Undead might drain energy levels */
+
+            if ((on(*mp, CANDRAIN) || on(*mp, DOUBLEDRAIN)) && rnd(100) < 15)
+            {
+                if (is_carrying(TR_AMULET))
+                   msg("The Amulet protects you from the %s's negative energy!",
+                       mname);
+                else
+                {
+                    lower_level(mp->t_index);
+
+                    if (on(*mp, DOUBLEDRAIN))
+                        lower_level(mp->t_index);
+                }
+
+                turn_on(*mp, DIDDRAIN);
+            }
+
+            /* permanently drain a wisdom point */
+
+            if (on(*mp, DRAINWISDOM) && rnd(100) < 15)
+            {
+                int ring_str;   /* Value of ring strengths */
+
+                /* Undo any ring changes */
+
+                ring_str = ring_value(R_ADDWISDOM) +
+                    (on(player, POWERWISDOM) ? 10 : 0);
+
+                pstats.s_wisdom -= ring_str;
+				
+                msg("You feel slightly less wise now.");
+				
+                pstats.s_wisdom = max(pstats.s_wisdom - 1, 3);
+                max_stats.s_wisdom = pstats.s_wisdom;
+
+                /* Now put back the ring changes */
+
+                pstats.s_wisdom += ring_str;
+
+            }
+
+            /* permanently drain a intelligence point */
+
+            if (on(*mp, DRAINBRAIN) && rnd(100) < 15)
+            {
+                int ring_str;   /* Value of ring strengths */
+
+                /* Undo any ring changes */
+
+                ring_str = ring_value(R_ADDINTEL) +
+                    (on(player, POWERINTEL) ? 10 : 0);
+
+                pstats.s_intel -= ring_str;
+
+                msg("You feel slightly less intelligent now.");
+                pstats.s_intel = max(pstats.s_intel - 1, 3);
+                max_stats.s_intel = pstats.s_intel;
+
+                /* Now put back the ring changes */
+
+                pstats.s_intel += ring_str;
+            }
+
+            /* Violet fungi and others hold the hero */
+
+            if (on(*mp, CANHOLD) && off(*mp, DIDHOLD)
+                && !is_wearing(R_FREEDOM))
+            {
+                turn_on(player, ISHELD);
+                turn_on(*mp, DIDHOLD);
+                hold_count++;
+            }
+
+            /* suckers will suck blood and run away */
+
+            if (on(*mp, CANDRAW))
+            {
+                turn_off(*mp, CANDRAW);
+                turn_on(*mp, ISFLEE);
+                msg("The %s sates itself with your blood.", mname);
+
+                if ((pstats.s_hpt -= 12) <= 0)
+                {
+                    death(mp->t_index);
+                    return TRUE;
+                }
+            }
+
+            /* el stinkos will force a reduction in strength */
+
+            if (on(*mp, CANSMELL))
+            {
+                turn_off(*mp, CANSMELL);
+
+                if (save(VS_MAGIC) || is_wearing(R_SUSABILITY))
+                    msg("You smell an unpleasant odor.");
+                else
+                {
+                    int   odor_str = -(rnd(6) + 1);
+
+                    if (on(player, CANSCENT))
+                    {
+                        msg("You pass out from a foul odor.");
+                        no_command += 4 + rnd(8);
+                    }
+                    else if (off(player, ISUNSMELL))
+                        msg("You are overcome by a foul odor.");
+
+                    if (lost_str == 0)
+                    {
+                        chg_str(odor_str, FALSE, TRUE);
+                        light_fuse(FUSE_RES_STRENGTH, 0, SMELLTIME, AFTER);
+                    }
+                    else
+                        lengthen_fuse(FUSE_RES_STRENGTH, SMELLTIME);
+                }
+            }
+
+            /* Paralyzation */
+
+            if (on(*mp, CANPARALYZE))
+            {
+                turn_off(*mp, CANPARALYZE);
+
+                if (!save(VS_PARALYZATION) && no_command == 0)
+                {
+                    if (on(player, CANINWALL))
+                        msg("The %s's touch has no effect.", mname);
+                    else
+                    {
+                        msg("The %s's touch paralyzes you.", mname);
+                        no_command = FREEZETIME;
+                    }
+                }
+            }
+
+            /* Rotting */
+
+            if (on(*mp, CANROT))
+            {
+                turn_off(*mp, CANROT);
+                turn_on(*mp, DOROT);
+            }
+
+            /* some monsters steal gold */
+
+            if (on(*mp, STEALGOLD))
+            {
+                long    lastpurse;
+                struct linked_list  *item;
+                struct object   *obj;
+
+                lastpurse = purse;
+                purse = (purse > GOLDCALC) ? purse - GOLDCALC : 0L;
+
+                if (!save(VS_MAGIC))
+                    purse = (purse > (4*GOLDCALC)) ? purse-(4*GOLDCALC) : 0L;
+
+                if (purse != lastpurse)
+                {
+                    msg("Your purse feels lighter.");
+
+                    /* Give the gold to the thief */
+
+                    for (item = mp->t_pack; item != NULL; item = next(item))
+                    {
+                        obj = OBJPTR(item);
+
+                        if (obj->o_type == GOLD)
+                        {
+                            obj->o_count += lastpurse - purse;
+                            break;
+                        }
+                    }
+
+                    /* Did we do it? */
+
+                    if (item == NULL)  /* Then make some */
+                    {
+                        item = new_item(sizeof *obj);
+                        obj = OBJPTR(item);
+                        obj->o_type = GOLD;
+                        obj->o_count = lastpurse - purse;
+                        obj->o_hplus = obj->o_dplus = 0;
+                        obj->o_damage = obj->o_hurldmg = "0d0";
+                        obj->o_ac = 11;
+                        obj->o_group = 0;
+                        obj->o_flags = 0;
+                        obj->o_mark[0] = '\0';
+                        obj->o_pos = mp->t_pos;
+
+                        attach(mp->t_pack, item);
+                    }
+                }
+
+                if (rnd(2))
+                    turn_on(*mp, ISFLEE);
+
+                turn_on(*mp, ISINVIS);
+            }
+
+            /* other monsters steal magic */
+
+            if (on(*mp, STEALMAGIC))
+            {
+                struct linked_list  *list, *stealit;
+                struct object   *obj;
+                int worth = 0;
+
+                stealit = NULL;
+
+                for (list = pack; list != NULL; list = next(list))
+                {
+                    obj = OBJPTR(list);
+
+                    if (rnd(33) == 0) /* some stuff degrades */
+                    {
+                        if (obj->o_flags & ISBLESSED)
+                            obj->o_flags &= ~ISBLESSED;
+                        else
+                            obj->o_flags |= ISCURSED;
+
+                        msg("You feel nimble fingers reach into you pack.");
+                    }
+
+                    if ((obj != cur_armor &&
+                         obj != cur_weapon &&
+                         obj != cur_ring[LEFT_1] &&
+                         obj != cur_ring[LEFT_2] &&
+                         obj != cur_ring[LEFT_3] &&
+                         obj != cur_ring[LEFT_4] &&
+                         obj != cur_ring[LEFT_5] &&
+                         obj != cur_ring[RIGHT_1] &&
+                         obj != cur_ring[RIGHT_2] &&
+                         obj != cur_ring[RIGHT_3] &&
+                         obj != cur_ring[RIGHT_4] &&
+                         obj != cur_ring[RIGHT_5] &&
+                         !(obj->o_flags & ISPROT) &&
+                         is_magic(obj)
+                         || level > 45)
+                        && get_worth(obj) > worth)
+                    {
+                        stealit = list;
+                        worth = get_worth(obj);
+                    }
+                }
+
+                if (stealit != NULL)
+                {
+                    struct object   *newobj;
+
+                    newobj = OBJPTR(stealit);
+
+                    if (newobj->o_count > 1 && newobj->o_group == 0)
+                    {
+                        int oc;
+                        struct linked_list  *nitem;
+                        struct object   *op;
+
+                        oc = --(newobj->o_count);
+                        newobj->o_count = 1;
+                        nitem = new_item(sizeof *newobj);
+                        op = OBJPTR(nitem);
+                        *op = *newobj;
+
+                       msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE));
+                        newobj->o_count = oc;
+                        attach(mp->t_pack, nitem);
+                    }
+                    else
+                    {
+                       msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE));
+                        newobj->o_flags &= ~ISCURSED;
+                        dropcheck(newobj);
+                        rem_pack(newobj);
+                        attach(mp->t_pack, stealit);
+
+                        if (newobj->o_type == ARTIFACT)
+                            has_artifact &= ~(1 << newobj->o_which);
+                    }
+
+                    if (newobj->o_flags & ISOWNED)
+                    {
+                        turn_on(*mp, NOMOVE);
+                        msg("The %s is transfixed by your ownership spell.",
+                            mname);
+                    }
+
+                    if (rnd(2))
+                        turn_on(*mp, ISFLEE);
+
+                    turn_on(*mp, ISINVIS);
+                    updpack();
+                }
+            }
+        }
+    }
+    else /* missed */
+    {
+        /* If the thing was trying to surprise, no good */
+
+        if (on(*mp, CANSURPRISE))
+            turn_off(*mp, CANSURPRISE);
+
+        m_bounce(weapon, mname);
+    }
+
+    count = 0;
+
+    status(FALSE);
+
+    return(did_hit);
+}
+
+
+/*
+    mon_mon_attack()
+        A monster attacks another monster
+*/
+
+int
+mon_mon_attack(struct thing *attacker, struct linked_list *mon, struct object *weapon, int thrown)
+{
+    struct thing *attackee = THINGPTR(mon);
+    int           did_hit  = FALSE;
+    int           ee_visible = cansee(attackee->t_pos.y, attackee->t_pos.x);
+    int           er_visible = cansee(attacker->t_pos.y, attacker->t_pos.x);
+    char         *mname1   = monsters[attacker->t_index].m_name;
+    char         *mname2   = monsters[attackee->t_index].m_name;
+
+    /* Similar monsters don't hit each other */
+
+    if (attacker->t_index == attackee->t_index)
+    {
+        if (attacker == THINGPTR(fam_ptr) && er_visible)
+            msg("Master, I cannot hit one of my brethren.");
+        if (!thrown && rnd(100) - attacker->t_stats.s_charisma + luck < 0) 
+		{
+            if (er_visible)
+                msg("Your %s has made a new ally.", mname1);
+				
+            turn_on(*attackee, ISCHARMED);
+		}
+
+        return(FALSE);
+    }
+
+    /* stop running and any healing */
+
+    attackee->t_rest_hpt = attackee->t_rest_pow = 0;
+    attacker->t_rest_hpt = attacker->t_rest_pow = 0;
+
+    if (roll_em(attacker, attackee, weapon, thrown,
+        wield_weap(weapon, attacker)))
+    {
+        did_hit = TRUE;
+
+        if (ee_visible && on(*attackee, CANSURPRISE))
+            turn_off(*attackee, CANSURPRISE);
+
+        if (ee_visible && er_visible && weapon != NULL)
+            msg("The %s's %s hits the %s.", mname1,
+                weaps[weapon->o_which].w_name, mname2);
+        else if (ee_visible && er_visible)
+            msg("The %s hits the %s.", mname1, mname2);
+
+        if (attackee->t_stats.s_hpt <= 0)
+        {
+            killed(attacker, mon, MESSAGE,
+                 on(*attacker, ISFAMILIAR) ? POINTS : NOPOINTS);
+            return(TRUE);
+        }
+    } 
+    else  /* missed */
+	{
+	    did_hit = FALSE;
+		
+		if (ee_visible && er_visible && weapon != NULL)
+            msg("The %s's %s misses the %s.", mname1,
+                weaps[weapon->o_which].w_name, mname2);
+        else if (ee_visible && er_visible)
+            msg("The %s misses the %s.", mname1, mname2);
+	}
+	
+	if (er_visible && !ee_visible)
+        msg("The %s struggles with something.",mname1);
+
+    if (off(*attackee, ISMEAN) && off(*attackee, ISFAMILIAR))
+        turn_on(*attackee, ISRUN);
+
+    count = 0;
+
+    status(FALSE);
+
+    return(did_hit);
+}
+
+
+/*
+    swing()
+        returns true if the swing hits
+*/
+
+int
+swing(int class, int at_lvl, int op_arm, int wplus)
+{
+    int res = rnd(20) + 1;
+    int need;
+
+    need = att_mat[class].base -
+        att_mat[class].factor *
+        ((min(at_lvl, att_mat[class].max_lvl) -
+          att_mat[class].offset) / att_mat[class].range) +
+        (10 - op_arm);
+
+    if (need > 20 && need <= 25)
+        need = 20;
+
+    return(res + wplus >= need);
+}
+
+/*
+    init_exp()
+        set up initial experience level change threshold
+*/
+
+void
+init_exp(void)
+{
+    max_stats.s_exp = e_levels[player.t_ctype];
+}
+
+/*
+    next_exp_level()
+        Do the next level arithmetic Returns number of levels to jump
+*/
+
+int
+next_exp_level(int print_message)
+{
+    int level_jump = 0;
+
+    while (pstats.s_exp >= max_stats.s_exp)
+    {
+        pstats.s_exp -= max_stats.s_exp;    /* excess experience points */
+        level_jump++;
+
+        if (max_stats.s_exp < 0x3fffffffL)  /* 2^30 - 1 */
+            max_stats.s_exp *= 2L;  /* twice as many for next */
+    }
+
+    if (print_message)
+        msg("You need %d more points to attain the %stitle of %s.",
+            max_stats.s_exp - pstats.s_exp,
+            (pstats.s_lvl > 14 ? "next " : ""),
+            cnames[player.t_ctype][min(pstats.s_lvl, 14)]);
+
+    return(level_jump);
+}
+
+/*
+    check_level()
+        Check to see if the guy has gone up a level.
+*/
+
+void
+check_level(void)
+{
+    int num_jumped, j, add;
+    int nsides;
+
+    if ((num_jumped = next_exp_level(NOMESSAGE)) <= 0)
+        return;
+
+    pstats.s_lvl += num_jumped; /* new experience level */
+
+    switch (player.t_ctype)
+    {
+        case C_MAGICIAN:
+        case C_ILLUSION: nsides = 4;
+                         break;
+        case C_THIEF:
+        case C_ASSASIN:
+        case C_NINJA:
+        case C_MONSTER:
+        default:         nsides = 6;
+                         break;
+        case C_CLERIC:
+        case C_DRUID:    nsides = 8;
+                         break;
+
+        case C_FIGHTER:
+        case C_PALADIN:
+        case C_RANGER:
+                         nsides = 12;
+                         break;
+    }
+
+    /* Take care of multi-level jumps */
+
+    for (add = 0, j = 0; j < num_jumped; j++)
+    {
+        int increase = roll(1, nsides) + const_bonus();
+
+        add += max(1, increase);
+    }
+
+    max_stats.s_hpt += add;
+    pstats.s_hpt += add;
+
+    msg("Welcome, %s, to level %d.",
+        cnames[player.t_ctype][min(pstats.s_lvl - 1, 14)], pstats.s_lvl);
+
+    next_exp_level(MESSAGE);
+
+    /* Now add new spell points and learn new spells */
+
+    nsides = 16 - nsides;
+
+    for (add = 0, j = 0; j < num_jumped; j++)
+    {
+        int increase = roll(1, nsides) + int_wis_bonus();
+
+        add += max(1, increase);
+    }
+
+    max_stats.s_power += add;
+    pstats.s_power += add;
+
+    learn_new_spells();
+
+    /* Create a more powerful familiar (if player has one) */
+
+    if (on(player, HASFAMILIAR) && on(player, CANSUMMON))
+        summon_monster((short) 0, FAMILIAR, NOMESSAGE);
+}
+
+/*
+    roll_em()
+        Roll several attacks
+*/
+
+int
+roll_em(struct thing *att_er, struct thing *def_er, struct object *weap, int thrown, struct object *my_weapon)
+{
+    struct stats *att = &att_er->t_stats;
+    struct stats *def = &def_er->t_stats;
+    int ndice, nsides, nplus, def_arm;
+    char *cp;
+    int prop_hplus = 0, prop_dplus = 0;
+    int is_player = (att_er == &player);
+    int did_hit = FALSE;
+
+    if (weap == NULL)
+        cp = att->s_dmg;
+    else if (!thrown)
+        cp = weap->o_damage;
+    else if ((weap->o_flags & ISMISL) && my_weapon != NULL &&
+         my_weapon->o_which == weap->o_launch)
+    {
+        cp = weap->o_hurldmg;
+        prop_hplus = my_weapon->o_hplus;
+        prop_dplus = my_weapon->o_dplus;
+    }
+    else
+        cp = (weap->o_flags & ISMISL ? weap->o_damage :
+            weap->o_hurldmg);
+
+    for (;;)
+    {
+        int damage;
+        int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus);
+        int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus);
+
+        /* Is attacker weak? */
+
+        if (on(*att_er, HASSTINK))
+            hplus -= 2;
+
+        if (is_player)
+        {
+            hplus += hitweight();   /* adjust for encumberence */
+            dplus += hung_dam();    /* adjust damage for hungry player */
+            dplus += ring_value(R_ADDDAM);
+        }
+
+        ndice = atoi(cp);
+
+        if (cp == NULL || (cp = strchr(cp, 'd')) == NULL)
+            break;
+
+        nsides = atoi(++cp);
+
+        if (cp != NULL && (cp = strchr(cp, '+')) != NULL)
+            nplus = atoi(++cp);
+        else
+            nplus = 0;
+
+        if (def == &pstats)
+        {
+            if (on(*att_er, NOMETAL) && cur_armor != NULL &&
+                (cur_armor->o_which == RING_MAIL ||
+                 cur_armor->o_which == SCALE_MAIL ||
+                 cur_armor->o_which == CHAIN_MAIL ||
+                 cur_armor->o_which == SPLINT_MAIL ||
+                 cur_armor->o_which == BANDED_MAIL ||
+                 cur_armor->o_which == GOOD_CHAIN ||
+                 cur_armor->o_which == PLATE_MAIL ||
+                 cur_armor->o_which == PLATE_ARMOR))
+                def_arm = def->s_arm;
+            else if (cur_armor != NULL)
+                def_arm = cur_armor->o_ac - 10 + pstats.s_arm;
+            else
+                def_arm = def->s_arm;
+            def_arm -= ring_value(R_PROTECT);
+        }
+        else
+            def_arm = def->s_arm;
+
+        if ((weap != NULL && weap->o_type == WEAPON &&
+             (weap->o_flags & ISSILVER) &&
+            !save_throw(VS_MAGIC, def_er)) ||
+            swing(att_er->t_ctype, att->s_lvl,
+            def_arm - dext_prot(def->s_dext),
+           hplus + str_plus(att->s_str) + dext_plus(att->s_dext)))
+        {
+            damage = roll(ndice, nsides) + dplus + nplus +
+                add_dam(att->s_str);
+
+            /* Rangers do +1/lvl vs. ISLARGE */
+
+            if (att_er->t_ctype == C_RANGER && on(*def_er, ISLARGE))
+                damage += pstats.s_lvl;
+
+            /* Ninja do +1 per lvl/2 */
+
+            if (att_er->t_ctype == C_NINJA)
+                damage += pstats.s_lvl / 2;
+
+            /* Check for half damage monsters */
+
+            if (on(*def_er, HALFDAMAGE) && (weap != NULL) &&
+                !((weap->o_flags & CANBURN) &&
+                on(*def_er, CANBBURN)))
+                damage /= 2;
+
+            /* undead get twice damage from silver weapons */
+
+            if (on(*def_er, ISUNDEAD) &&
+                (weap != NULL) && (weap->o_flags & ISSILVER))
+                damage *= 2;
+
+            /* Check for fireproof monsters */
+
+            if (on(*def_er, NOFIRE) && (weap != NULL) &&
+                (weap->o_flags & CANBURN))
+                damage = 0;
+
+            /* Check for metal proof monsters */
+
+            if (on(*def_er, NOMETAL) && (weap != NULL) &&
+                (weap->o_flags & ISMETAL))
+                damage = 0;
+
+            /* Check for monsters that ignore sharp weapons  */
+
+            if (on(*def_er, NOSHARP) && (weap != NULL) &&
+                (weap->o_flags & ISSHARP))
+                damage = 0;
+
+            /* Check for poisoned weapons */
+
+            if ((weap != NULL) && (weap->o_flags & ISPOISON)
+                && off(*def_er, ISUNDEAD)
+                && !save_throw(VS_POISON, def_er))
+                damage = max(damage, (def->s_hpt / 2) + 5);
+
+            /* Check for no-damage and division */
+
+            if (on(*def_er, BLOWDIVIDE) && rnd(3) == 0 &&
+                !((weap != NULL) && (weap->o_flags & CANBURN)))
+            {
+                damage = 0;
+                creat_mons(def_er, def_er->t_index, NOMESSAGE);
+            }
+
+            damage = max(0, damage);
+
+            /*
+             * sleeping monsters are backstabbed by certain
+             * player classes, but only when they can see
+             */
+
+            if (is_player && !thrown && damage > 0 &&
+                (off(*def_er, ISRUN) || def_er->t_no_move > 0) &&
+                (player.t_ctype == C_THIEF ||
+                 player.t_ctype == C_NINJA ||
+                 player.t_ctype == C_ASSASIN) &&
+				 off(player,ISBLIND)
+                && (wield_ok(&player, my_weapon, NOMESSAGE))
+                && (wear_ok(&player, cur_armor, NOMESSAGE)))
+            {
+                damage *= (pstats.s_lvl / 4 + 2);
+
+                msg("You backstabbed the %s %d times!",
+                    monsters[def_er->t_index].m_name,
+                    (pstats.s_lvl / 4) + 2);
+
+                if (player.t_ctype == C_NINJA ||
+                    player.t_ctype == C_ASSASIN)
+                    pstats.s_exp += def_er->t_stats.s_exp
+                        / 2;
+            }
+
+            def->s_hpt -= damage;   /* Do the damage */
+
+            debug("Hit %s for %d (%d) ",
+                  monsters[def_er->t_index].m_name, damage,
+                  def_er->t_stats.s_hpt);
+
+            if (is_player && is_wearing(R_VREGEN))
+            {
+                damage = (ring_value(R_VREGEN) * damage) / 3;
+                pstats.s_hpt = min(max_stats.s_hpt,
+                    pstats.s_hpt + damage);
+            }
+
+            /* stun monsters when taking more than 1/3 their max hpts */
+
+            if (is_player && !thrown && !did_hit &&
+                (player.t_ctype == C_FIGHTER) &&
+                (damage > def_er->maxstats.s_hpt / 3) )
+            {
+                if (def->s_hpt > 0)
+                {
+                    msg("The %s has been stunned!",
+                      monsters[def_er->t_index].m_name);
+                    def_er->t_no_move += rnd(4) + 1;
+                }
+                pstats.s_exp += def_er->t_stats.s_exp / 4;
+            }
+
+            did_hit = TRUE;
+        }