aboutsummaryrefslogtreecommitdiffstats
path: root/games/hack
diff options
context:
space:
mode:
authorsvn2git <svn2git@FreeBSD.org>1993-11-01 00:00:00 -0800
committersvn2git <svn2git@FreeBSD.org>1993-11-01 00:00:00 -0800
commit8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (patch)
treec5b2ce776438e0a52b492a2ab6ab41360b8ba1f6 /games/hack
downloadsrc-8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20.tar.gz
src-8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20.zip
Release FreeBSD 1.0release/1.0.0_cvs
This commit was manufactured to restore the state of the 1.0-RELEASE image. Releases prior to 5.3-RELEASE are omitting the secure/ and crypto/ subdirs.
Diffstat (limited to 'games/hack')
-rw-r--r--games/hack/COPYRIGHT6
-rw-r--r--games/hack/Makefile38
-rw-r--r--games/hack/Makequest196
-rw-r--r--games/hack/OWNER2
-rw-r--r--games/hack/Original_READ_ME61
-rw-r--r--games/hack/READ_ME92
-rw-r--r--games/hack/alloc.c47
-rw-r--r--games/hack/config.h139
-rw-r--r--games/hack/data232
-rw-r--r--games/hack/date.h2
-rw-r--r--games/hack/def.edog.h12
-rw-r--r--games/hack/def.eshk.h24
-rw-r--r--games/hack/def.flag.h42
-rw-r--r--games/hack/def.func_tab.h16
-rw-r--r--games/hack/def.gen.h15
-rw-r--r--games/hack/def.gold.h12
-rw-r--r--games/hack/def.mkroom.h26
-rw-r--r--games/hack/def.monst.h60
-rw-r--r--games/hack/def.obj.h48
-rw-r--r--games/hack/def.objclass.h60
-rw-r--r--games/hack/def.objects.h289
-rw-r--r--games/hack/def.permonst.h25
-rw-r--r--games/hack/def.rm.h52
-rw-r--r--games/hack/def.trap.h27
-rw-r--r--games/hack/def.wseg.h13
-rw-r--r--games/hack/hack.6155
-rw-r--r--games/hack/hack.Decl.c43
-rw-r--r--games/hack/hack.apply.c437
-rw-r--r--games/hack/hack.bones.c95
-rw-r--r--games/hack/hack.c798
-rw-r--r--games/hack/hack.cmd.c302
-rw-r--r--games/hack/hack.do.c488
-rw-r--r--games/hack/hack.do_name.c289
-rw-r--r--games/hack/hack.do_wear.c336
-rw-r--r--games/hack/hack.dog.c413
-rw-r--r--games/hack/hack.eat.c459
-rw-r--r--games/hack/hack.end.c642
-rw-r--r--games/hack/hack.engrave.c306
-rw-r--r--games/hack/hack.fight.c358
-rw-r--r--games/hack/hack.fix113
-rw-r--r--games/hack/hack.h160
-rw-r--r--games/hack/hack.invent.c863
-rw-r--r--games/hack/hack.ioctl.c53
-rw-r--r--games/hack/hack.lev.c285
-rw-r--r--games/hack/hack.main.c499
-rw-r--r--games/hack/hack.makemon.c198
-rw-r--r--games/hack/hack.mfndpos.h12
-rw-r--r--games/hack/hack.mhitu.c363
-rw-r--r--games/hack/hack.mklev.c741
-rw-r--r--games/hack/hack.mkmaze.c136
-rw-r--r--games/hack/hack.mkobj.c148
-rw-r--r--games/hack/hack.mkshop.c274
-rw-r--r--games/hack/hack.mon.c853
-rw-r--r--games/hack/hack.monst.c79
-rw-r--r--games/hack/hack.o_init.c160
-rw-r--r--games/hack/hack.objnam.c547
-rw-r--r--games/hack/hack.options.c203
-rw-r--r--games/hack/hack.pager.c406
-rw-r--r--games/hack/hack.potion.c386
-rw-r--r--games/hack/hack.pri.c660
-rw-r--r--games/hack/hack.read.c539
-rw-r--r--games/hack/hack.rip.c81
-rw-r--r--games/hack/hack.rumors.c63
-rw-r--r--games/hack/hack.save.c238
-rw-r--r--games/hack/hack.search.c133
-rw-r--r--games/hack/hack.sh14
-rw-r--r--games/hack/hack.shk.c987
-rw-r--r--games/hack/hack.shknam.c140
-rw-r--r--games/hack/hack.steal.c203
-rw-r--r--games/hack/hack.termcap.c276
-rw-r--r--games/hack/hack.timeout.c62
-rw-r--r--games/hack/hack.topl.c192
-rw-r--r--games/hack/hack.track.c38
-rw-r--r--games/hack/hack.trap.c447
-rw-r--r--games/hack/hack.tty.c338
-rw-r--r--games/hack/hack.u_init.c357
-rw-r--r--games/hack/hack.unix.c430
-rw-r--r--games/hack/hack.vault.c259
-rw-r--r--games/hack/hack.version.c16
-rw-r--r--games/hack/hack.wield.c99
-rw-r--r--games/hack/hack.wizard.c189
-rw-r--r--games/hack/hack.worm.c183
-rw-r--r--games/hack/hack.worn.c65
-rw-r--r--games/hack/hack.zap.c642
-rw-r--r--games/hack/help132
-rw-r--r--games/hack/hh55
-rw-r--r--games/hack/makedefs.c224
-rw-r--r--games/hack/pathnames.h39
-rw-r--r--games/hack/rnd.c30
-rw-r--r--games/hack/rumors505
90 files changed, 20772 insertions, 0 deletions
diff --git a/games/hack/COPYRIGHT b/games/hack/COPYRIGHT
new file mode 100644
index 000000000000..71a94494a8a0
--- /dev/null
+++ b/games/hack/COPYRIGHT
@@ -0,0 +1,6 @@
+This entire subtree is copyright the Stichting Mathematisch Centrum.
+The following copyright notice applies to all files found here. None of
+these files contain AT&T proprietary source code.
+_____________________________________________________________________________
+
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
diff --git a/games/hack/Makefile b/games/hack/Makefile
new file mode 100644
index 000000000000..0432786c3a1a
--- /dev/null
+++ b/games/hack/Makefile
@@ -0,0 +1,38 @@
+# @(#)Makefile 5.10 (Berkeley) 12/8/90
+
+PROG= hack
+SRCS= alloc.c hack.Decl.c hack.apply.c hack.bones.c hack.c hack.cmd.c \
+ hack.do.c hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c \
+ hack.end.c hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c \
+ hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c hack.mklev.c \
+ hack.mkmaze.c hack.mkobj.c hack.mkshop.c hack.mon.c hack.monst.c \
+ hack.o_init.c hack.objnam.c hack.options.c hack.pager.c hack.potion.c \
+ hack.pri.c hack.read.c hack.rip.c hack.rumors.c hack.save.c \
+ hack.search.c hack.shk.c hack.shknam.c hack.steal.c hack.termcap.c \
+ hack.timeout.c hack.topl.c hack.track.c hack.trap.c hack.tty.c \
+ hack.u_init.c hack.unix.c hack.vault.c hack.version.c hack.wield.c \
+ hack.wizard.c hack.worm.c hack.worn.c hack.zap.c rnd.c
+CFLAGS+= -I.
+MAN6= hack.6
+DPSRCS= hack.onames.h
+DPADD= ${LIBTERM}
+LDADD= -ltermcap
+HIDEGAME=hidegame
+CLEANFILES= hack.onames.h makedefs
+
+hack.onames.h: makedefs def.objects.h
+ ./makedefs ${.CURDIR}/def.objects.h > hack.onames.h
+
+makedefs: makedefs.c
+ ${CC} ${CFLAGS} -o ${.TARGET} ${.CURDIR}/${.PREFIX}.c
+
+beforeinstall:
+ install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \
+ ${DESTDIR}/var/games/hackdir/perm
+ install -c -o ${BINOWN} -g ${BINGRP} -m 666 /dev/null \
+ ${DESTDIR}/var/games/hackdir/record
+ install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/help \
+ ${.CURDIR}/hh ${.CURDIR}/data ${DESTDIR}/var/games/hackdir
+ rm -f ${DESTDIR}/var/games/hackdir/bones*
+
+.include <bsd.prog.mk>
diff --git a/games/hack/Makequest b/games/hack/Makequest
new file mode 100644
index 000000000000..9271c2841c92
--- /dev/null
+++ b/games/hack/Makequest
@@ -0,0 +1,196 @@
+# Hack or Quest Makefile.
+
+# on some systems the termcap library is in -ltermcap
+TERMLIB = -ltermlib
+
+
+# make hack
+GAME = quest
+GAMEDIR = /usr/games/lib/questdir
+CFLAGS = -g -DQUEST
+HACKCSRC = hack.Decl.c\
+ hack.apply.c hack.bones.c hack.c hack.cmd.c hack.do.c\
+ hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c hack.end.c\
+ hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c\
+ hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c\
+ hack.mklev.c hack.mkmaze.c hack.mkobj.c hack.mkshop.c\
+ hack.mon.c hack.monst.c hack.o_init.c hack.objnam.c\
+ hack.options.c hack.pager.c hack.potion.c hack.pri.c\
+ hack.read.c hack.rip.c hack.rumors.c hack.save.c\
+ hack.search.c hack.shk.c hack.shknam.c hack.steal.c\
+ hack.termcap.c hack.timeout.c hack.topl.c\
+ hack.track.c hack.trap.c hack.tty.c hack.unix.c\
+ hack.u_init.c hack.vault.c\
+ hack.wield.c hack.wizard.c hack.worm.c hack.worn.c hack.zap.c\
+ hack.version.c rnd.c alloc.c
+
+CSOURCES = $(HACKCSRC) makedefs.c
+
+HSOURCES = hack.h hack.mfndpos.h config.h\
+ def.edog.h def.eshk.h def.flag.h def.func_tab.h def.gold.h\
+ def.mkroom.h\
+ def.monst.h def.obj.h def.objclass.h def.objects.h\
+ def.permonst.h def.rm.h def.trap.h def.wseg.h
+
+SOURCES = $(CSOURCES) $(HSOURCES)
+
+AUX = data help hh rumors hack.6 hack.sh
+
+DISTR = $(SOURCES) $(AUX) READ_ME Makefile date.h hack.onames.h
+
+HOBJ = hack.Decl.o hack.apply.o hack.bones.o hack.o hack.cmd.o hack.do.o\
+ hack.do_name.o hack.do_wear.o hack.dog.o hack.eat.o hack.end.o\
+ hack.engrave.o hack.fight.o hack.invent.o hack.ioctl.o\
+ hack.lev.o hack.main.o hack.makemon.o hack.mhitu.o hack.mklev.o\
+ hack.mkmaze.o hack.mkobj.o hack.mkshop.o hack.mon.o\
+ hack.monst.o hack.o_init.o hack.objnam.o hack.options.o\
+ hack.pager.o hack.potion.o hack.pri.o\
+ hack.read.o hack.rip.o hack.rumors.o hack.save.o\
+ hack.search.o hack.shk.o hack.shknam.o hack.steal.o\
+ hack.termcap.o hack.timeout.o hack.topl.o\
+ hack.track.o hack.trap.o\
+ hack.tty.o hack.unix.o hack.u_init.o hack.vault.o hack.wield.o\
+ hack.wizard.o hack.worm.o hack.worn.o hack.zap.o\
+ hack.version.o rnd.o alloc.o
+
+$(GAME): $(HOBJ) Makefile
+ @echo "Loading ..."
+ @ld -X -o $(GAME) /lib/crt0.o $(HOBJ) $(TERMLIB) -lc
+
+all: $(GAME) lint
+ @echo "Done."
+
+makedefs: makedefs.c
+ cc -o makedefs makedefs.c
+
+
+hack.onames.h: makedefs def.objects.h
+ makedefs > hack.onames.h
+
+lint:
+# lint cannot have -p here because (i) capitals are meaningful:
+# [Ww]izard, (ii) identifiers may coincide in the first six places:
+# doweararm() versus dowearring().
+# _flsbuf comes from <stdio.h>, a bug in the system libraries.
+ @echo lint -axbh -DLINT ...
+ @lint -axbh -DLINT $(HACKCSRC) | sed '/_flsbuf/d'
+
+
+diff:
+ @- for i in $(SOURCES) $(AUX) ; do \
+ cmp -s $$i $D/$$i || \
+ ( echo diff $D/$$i $$i ; diff $D/$$i $$i ; echo ) ; done
+
+distribution: Makefile
+ @- for i in READ_ME $(SOURCES) $(AUX) Makefile date.h hack.onames.h\
+ ; do \
+ cmp -s $$i $D/$$i || \
+ ( echo cp $$i $D ; cp $$i $D ) ; done
+# the distribution directory also contains the empty files perm and record.
+
+
+install:
+ rm -f $(GAMEDIR)/$(GAME)
+ cp $(GAME) $(GAMEDIR)/$(GAME)
+ chmod 04511 $(GAMEDIR)/$(GAME)
+ rm -f $(GAMEDIR)/bones*
+# cp hack.6 /usr/man/man6
+
+clean:
+ rm -f *.o
+
+
+depend:
+# For the moment we are lazy and disregard /usr/include files because
+# the sources contain them conditionally. Perhaps we should use cpp.
+# ( /bin/grep '^#[ ]*include' $$i | sed -n \
+# -e 's,<\(.*\)>,"/usr/include/\1",' \
+#
+ for i in ${CSOURCES}; do \
+ ( /bin/grep '^#[ ]*include[ ]*"' $$i | sed -n \
+ -e 's/[^"]*"\([^"]*\)".*/\1/' \
+ -e H -e '$$g' -e '$$s/\n/ /g' \
+ -e '$$s/.*/'$$i': &/' -e '$$s/\.c:/.o:/p' \
+ >> makedep); done
+ for i in ${HSOURCES}; do \
+ ( /bin/grep '^#[ ]*include[ ]*"' $$i | sed -n \
+ -e 's/[^"]*"\([^"]*\)".*/\1/' \
+ -e H -e '$$g' -e '$$s/\n/ /g' \
+ -e '$$s/.*/'$$i': &\
+ touch '$$i/p \
+ >> makedep); done
+ @echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
+ @echo '$$r makedep' >>eddep
+ @echo 'w' >>eddep
+ @cp Makefile Makefile.bak
+ ed - Makefile < eddep
+ @rm -f eddep makedep
+ @echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile
+ @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile
+ @echo '# see make depend above' >> Makefile
+ - diff Makefile Makefile.bak
+ @rm -f Makefile.bak
+
+# DO NOT DELETE THIS LINE
+
+hack.Decl.o: hack.h def.mkroom.h
+hack.apply.o: hack.h def.edog.h def.mkroom.h
+hack.bones.o: hack.h
+hack.o: hack.h
+hack.cmd.o: hack.h def.func_tab.h
+hack.do.o: hack.h
+hack.do_name.o: hack.h
+hack.do_wear.o: hack.h
+hack.dog.o: hack.h hack.mfndpos.h def.edog.h def.mkroom.h
+hack.eat.o: hack.h
+hack.end.o: hack.h
+hack.engrave.o: hack.h
+hack.fight.o: hack.h
+hack.invent.o: hack.h def.wseg.h
+hack.ioctl.o: config.h
+hack.lev.o: hack.h def.mkroom.h def.wseg.h
+hack.main.o: hack.h
+hack.makemon.o: hack.h
+hack.mhitu.o: hack.h
+hack.mklev.o: hack.h def.mkroom.h
+hack.mkmaze.o: hack.h def.mkroom.h
+hack.mkobj.o: hack.h
+hack.mkshop.o: hack.h def.mkroom.h def.eshk.h
+hack.mon.o: hack.h hack.mfndpos.h
+hack.monst.o: hack.h def.eshk.h
+hack.o_init.o: config.h def.objects.h hack.onames.h
+hack.objnam.o: hack.h
+hack.options.o: config.h hack.h
+hack.pager.o: hack.h
+hack.potion.o: hack.h
+hack.pri.o: hack.h def.wseg.h
+hack.read.o: hack.h
+hack.rip.o: hack.h
+hack.rumors.o: hack.h
+hack.save.o: hack.h
+hack.search.o: hack.h
+hack.shk.o: hack.h hack.mfndpos.h def.mkroom.h def.eshk.h
+hack.shknam.o: hack.h
+hack.steal.o: hack.h
+hack.termcap.o: config.h def.flag.h
+hack.timeout.o: hack.h
+hack.topl.o: hack.h
+hack.track.o: hack.h
+hack.trap.o: hack.h def.mkroom.h
+hack.tty.o: hack.h
+hack.unix.o: hack.h def.mkroom.h
+hack.u_init.o: hack.h
+hack.vault.o: hack.h def.mkroom.h
+hack.wield.o: hack.h
+hack.wizard.o: hack.h
+hack.worm.o: hack.h def.wseg.h
+hack.worn.o: hack.h
+hack.zap.o: hack.h
+hack.version.o: date.h
+hack.h: config.h def.objclass.h def.monst.h def.gold.h def.trap.h def.obj.h def.flag.h def.rm.h def.permonst.h hack.onames.h
+ touch hack.h
+def.objects.h: config.h def.objclass.h
+ touch def.objects.h
+# DEPENDENCIES MUST END AT END OF FILE
+# IF YOU PUT STUFF HERE IT WILL GO AWAY
+# see make depend above
diff --git a/games/hack/OWNER b/games/hack/OWNER
new file mode 100644
index 000000000000..be2d1e530444
--- /dev/null
+++ b/games/hack/OWNER
@@ -0,0 +1,2 @@
+Andries Brouwer
+mcvax!aeb
diff --git a/games/hack/Original_READ_ME b/games/hack/Original_READ_ME
new file mode 100644
index 000000000000..9d2070be09e4
--- /dev/null
+++ b/games/hack/Original_READ_ME
@@ -0,0 +1,61 @@
+This is export hack, my first semester programming project.
+
+To set it up for your system, you will have to do the following:
+ 1: create a hack uid, to own the top ten list, etc.
+ 2: create a hack directory "/usr/lib/game/hack" is the default.
+ 2.5: make the directory 700 mode. /* sav files go in there...*/
+ 3: modify hack.main.c to use the new directory.
+ 4: modify hack.main.c so it uses the new hack gid. Gid accounts can
+go into magic mode without the password, can get cores with ^G, etc.
+(make sure gid isn't checked anywhere else...)
+ 5: recompile hack.
+ 6: put it in games after making it set-uid hack.
+ 8: fix the bugs I undobtedly left in it.
+ 9: tell me what you think of it.
+
+ Hack uses the UCB file /etc/termcap to get your terminal escape codes.
+If you don't use it, you will have to make extensive changes to hack.pri.c
+
+If you find any bugs (That you think I don't know about), or have any
+awesome new changes (Like a better save (One that works!)), or have ANY
+questions, write me
+ Jay Fenlason
+ 29 East St.
+ Sudbury Mass.
+ 01776
+
+or call me at (617) 443-5036. Since I have both a modem and a teen-age
+sister, Good Luck.
+
+
+Hack is split (roughly) into several source files that do different things.
+I have tried to fit all the procedures having to do with a certain segment
+of the game into a single file, but the job is not the best in the world.
+The rough splits are:
+
+hack.c General random stuff and things I never got around to moving.
+hack.main.c main() and other random procedures, also the lock file stuff.
+hack.mon.c Monsters, moving, attacking, etc.
+hack.do.c drink, eat, read, wield, save, etc.
+hack.do1.c zap, wear, remove, etc...
+hack.pri.c stuff having to do with the screen, most of the terminal
+ independant stuff is in here.
+hack.lev.c temp files and calling of mklev.
+
+Because of the peculiar restraints on our system, I make mklev (create
+a level) a separate procedure execd by hack when needed. The source for
+mklev is (Naturaly) mklev.c. You may want to put mklev back into hack.
+Good luck.
+
+Most of hack was written by me, with help from
+ Kenny Woodland (KW) (general random things including
+ the original BUZZ())
+ Mike Thome (MT) (The original chamelian)
+ and Jon Payne (JP) (The original lock file kludge and
+ the massive CURS())
+
+This entire program would not have been possible without the SFSU Logo
+Workshop. I am eternally grateful to all of our students (Especially K.L.),
+without whom I would never have seen Rogue. I am especially grateful to
+Mike Clancy, without whose generous help I would never have gotten to play
+ROGUE.
diff --git a/games/hack/READ_ME b/games/hack/READ_ME
new file mode 100644
index 000000000000..cfe6ca2fe3eb
--- /dev/null
+++ b/games/hack/READ_ME
@@ -0,0 +1,92 @@
+Hack is a display oriented dungeons & dragons - like game.
+Both display and command structure resemble rogue.
+(For a game with the same structure but entirely different display -
+a real cave instead of dull rectangles - try Quest)
+
+Hack was originally written by Jay Fenlason (at lincolnsudbury:
+ 29 East St., Sudbury Mass., 01776) with help from
+ Kenny Woodland, Mike Thome and Jon Payne.
+Basically it was an implementation of Rogue, however, with 52+ instead of 26
+ monster types.
+The current version is more than thrice as large (with such new features as
+ the dog, the long worms, the shops, etc.) and almost entirely rewritten
+ (only the display routines are the original ones - I must rewrite these
+ too one day; especially when you are blind strange things still happen).
+
+Files for hack:
+ hack The actual game
+ record Top 100 list (just start with an empty file)
+ news Tells about recent changes in hack, or bugs found ...
+ (Just start with no news file.)
+ data Auxiliary file used by hack to give you the names
+ and sometimes some more information on the
+ objects and monsters.
+ help Introductory information (no doubt outdated).
+ hh Compactified version of help.
+ perm An empty file used for locking purposes.
+ rumors Texts for fortune cookies.
+ (Some of these contain information on the game,
+ others are just plain stupid. Additional rumors
+ are appreciated.)
+ hack.sh A shell script.
+ (We have hack.sh in /usr/games/hack and
+ hack in /usr/games/lib/hackdir/hack and all the other
+ hack stuff in /usr/games/lib/hackdir - perhaps this
+ will make the script clear.
+ There is no need for you to use it.)
+ READ_ME This file.
+ Original_READ_ME Jay Fenlason's READ_ME
+
+System files used:
+ /etc/termcap Used in conjunction with the environment variable
+ $TERM.
+ /bin/cat
+ /usr/ucb/more
+ /bin/sh Used when $SHELL is undefined.
+
+How to install hack:
+0. Compile the sources. Perhaps you should first look at the file config.h
+ and define BSD if you are on a BSDtype system,
+ define STUPID if your C-compiler chokes on complicated expressions.
+ Make sure schar and uchar represent signed and unsigned types.
+ If your C compiler doesnt allow initialization of bit fields
+ change Bitfield. When config.h looks reasonable, say 'make'.
+ (Perhaps you have to change TERMLIB in the makefile.)
+1. If it didnt exist already, introduce a loginname `play' .
+2. The program hack resides in a directory so that it is executable
+ for everybody and is suid play:
+ ---s--s--x 1 play 206848 Apr 3 00:17 hack
+ Perhaps you wish to restrict playing to certain hours, or have games
+ running under nice; in that case you might write a program play.c
+ such that the program play is suid play and executable for everybody
+ while all the games in /usr/games are readable or executable for
+ play only; all the program play does is asking for the name of a game,
+ checking that time-of-day and system load do not forbid playing,
+ and then executing the game. Thus:
+ -r-sr-sr-x 1 play 13312 May 24 12:52 play
+ ---x------ 1 play 206848 Apr 3 00:17 hack
+ If you are worried about security you might let play do
+ chroot("/usr/games") so that no player can get access to the rest
+ of the system via shell escapes and the likes.
+ If you #define SECURE in config.h then hack will not setuid(getuid())
+ before executing a chdir(). Hack will always do setuid(getuid()) with
+ a fork. If you do not define UNIX then hack will not fork.
+3. The rest of the stuff belonging to hack sits in a subdirectory hackdir
+ (on our system /usr/games/lib/hackdir) with modes
+ drwx------ 3 play 1024 Aug 9 09:03 hackdir
+ Here all the temporary files will be created (with names like xlock.17
+ or user.5).
+4. If you are not really short on file space, creating a subdirectory
+ hackdir/save (modes again drwx------) will enable users to save their
+ unfinished games.
+
+The program hack is called
+$ hack [-d hackdir] [maxnrofplayers]
+(for playing) or
+$ hack [-d hackdir] -s [listofusers | limit | all]
+(for seeing part of the scorelist).
+The shell file hack (in this kit called hack.sh) takes care of
+calling hack with the right arguments.
+
+Send complaints, bug reports, suggestions for improvements to
+mcvax!aeb - in real life Andries Brouwer.
diff --git a/games/hack/alloc.c b/games/hack/alloc.c
new file mode 100644
index 000000000000..d94bf8b903b8
--- /dev/null
+++ b/games/hack/alloc.c
@@ -0,0 +1,47 @@
+/* alloc.c - version 1.0.2 */
+#ifdef LINT
+
+/*
+ a ridiculous definition, suppressing
+ "possible pointer alignment problem" for (long *) malloc()
+ "enlarg defined but never used"
+ "ftell defined (in <stdio.h>) but never used"
+ from lint
+*/
+#include <stdio.h>
+long *
+alloc(n) unsigned n; {
+long dummy = ftell(stderr);
+ if(n) dummy = 0; /* make sure arg is used */
+ return(&dummy);
+}
+
+#else
+
+extern char *malloc();
+extern char *realloc();
+
+long *
+alloc(lth)
+register unsigned lth;
+{
+ register char *ptr;
+
+ if(!(ptr = malloc(lth)))
+ panic("Cannot get %d bytes", lth);
+ return((long *) ptr);
+}
+
+long *
+enlarge(ptr,lth)
+register char *ptr;
+register unsigned lth;
+{
+ register char *nptr;
+
+ if(!(nptr = realloc(ptr,lth)))
+ panic("Cannot reallocate %d bytes", lth);
+ return((long *) nptr);
+}
+
+#endif LINT
diff --git a/games/hack/config.h b/games/hack/config.h
new file mode 100644
index 000000000000..f382937ba554
--- /dev/null
+++ b/games/hack/config.h
@@ -0,0 +1,139 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* config.h - version 1.0.3 */
+
+#include "pathnames.h"
+
+#ifndef CONFIG /* make sure the compiler doesnt see the typedefs twice */
+
+#define CONFIG
+#define UNIX /* delete if no fork(), exec() available */
+#define CHDIR /* delete if no chdir() available */
+
+/*
+ * Some include files are in a different place under SYSV
+ * BSD SYSV
+ * <sys/wait.h> <wait.h>
+ * <sys/time.h> <time.h>
+ * <sgtty.h> <termio.h>
+ * Some routines are called differently
+ * index strchr
+ * rindex strrchr
+ * Also, the code for suspend and various ioctls is only given for BSD4.2
+ * (I do not have access to a SYSV system.)
+ */
+#define BSD /* delete this line on System V */
+
+/* #define STUPID */ /* avoid some complicated expressions if
+ your C compiler chokes on them */
+/* #define PYRAMID_BUG */ /* avoid a bug on the Pyramid */
+/* #define NOWAITINCLUDE */ /* neither <wait.h> nor <sys/wait.h> exists */
+
+#define WIZARD "bruno" /* the person allowed to use the -D option */
+#define RECORD "record"/* the file containing the list of topscorers */
+#define NEWS "news" /* the file containing the latest hack news */
+#define HELP "help" /* the file containing a description of the commands */
+#define SHELP "hh" /* abbreviated form of the same */
+#define RUMORFILE "rumors" /* a file with fortune cookies */
+#define DATAFILE "data" /* a file giving the meaning of symbols used */
+#define FMASK 0660 /* file creation mask */
+#define HLOCK "perm" /* an empty file used for locking purposes */
+#define LLOCK "safelock" /* link to previous */
+
+#ifdef UNIX
+/*
+ * Define DEF_PAGER as your default pager, e.g. "/bin/cat" or "/usr/ucb/more"
+ * If defined, it can be overridden by the environment variable PAGER.
+ * Hack will use its internal pager if DEF_PAGER is not defined.
+ * (This might be preferable for security reasons.)
+ * #define DEF_PAGER ".../mydir/mypager"
+ */
+
+/*
+ * If you define MAIL, then the player will be notified of new mail
+ * when it arrives. If you also define DEF_MAILREADER then this will
+ * be the default mail reader, and can be overridden by the environment
+ * variable MAILREADER; otherwise an internal pager will be used.
+ * A stat system call is done on the mailbox every MAILCKFREQ moves.
+ */
+/* #define MAIL */
+#define DEF_MAILREADER _PATH_MAIL /* or e.g. /bin/mail */
+#define MAILCKFREQ 100
+
+
+#define SHELL /* do not delete the '!' command */
+
+#ifdef BSD
+#define SUSPEND /* let ^Z suspend the game */
+#endif BSD
+#endif UNIX
+
+#ifdef CHDIR
+/*
+ * If you define HACKDIR, then this will be the default playground;
+ * otherwise it will be the current directory.
+ */
+#ifdef QUEST
+#define HACKDIR _PATH_QUEST
+#else QUEST
+#define HACKDIR _PATH_HACK
+#endif QUEST
+
+/*
+ * Some system administrators are stupid enough to make Hack suid root
+ * or suid daemon, where daemon has other powers besides that of reading or
+ * writing Hack files. In such cases one should be careful with chdir's
+ * since the user might create files in a directory of his choice.
+ * Of course SECURE is meaningful only if HACKDIR is defined.
+ */
+#define SECURE /* do setuid(getuid()) after chdir() */
+
+/*
+ * If it is desirable to limit the number of people that can play Hack
+ * simultaneously, define HACKDIR, SECURE and MAX_NR_OF_PLAYERS.
+ * #define MAX_NR_OF_PLAYERS 100
+ */
+#endif CHDIR
+
+/* size of terminal screen is (at least) (ROWNO+2) by COLNO */
+#define COLNO 80
+#define ROWNO 22
+
+/*
+ * small signed integers (8 bits suffice)
+ * typedef char schar;
+ * will do when you have signed characters; otherwise use
+ * typedef short int schar;
+ */
+typedef char schar;
+
+/*
+ * small unsigned integers (8 bits suffice - but 7 bits do not)
+ * - these are usually object types; be careful with inequalities! -
+ * typedef unsigned char uchar;
+ * will be satisfactory if you have an "unsigned char" type; otherwise use
+ * typedef unsigned short int uchar;
+ */
+typedef unsigned char uchar;
+
+/*
+ * small integers in the range 0 - 127, usually coordinates
+ * although they are nonnegative they must not be declared unsigned
+ * since otherwise comparisons with signed quantities are done incorrectly
+ */
+typedef schar xchar;
+typedef xchar boolean; /* 0 or 1 */
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Declaration of bitfields in various structs; if your C compiler
+ * doesnt handle bitfields well, e.g., if it is unable to initialize
+ * structs containing bitfields, then you might use
+ * #define Bitfield(x,n) uchar x
+ * since the bitfields used never have more than 7 bits. (Most have 1 bit.)
+ */
+#define Bitfield(x,n) unsigned x:n
+
+#define SIZE(x) (int)(sizeof(x) / sizeof(x[0]))
+
+#endif CONFIG
diff --git a/games/hack/data b/games/hack/data
new file mode 100644
index 000000000000..5d8d509b0fd9
--- /dev/null
+++ b/games/hack/data
@@ -0,0 +1,232 @@
+ Hack & Quest data file - version 1.0.3
+@ human (or you)
+- a wall
+| a wall
++ a door
+. the floor of a room
+ a dark part of a room
+# a corridor
+} water filled area
+< the staircase to the previous level
+> the staircase to the next level
+^ a trap
+$ a pile, pot or chest of gold
+%% a piece of food
+! a potion
+* a gem
+? a scroll
+= a ring
+/ a wand
+[ a suit of armor
+) a weapon
+( a useful item (camera, key, rope etc.)
+0 an iron ball
+_ an iron chain
+` an enormous rock
+" an amulet
+, a trapper
+: a chameleon
+; a giant eel
+' a lurker above
+& a demon
+A a giant ant
+B a giant bat
+C a centaur;
+ Of all the monsters put together by the Greek imagination
+ the Centaurs (Kentauroi) constituted a class in themselves.
+ Despite a strong streak of sensuality in their make-up,
+ their normal behaviour was moral, and they took a kindly
+ thought of man's welfare. The attempted outrage of Nessos on
+ Deianeira, and that of the whole tribe of Centaurs on the
+ Lapith women, are more than offset by the hospitality of
+ Pholos and by the wisdom of Cheiron, physician, prophet,
+ lyrist, and the instructor of Achilles. Further, the Cen-
+ taurs were peculiar in that their nature, which united the
+ body of a horse with the trunk and head of a man, involved
+ an unthinkable duplication of vital organs and important
+ members. So grotesque a combination seems almost un-Greek.
+ These strange creatures were said to live in the caves and
+ clefts of the mountains, myths associating them especially
+ with the hills of Thessaly and the range of Erymanthos.
+ [Mythology of all races, Vol. 1, pp. 270-271]
+D a dragon;
+ In the West the dragon was the natural enemy of man. Although
+ preferring to live in bleak and desolate regions, whenever it was
+ seen among men it left in its wake a trail of destruction and
+ disease. Yet any attempt to slay this beast was a perilous under-
+ taking. For the dragon's assailant had to contend not only with
+ clouds of sulphurous fumes pouring from its fire-breathing nos-
+ trils, but also with the thrashings of its tail, the most deadly
+ part of its serpent-like body.
+ [From: Mythical Beasts by Deirdre Headon (The Leprechaun Library)]
+E a floating eye
+F a freezing sphere
+G a gnome;
+ ... And then a gnome came by, carrying a bundle, an old fellow
+ three times as large as an imp and wearing clothes of a sort,
+ especially a hat. And he was clearly just as frightened as the
+ imps though he could not go so fast. Ramon Alonzo saw that there
+ must be some great trouble that was vexing magical things; and,
+ since gnomes speak the language of men, and will answer if spoken
+ to gently, he raised his hat, and asked of the gnome his name.
+ The gnome did not stop his hasty shuffle a moment as he answered
+ 'Alaraba' and grabbed the rim of his hat but forgot to doff it.
+ 'What is the trouble, Alaraba?' said Ramon Alonzo.
+ 'White magic. Run!' said the gnome ...
+ [From: The Charwoman's Shadow, by Lord Dunsany.]
+H a hobgoblin;
+ Hobgoblin. Used by the Puritans and in later times for
+ wicked goblin spirits, as in Bunyan's 'Hobgoblin nor foul
+ friend', but its more correct use is for the friendly spir-
+ its of the brownie type. In 'A midsummer night's dream' a
+ fairy says to Shakespeare's Puck:
+ Those that Hobgoblin call you, and sweet Puck,
+ You do their work, and they shall have good luck:
+ Are you not he?
+ and obviously Puck would not wish to be called a hobgoblin
+ if that was an ill-omened word.
+ Hobgoblins are on the whole, good-humoured and ready to be
+ helpful, but fond of practical joking, and like most of the
+ fairies rather nasty people to annoy. Boggarts hover on the
+ verge of hobgoblindom. Bogles are just over the edge.
+ One Hob mentioned by Henderson, was Hob Headless who haunted
+ the road between Hurworth and Neasham, but could not cross
+ the little river Kent, which flowed into the Tess. He was
+ exorcised and laid under a large stone by the roadside for
+ ninety-nine years and a day. If anyone was so unwary as to
+ sit on that stone, he would be unable to quit it for ever.
+ The ninety-nine years is nearly up, so trouble may soon be
+ heard of on the road between Hurworth and Neasham.
+ [Katharine Briggs, A dictionary of Fairies]
+I an invisible stalker
+J a jackal
+K a kobold
+L a leprechaun;
+ The Irish Leprechaun is the Faeries' shoemaker and is known
+ under various names in different parts of Ireland: Cluri-
+ caune in Cork, Lurican in Kerry, Lurikeen in Kildare and Lu-
+ rigadaun in Tipperary. Although he works for the Faeries,
+ the Leprechaun is not of the same species. He is small, has
+ dark skin and wears strange clothes. His nature has some-
+ thing of the manic-depressive about it: first he is quite
+ happy, whistling merrily as he nails a sole on to a shoe; a
+ few minutes later, he is sullen and morose, drunk on his
+ home-made heather ale. The Leprechaun's two great loves are
+ tobacco and whiskey, and he is a first-rate con-man, impos-
+ sible to out-fox. No one, no matter how clever, has ever
+ managed to cheat him out of his hidden pot of gold or his
+ magic shilling. At the last minute he always thinks of some
+ way to divert his captor's attention and vanishes in the
+ twinkling of an eye.
+ [From: A Field Guide to the Little People
+ by Nancy Arrowsmith & George Moorse. ]
+M a mimic
+N a nymph
+O an orc
+P a purple worm
+Q a quasit
+R a rust monster
+S a snake
+T a troll
+U an umber hulk
+V a vampire
+W a wraith
+X a xorn
+Y a yeti
+Z a zombie
+a an acid blob
+b a giant beetle
+c a cockatrice;
+ Once in a great while, when the positions of the stars are
+ just right, a seven-year-old rooster will lay an egg. Then,
+ along will come a snake, to coil around the egg, or a toad,
+ to squat upon the egg, keeping it warm and helping it to
+ hatch. When it hatches, out comes a creature called basil-
+ isk, or cockatrice, the most deadly of all creatures. A sin-
+ gle glance from its yellow, piercing toad's eyes will kill
+ both man and beast. Its power of destruction is said to be
+ so great that sometimes simply to hear its hiss can prove
+ fatal. Its breath is so venomenous that it causes all vege-
+ tation to wither.
+ There is, however, one creature which can withstand the
+ basilisk's deadly gaze, and this is the weasel. No one knows
+ why this is so, but although the fierce weasel can slay the
+ basilisk, it will itself be killed in the struggle. Perhaps
+ the weasel knows the basilisk's fatal weakness: if it ever
+ sees its own reflection in a mirror it will perish instant-
+ ly. But even a dead basilisk is dangerous, for it is said
+ that merely touching its lifeless body can cause a person to
+ sicken and die.
+ [From: Mythical Beasts by Deirdre Headon (The Leprechaun
+ Library) and other sources. ]
+d a dog
+e an ettin
+f a fog cloud
+g a gelatinous cube
+h a homunculus
+i an imp;
+ ... imps ... little creatures of two feet high that could
+ gambol and jump prodigiously; ...
+ [From: The Charwoman's Shadow, by Lord Dunsany.]
+
+ An 'imp' is an off-shoot or cutting. Thus an 'ymp tree' was
+ a grafted tree, or one grown from a cutting, not from seed.
+ 'Imp' properly means a small devil, an off-shoot of Satan,
+ but the distinction between goblins or bogles and imps from
+ hell is hard to make, and many in the Celtic countries as
+ well as the English Puritans regarded all fairies as devils.
+ The fairies of tradition often hover uneasily between the
+ ghostly and the diabolic state.
+ [Katharine Briggs, A dictionary of Fairies]
+j a jaguar
+k a killer bee
+l a leocrotta
+m a minotaur
+n a nurse
+o an owlbear
+p a piercer
+q a quivering blob
+r a giant rat
+s a scorpion
+t a tengu;
+ The tengu was the most troublesome creature of Japanese
+ legend. Part bird and part man, with red beak for a nose
+ and flashing eyes, the tengu was notorious for stirring up
+ feuds and prolonging enmity between families. Indeed, the
+ belligerent tengus were supposed to have been man's first
+ instructors in the use of arms.
+ [From: Mythical Beasts by Deirdre Headon
+ (The Leprechaun Library). ]
+u a unicorn;
+ Men have always sought the elusive unicorn, for the single
+ twisted horn which projected from its forehead was thought
+ to be a powerful talisman. It was said that the unicorn had
+ simply to dip the tip of its horn in a muddy pool for the
+ water to become pure. Men also believed that to drink from
+ this horn was a protection against all sickness, and that if
+ the horn was ground to a powder it would act as an antidote
+ to all poisons. Less than 200 years ago in France, the horn
+ of a unicorn was used in a ceremony to test the royal food
+ for poison.
+ Although only the size of a small horse, the unicorn is a
+ very fierce beast, capable of killing an elephant with a
+ single thrust from its horn. Its fleetness of foot also
+ makes this solitary creature difficult to capture. However,
+ it can be tamed and captured by a maiden. Made gentle by the
+ sight of a virgin, the unicorn can be lured to lay its head
+ in her lap, and in this docile mood, the maiden may secure
+ it with a golden rope.
+ [From: Mythical Beasts by Deirdre Headon
+ (The Leprechaun Library). ]
+v a violet fungi
+w a long worm;
+ From its teeth the crysknife can be manufactured.
+~ the tail of a long worm
+x a xan;
+ The xan were animals sent to prick the legs of the Lords of Xibalba.
+y a yellow light
+z a zruty;
+ The zruty are wild and gigantic beings, living in the wildernesses
+ of the Tatra mountains.
+1 The wizard of Yendor
+2 The mail daemon
diff --git a/games/hack/date.h b/games/hack/date.h
new file mode 100644
index 000000000000..9a7ef7688854
--- /dev/null
+++ b/games/hack/date.h
@@ -0,0 +1,2 @@
+
+char datestring[] = "Tue Jul 23 1985";
diff --git a/games/hack/def.edog.h b/games/hack/def.edog.h
new file mode 100644
index 000000000000..a5c2b4616e01
--- /dev/null
+++ b/games/hack/def.edog.h
@@ -0,0 +1,12 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.edog.h - version 1.0.2 */
+
+struct edog {
+ long hungrytime; /* at this time dog gets hungry */
+ long eattime; /* dog is eating */
+ long droptime; /* moment dog dropped object */
+ unsigned dropdist; /* dist of drpped obj from @ */
+ unsigned apport; /* amount of training */
+ long whistletime; /* last time he whistled */
+};
+#define EDOG(mp) ((struct edog *)(&(mp->mextra[0])))
diff --git a/games/hack/def.eshk.h b/games/hack/def.eshk.h
new file mode 100644
index 000000000000..2ebf2804d348
--- /dev/null
+++ b/games/hack/def.eshk.h
@@ -0,0 +1,24 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.eshk.h - version 1.0.2 : added 'following' */
+
+#define BILLSZ 200
+struct bill_x {
+ unsigned bo_id;
+ unsigned useup:1;
+ unsigned bquan:7;
+ unsigned price; /* price per unit */
+};
+
+struct eshk {
+ long int robbed; /* amount stolen by most recent customer */
+ boolean following; /* following customer since he owes us sth */
+ schar shoproom; /* index in rooms; set by inshop() */
+ coord shk; /* usual position shopkeeper */
+ coord shd; /* position shop door */
+ int shoplevel; /* level of his shop */
+ int billct;
+ struct bill_x bill[BILLSZ];
+ int visitct; /* nr of visits by most recent customer */
+ char customer[PL_NSIZ]; /* most recent customer */
+ char shknam[PL_NSIZ];
+};
diff --git a/games/hack/def.flag.h b/games/hack/def.flag.h
new file mode 100644
index 000000000000..221f33dd9166
--- /dev/null
+++ b/games/hack/def.flag.h
@@ -0,0 +1,42 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.flag.h - version 1.0.3 */
+
+struct flag {
+ unsigned ident; /* social security number for each monster */
+ unsigned debug:1; /* in debugging mode */
+#define wizard flags.debug
+ unsigned toplin:2; /* a top line (message) has been printed */
+ /* 0: top line empty; 2: no --More-- reqd. */
+ unsigned cbreak:1; /* in cbreak mode, rogue format */
+ unsigned standout:1; /* use standout for --More-- */
+ unsigned nonull:1; /* avoid sending nulls to the terminal */
+ unsigned time:1; /* display elapsed 'time' */
+ unsigned nonews:1; /* suppress news printing */
+ unsigned notombstone:1;
+ unsigned end_top, end_around; /* describe desired score list */
+ unsigned end_own:1; /* idem (list all own scores) */
+ unsigned no_rest_on_space:1; /* spaces are ignored */
+ unsigned beginner:1;
+ unsigned female:1;
+ unsigned invlet_constant:1; /* let objects keep their
+ inventory symbol */
+ unsigned move:1;
+ unsigned mv:1;
+ unsigned run:3; /* 0: h (etc), 1: H (etc), 2: fh (etc) */
+ /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */
+ unsigned nopick:1; /* do not pickup objects */
+ unsigned echo:1; /* 1 to echo characters */
+ unsigned botl:1; /* partially redo status line */
+ unsigned botlx:1; /* print an entirely new bottom line */
+ unsigned nscrinh:1; /* inhibit nscr() in pline(); */
+ unsigned made_amulet:1;
+ unsigned no_of_wizards:2;/* 0, 1 or 2 (wizard and his shadow) */
+ /* reset from 2 to 1, but never to 0 */
+ unsigned moonphase:3;
+#define NEW_MOON 0
+#define FULL_MOON 4
+
+};
+
+extern struct flag flags;
+
diff --git a/games/hack/def.func_tab.h b/games/hack/def.func_tab.h
new file mode 100644
index 000000000000..63f74d2ef618
--- /dev/null
+++ b/games/hack/def.func_tab.h
@@ -0,0 +1,16 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.func_tab.h - version 1.0.2 */
+
+struct func_tab {
+ char f_char;
+ int (*f_funct)();
+};
+
+extern struct func_tab cmdlist[];
+
+struct ext_func_tab {
+ char *ef_txt;
+ int (*ef_funct)();
+};
+
+extern struct ext_func_tab extcmdlist[];
diff --git a/games/hack/def.gen.h b/games/hack/def.gen.h
new file mode 100644
index 000000000000..f1e44fc98977
--- /dev/null
+++ b/games/hack/def.gen.h
@@ -0,0 +1,15 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.gen.h version 1.0.1: added ONCE flag */
+
+struct gen {
+ struct gen *ngen;
+ xchar gx,gy;
+ unsigned gflag; /* 037: trap type; 040: SEEN flag */
+ /* 0100: ONCE only */
+#define TRAPTYPE 037
+#define SEEN 040
+#define ONCE 0100
+};
+extern struct gen *fgold, *ftrap;
+struct gen *g_at();
+#define newgen() (struct gen *) alloc(sizeof(struct gen))
diff --git a/games/hack/def.gold.h b/games/hack/def.gold.h
new file mode 100644
index 000000000000..808890883509
--- /dev/null
+++ b/games/hack/def.gold.h
@@ -0,0 +1,12 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.gold.h - version 1.0.2 */
+
+struct gold {
+ struct gold *ngold;
+ xchar gx,gy;
+ long amount;
+};
+
+extern struct gold *fgold;
+struct gold *g_at();
+#define newgold() (struct gold *) alloc(sizeof(struct gold))
diff --git a/games/hack/def.mkroom.h b/games/hack/def.mkroom.h
new file mode 100644
index 000000000000..ddbb62be1b02
--- /dev/null
+++ b/games/hack/def.mkroom.h
@@ -0,0 +1,26 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.mkroom.h - version 1.0.3 */
+
+struct mkroom {
+ schar lx,hx,ly,hy; /* usually xchar, but hx may be -1 */
+ schar rtype,rlit,doorct,fdoor;
+};
+
+#define MAXNROFROOMS 15
+extern struct mkroom rooms[MAXNROFROOMS+1];
+
+#define DOORMAX 100
+extern coord doors[DOORMAX];
+
+/* various values of rtype */
+/* 0: ordinary room; 8-15: various shops */
+/* Note: some code assumes that >= 8 means shop, so be careful when adding
+ new roomtypes */
+#define SWAMP 3
+#define VAULT 4
+#define BEEHIVE 5
+#define MORGUE 6
+#define ZOO 7
+#define SHOPBASE 8
+#define WANDSHOP 9
+#define GENERAL 15
diff --git a/games/hack/def.monst.h b/games/hack/def.monst.h
new file mode 100644
index 000000000000..88836af7aee3
--- /dev/null
+++ b/games/hack/def.monst.h
@@ -0,0 +1,60 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.monst.h - version 1.0.2 */
+
+struct monst {
+ struct monst *nmon;
+ struct permonst *data;
+ unsigned m_id;
+ xchar mx,my;
+ xchar mdx,mdy; /* if mdispl then pos where last displayed */
+#define MTSZ 4
+ coord mtrack[MTSZ]; /* monster track */
+ schar mhp,mhpmax;
+ char mappearance; /* nonzero for undetected 'M's and for '1's */
+ Bitfield(mimic,1); /* undetected mimic */
+ Bitfield(mdispl,1); /* mdx,mdy valid */
+ Bitfield(minvis,1); /* invisible */
+ Bitfield(cham,1); /* shape-changer */
+ Bitfield(mhide,1); /* hides beneath objects */
+ Bitfield(mundetected,1); /* not seen in present hiding place */
+ Bitfield(mspeed,2);
+ Bitfield(msleep,1);
+ Bitfield(mfroz,1);
+ Bitfield(mconf,1);
+ Bitfield(mflee,1); /* fleeing */
+ Bitfield(mfleetim,7); /* timeout for mflee */
+ Bitfield(mcan,1); /* has been cancelled */
+ Bitfield(mtame,1); /* implies peaceful */
+ Bitfield(mpeaceful,1); /* does not attack unprovoked */
+ Bitfield(isshk,1); /* is shopkeeper */
+ Bitfield(isgd,1); /* is guard */
+ Bitfield(mcansee,1); /* cansee 1, temp.blinded 0, blind 0 */
+ Bitfield(mblinded,7); /* cansee 0, temp.blinded n, blind 0 */
+ Bitfield(mtrapped,1); /* trapped in a pit or bear trap */
+ Bitfield(mnamelth,6); /* length of name (following mxlth) */
+#ifndef NOWORM
+ Bitfield(wormno,5); /* at most 31 worms on any level */
+#endif NOWORM
+ unsigned mtrapseen; /* bitmap of traps we've been trapped in */
+ long mlstmv; /* prevent two moves at once */
+ struct obj *minvent;
+ long mgold;
+ unsigned mxlth; /* length of following data */
+ /* in order to prevent alignment problems mextra should
+ be (or follow) a long int */
+ long mextra[1]; /* monster dependent info */
+};
+
+#define newmonst(xl) (struct monst *) alloc((unsigned)(xl) + sizeof(struct monst))
+
+extern struct monst *fmon;
+extern struct monst *fallen_down;
+struct monst *m_at();
+
+/* these are in mspeed */
+#define MSLOW 1 /* slow monster */
+#define MFAST 2 /* speeded monster */
+
+#define NAME(mtmp) (((char *) mtmp->mextra) + mtmp->mxlth)
+#define MREGEN "TVi1"
+#define UNDEAD "ZVW "
diff --git a/games/hack/def.obj.h b/games/hack/def.obj.h
new file mode 100644
index 000000000000..50b21df1b8f0
--- /dev/null
+++ b/games/hack/def.obj.h
@@ -0,0 +1,48 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.obj.h - version 1.0.3 */
+
+struct obj {
+ struct obj *nobj;
+ unsigned o_id;
+ unsigned o_cnt_id; /* id of container object is in */
+ xchar ox,oy;
+ xchar odx,ody;
+ uchar otyp;
+ uchar owt;
+ uchar quan; /* use oextra for tmp gold objects */
+ schar spe; /* quality of weapon, armor or ring (+ or -)
+ number of charges for wand ( >= -1 )
+ special for uball and amulet %% BAH */
+ char olet;
+ char invlet;
+ Bitfield(oinvis,1); /* not yet implemented */
+ Bitfield(odispl,1);
+ Bitfield(known,1); /* exact nature known */
+ Bitfield(dknown,1); /* color or text known */
+ Bitfield(cursed,1);
+ Bitfield(unpaid,1); /* on some bill */
+ Bitfield(rustfree,1);
+ Bitfield(onamelth,6);
+ long age; /* creation date */
+ long owornmask;
+#define W_ARM 01L
+#define W_ARM2 02L
+#define W_ARMH 04L
+#define W_ARMS 010L
+#define W_ARMG 020L
+#define W_ARMOR (W_ARM | W_ARM2 | W_ARMH | W_ARMS | W_ARMG)
+#define W_RINGL 010000L /* make W_RINGL = RING_LEFT (see uprop) */
+#define W_RINGR 020000L
+#define W_RING (W_RINGL | W_RINGR)
+#define W_WEP 01000L
+#define W_BALL 02000L
+#define W_CHAIN 04000L
+ long oextra[1]; /* used for name of ordinary objects - length
+ is flexible; amount for tmp gold objects */
+};
+
+extern struct obj *fobj;
+
+#define newobj(xl) (struct obj *) alloc((unsigned)(xl) + sizeof(struct obj))
+#define ONAME(otmp) ((char *) otmp->oextra)
+#define OGOLD(otmp) (otmp->oextra[0])
diff --git a/games/hack/def.objclass.h b/games/hack/def.objclass.h
new file mode 100644
index 000000000000..9e17de237ee7
--- /dev/null
+++ b/games/hack/def.objclass.h
@@ -0,0 +1,60 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.objclass.h - version 1.0.3 */
+
+/* definition of a class of objects */
+
+struct objclass {
+ char *oc_name; /* actual name */
+ char *oc_descr; /* description when name unknown */
+ char *oc_uname; /* called by user */
+ Bitfield(oc_name_known,1);
+ Bitfield(oc_merge,1); /* merge otherwise equal objects */
+ char oc_olet;
+ schar oc_prob; /* probability for mkobj() */
+ schar oc_delay; /* delay when using such an object */
+ uchar oc_weight;
+ schar oc_oc1, oc_oc2;
+ int oc_oi;
+#define nutrition oc_oi /* for foods */
+#define a_ac oc_oc1 /* for armors - only used in ARM_BONUS */
+#define ARM_BONUS(obj) ((10 - objects[obj->otyp].a_ac) + obj->spe)
+#define a_can oc_oc2 /* for armors */
+#define bits oc_oc1 /* for wands and rings */
+ /* wands */
+#define NODIR 1
+#define IMMEDIATE 2
+#define RAY 4
+ /* rings */
+#define SPEC 1 /* +n is meaningful */
+#define wldam oc_oc1 /* for weapons and PICK_AXE */
+#define wsdam oc_oc2 /* for weapons and PICK_AXE */
+#define g_val oc_oi /* for gems: value on exit */
+};
+
+extern struct objclass objects[];
+
+/* definitions of all object-symbols */
+
+#define ILLOBJ_SYM '\\'
+#define AMULET_SYM '"'
+#define FOOD_SYM '%'
+#define WEAPON_SYM ')'
+#define TOOL_SYM '('
+#define BALL_SYM '0'
+#define CHAIN_SYM '_'
+#define ROCK_SYM '`'
+#define ARMOR_SYM '['
+#define POTION_SYM '!'
+#define SCROLL_SYM '?'
+#define WAND_SYM '/'
+#define RING_SYM '='
+#define GEM_SYM '*'
+/* Other places with explicit knowledge of object symbols:
+ * ....shk.c: char shtypes[] = "=/)%?![";
+ * mklev.c: "=/)%?![<>"
+ * hack.mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**";
+ * hack.apply.c: otmp = getobj("0#%", "put in");
+ * hack.eat.c: otmp = getobj("%", "eat");
+ * hack.invent.c: if(index("!%?[)=*(0/\"", sym)){
+ * hack.invent.c: || index("%?!*",otmp->olet))){
+ */
diff --git a/games/hack/def.objects.h b/games/hack/def.objects.h
new file mode 100644
index 000000000000..b4400fc4a70b
--- /dev/null
+++ b/games/hack/def.objects.h
@@ -0,0 +1,289 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.objects.h - version 1.0.3 */
+
+/* objects have letter " % ) ( 0 _ ` [ ! ? / = * */
+#include "config.h"
+#include "def.objclass.h"
+#define NULL (char *)0
+
+struct objclass objects[] = {
+
+ { "strange object", NULL, NULL, 1, 0,
+ ILLOBJ_SYM, 0, 0, 0, 0, 0, 0 },
+ { "amulet of Yendor", NULL, NULL, 1, 0,
+ AMULET_SYM, 100, 0, 2, 0, 0, 0 },
+
+#define FOOD(name,prob,delay,weight,nutrition) { name, NULL, NULL, 1, 1,\
+ FOOD_SYM, prob, delay, weight, 0, 0, nutrition }
+
+/* dog eats foods 0-4 but prefers 1 above 0,2,3,4 */
+/* food 4 can be read */
+/* food 5 improves your vision */
+/* food 6 makes you stronger (like Popeye) */
+/* foods CORPSE up to CORPSE+52 are cadavers */
+
+ FOOD("food ration", 50, 5, 4, 800),
+ FOOD("tripe ration", 20, 1, 2, 200),
+ FOOD("pancake", 3, 1, 1, 200),
+ FOOD("dead lizard", 3, 0, 1, 40),
+ FOOD("fortune cookie", 7, 0, 1, 40),
+ FOOD("carrot", 2, 0, 1, 50),
+ FOOD("tin", 7, 0, 1, 0),
+ FOOD("orange", 1, 0, 1, 80),
+ FOOD("apple", 1, 0, 1, 50),
+ FOOD("pear", 1, 0, 1, 50),
+ FOOD("melon", 1, 0, 1, 100),
+ FOOD("banana", 1, 0, 1, 80),
+ FOOD("candy bar", 1, 0, 1, 100),
+ FOOD("egg", 1, 0, 1, 80),
+ FOOD("clove of garlic", 1, 0, 1, 40),
+ FOOD("lump of royal jelly", 0, 0, 1, 200),
+
+ FOOD("dead human", 0, 4, 40, 400),
+ FOOD("dead giant ant", 0, 1, 3, 30),
+ FOOD("dead giant bat", 0, 1, 3, 30),
+ FOOD("dead centaur", 0, 5, 50, 500),
+ FOOD("dead dragon", 0, 15, 150, 1500),
+ FOOD("dead floating eye", 0, 1, 1, 10),
+ FOOD("dead freezing sphere", 0, 1, 1, 10),
+ FOOD("dead gnome", 0, 1, 10, 100),
+ FOOD("dead hobgoblin", 0, 2, 20, 200),
+ FOOD("dead stalker", 0, 4, 40, 400),
+ FOOD("dead jackal", 0, 1, 10, 100),
+ FOOD("dead kobold", 0, 1, 10, 100),
+ FOOD("dead leprechaun", 0, 4, 40, 400),
+ FOOD("dead mimic", 0, 4, 40, 400),
+ FOOD("dead nymph", 0, 4, 40, 400),
+ FOOD("dead orc", 0, 2, 20, 200),
+ FOOD("dead purple worm", 0, 7, 70, 700),
+ FOOD("dead quasit", 0, 2, 20, 200),
+ FOOD("dead rust monster", 0, 5, 50, 500),
+ FOOD("dead snake", 0, 1, 10, 100),
+ FOOD("dead troll", 0, 4, 40, 400),
+ FOOD("dead umber hulk", 0, 5, 50, 500),
+ FOOD("dead vampire", 0, 4, 40, 400),
+ FOOD("dead wraith", 0, 1, 1, 10),
+ FOOD("dead xorn", 0, 7, 70, 700),
+ FOOD("dead yeti", 0, 7, 70, 700),
+ FOOD("dead zombie", 0, 1, 3, 30),
+ FOOD("dead acid blob", 0, 1, 3, 30),
+ FOOD("dead giant beetle", 0, 1, 1, 10),
+ FOOD("dead cockatrice", 0, 1, 3, 30),
+ FOOD("dead dog", 0, 2, 20, 200),
+ FOOD("dead ettin", 0, 1, 3, 30),
+ FOOD("dead fog cloud", 0, 1, 1, 10),
+ FOOD("dead gelatinous cube", 0, 1, 10, 100),
+ FOOD("dead homunculus", 0, 2, 20, 200),
+ FOOD("dead imp", 0, 1, 1, 10),
+ FOOD("dead jaguar", 0, 3, 30, 300),
+ FOOD("dead killer bee", 0, 1, 1, 10),
+ FOOD("dead leocrotta", 0, 5, 50, 500),
+ FOOD("dead minotaur", 0, 7, 70, 700),
+ FOOD("dead nurse", 0, 4, 40, 400),
+ FOOD("dead owlbear", 0, 7, 70, 700),
+ FOOD("dead piercer", 0, 2, 20, 200),
+ FOOD("dead quivering blob", 0, 1, 10, 100),
+ FOOD("dead giant rat", 0, 1, 3, 30),
+ FOOD("dead giant scorpion", 0, 1, 10, 100),
+ FOOD("dead tengu", 0, 3, 30, 300),
+ FOOD("dead unicorn", 0, 3, 30, 300),
+ FOOD("dead violet fungi", 0, 1, 10, 100),
+ FOOD("dead long worm", 0, 5, 50, 500),
+/* %% wt of long worm should be proportional to its length */
+ FOOD("dead xan", 0, 3, 30, 300),
+ FOOD("dead yellow light", 0, 1, 1, 10),
+ FOOD("dead zruty", 0, 6, 60, 600),
+
+/* weapons ... - ROCK come several at a time */
+/* weapons ... - (ROCK-1) are shot using idem+(BOW-ARROW) */
+/* weapons AXE, SWORD, THSWORD are good for worm-cutting */
+/* weapons (PICK-)AXE, DAGGER, CRYSKNIFE are good for tin-opening */
+#define WEAPON(name,prob,wt,ldam,sdam) { name, NULL, NULL, 1, 0 /*%%*/,\
+ WEAPON_SYM, prob, 0, wt, ldam, sdam, 0 }
+
+ WEAPON("arrow", 7, 0, 6, 6),
+ WEAPON("sling bullet", 7, 0, 4, 6),
+ WEAPON("crossbow bolt", 7, 0, 4, 6),
+ WEAPON("dart", 7, 0, 3, 2),
+ WEAPON("rock", 6, 1, 3, 3),
+ WEAPON("boomerang", 2, 3, 9, 9),
+ WEAPON("mace", 9, 3, 6, 7),
+ WEAPON("axe", 6, 3, 6, 4),
+ WEAPON("flail", 6, 3, 6, 5),
+ WEAPON("long sword", 8, 3, 8, 12),
+ WEAPON("two handed sword", 6, 4, 12, 6),
+ WEAPON("dagger", 6, 3, 4, 3),
+ WEAPON("worm tooth", 0, 4, 2, 2),
+ WEAPON("crysknife", 0, 3, 10, 10),
+ WEAPON("spear", 6, 3, 6, 8),
+ WEAPON("bow", 6, 3, 4, 6),
+ WEAPON("sling", 5, 3, 6, 6),
+ WEAPON("crossbow", 6, 3, 4, 6),
+
+ { "whistle", "whistle", NULL, 0, 0,
+ TOOL_SYM, 90, 0, 2, 0, 0, 0 },
+ { "magic whistle", "whistle", NULL, 0, 0,
+ TOOL_SYM, 10, 0, 2, 0, 0, 0 },
+ { "expensive camera", NULL, NULL, 1, 1,
+ TOOL_SYM, 0, 0, 3, 0, 0, 0 },
+ { "ice box", "large box", NULL, 0, 0,
+ TOOL_SYM, 0, 0, 40, 0, 0, 0 },
+ { "pick-axe", NULL, NULL, 1, 1,
+ TOOL_SYM, 0, 0, 5, 6, 3, 0 },
+ { "can opener", NULL, NULL, 1, 1,
+ TOOL_SYM, 0, 0, 1, 0, 0, 0 },
+ { "heavy iron ball", NULL, NULL, 1, 0,
+ BALL_SYM, 100, 0, 20, 0, 0, 0 },
+ { "iron chain", NULL, NULL, 1, 0,
+ CHAIN_SYM, 100, 0, 20, 0, 0, 0 },
+ { "enormous rock", NULL, NULL, 1, 0,
+ ROCK_SYM, 100, 0, 200 /* > MAX_CARR_CAP */, 0, 0, 0 },
+
+#define ARMOR(name,prob,delay,ac,can) { name, NULL, NULL, 1, 0,\
+ ARMOR_SYM, prob, delay, 8, ac, can, 0 }
+ ARMOR("helmet", 3, 1, 9, 0),
+ ARMOR("plate mail", 5, 5, 3, 2),
+ ARMOR("splint mail", 8, 5, 4, 1),
+ ARMOR("banded mail", 10, 5, 4, 0),
+ ARMOR("chain mail", 10, 5, 5, 1),
+ ARMOR("scale mail", 10, 5, 6, 0),
+ ARMOR("ring mail", 15, 5, 7, 0),
+ /* the armors below do not rust */
+ ARMOR("studded leather armor", 13, 3, 7, 1),
+ ARMOR("leather armor", 17, 3, 8, 0),
+ ARMOR("elven cloak", 5, 0, 9, 3),
+ ARMOR("shield", 3, 0, 9, 0),
+ ARMOR("pair of gloves", 1, 1, 9, 0),
+
+#define POTION(name,color) { name, color, NULL, 0, 1,\
+ POTION_SYM, 0, 0, 2, 0, 0, 0 }
+
+ POTION("restore strength", "orange"),
+ POTION("booze", "bubbly"),
+ POTION("invisibility", "glowing"),
+ POTION("fruit juice", "smoky"),
+ POTION("healing", "pink"),
+ POTION("paralysis", "puce"),
+ POTION("monster detection", "purple"),
+ POTION("object detection", "yellow"),
+ POTION("sickness", "white"),
+ POTION("confusion", "swirly"),
+ POTION("gain strength", "purple-red"),
+ POTION("speed", "ruby"),
+ POTION("blindness", "dark green"),
+ POTION("gain level", "emerald"),
+ POTION("extra healing", "sky blue"),
+ POTION("levitation", "brown"),
+ POTION(NULL, "brilliant blue"),
+ POTION(NULL, "clear"),
+ POTION(NULL, "magenta"),
+ POTION(NULL, "ebony"),
+
+#define SCROLL(name,text,prob) { name, text, NULL, 0, 1,\
+ SCROLL_SYM, prob, 0, 3, 0, 0, 0 }
+ SCROLL("mail", "KIRJE", 0),
+ SCROLL("enchant armor", "ZELGO MER", 6),
+ SCROLL("destroy armor", "JUYED AWK YACC", 5),
+ SCROLL("confuse monster", "NR 9", 5),
+ SCROLL("scare monster", "XIXAXA XOXAXA XUXAXA", 4),
+ SCROLL("blank paper", "READ ME", 3),
+ SCROLL("remove curse", "PRATYAVAYAH", 6),
+ SCROLL("enchant weapon", "DAIYEN FOOELS", 6),
+ SCROLL("damage weapon", "HACKEM MUCHE", 5),
+ SCROLL("create monster", "LEP GEX VEN ZEA", 5),
+ SCROLL("taming", "PRIRUTSENIE", 1),
+ SCROLL("genocide", "ELBIB YLOH",2),
+ SCROLL("light", "VERR YED HORRE", 10),
+ SCROLL("teleportation", "VENZAR BORGAVVE", 5),
+ SCROLL("gold detection", "THARR", 4),
+ SCROLL("food detection", "YUM YUM", 1),
+ SCROLL("identify", "KERNOD WEL", 18),
+ SCROLL("magic mapping", "ELAM EBOW", 5),
+ SCROLL("amnesia", "DUAM XNAHT", 3),
+ SCROLL("fire", "ANDOVA BEGARIN", 5),
+ SCROLL("punishment", "VE FORBRYDERNE", 1),
+ SCROLL(NULL, "VELOX NEB", 0),
+ SCROLL(NULL, "FOOBIE BLETCH", 0),
+ SCROLL(NULL, "TEMOV", 0),
+ SCROLL(NULL, "GARVEN DEH", 0),
+
+#define WAND(name,metal,prob,flags) { name, metal, NULL, 0, 0,\
+ WAND_SYM, prob, 0, 3, flags, 0, 0 }
+
+ WAND("light", "iridium", 10, NODIR),
+ WAND("secret door detection", "tin", 5, NODIR),
+ WAND("create monster", "platinum", 5, NODIR),
+ WAND("wishing", "glass", 1, NODIR),
+ WAND("striking", "zinc", 9, IMMEDIATE),
+ WAND("slow monster", "balsa", 5, IMMEDIATE),
+ WAND("speed monster", "copper", 5, IMMEDIATE),
+ WAND("undead turning", "silver", 5, IMMEDIATE),
+ WAND("polymorph", "brass", 5, IMMEDIATE),
+ WAND("cancellation", "maple", 5, IMMEDIATE),
+ WAND("teleportation", "pine", 5, IMMEDIATE),
+ WAND("make invisible", "marble", 9, IMMEDIATE),
+ WAND("digging", "iron", 5, RAY),
+ WAND("magic missile", "aluminium", 10, RAY),
+ WAND("fire", "steel", 5, RAY),
+ WAND("sleep", "curved", 5, RAY),
+ WAND("cold", "short", 5, RAY),
+ WAND("death", "long", 1, RAY),
+ WAND(NULL, "oak", 0, 0),
+ WAND(NULL, "ebony", 0, 0),
+ WAND(NULL, "runed", 0, 0),
+
+#define RING(name,stone,spec) { name, stone, NULL, 0, 0,\
+ RING_SYM, 0, 0, 1, spec, 0, 0 }
+
+ RING("adornment", "engagement", 0),
+ RING("teleportation", "wooden", 0),
+ RING("regeneration", "black onyx", 0),
+ RING("searching", "topaz", 0),
+ RING("see invisible", "pearl", 0),
+ RING("stealth", "sapphire", 0),
+ RING("levitation", "moonstone", 0),
+ RING("poison resistance", "agate", 0),
+ RING("aggravate monster", "tiger eye", 0),
+ RING("hunger", "shining", 0),
+ RING("fire resistance", "gold", 0),
+ RING("cold resistance", "copper", 0),
+ RING("protection from shape changers", "diamond", 0),
+ RING("conflict", "jade", 0),
+ RING("gain strength", "ruby", SPEC),
+ RING("increase damage", "silver", SPEC),
+ RING("protection", "granite", SPEC),
+ RING("warning", "wire", 0),
+ RING("teleport control", "iron", 0),
+ RING(NULL, "ivory", 0),
+ RING(NULL, "blackened", 0),
+
+/* gems ************************************************************/
+#define GEM(name,color,prob,gval) { name, color, NULL, 0, 1,\
+ GEM_SYM, prob, 0, 1, 0, 0, gval }
+ GEM("diamond", "blue", 1, 4000),
+ GEM("ruby", "red", 1, 3500),
+ GEM("sapphire", "blue", 1, 3000),
+ GEM("emerald", "green", 1, 2500),
+ GEM("turquoise", "green", 1, 2000),
+ GEM("aquamarine", "blue", 1, 1500),
+ GEM("tourmaline", "green", 1, 1000),
+ GEM("topaz", "yellow", 1, 900),
+ GEM("opal", "yellow", 1, 800),
+ GEM("garnet", "dark", 1, 700),
+ GEM("amethyst", "violet", 2, 650),
+ GEM("agate", "green", 2, 600),
+ GEM("onyx", "white", 2, 550),
+ GEM("jasper", "yellowish brown", 2, 500),
+ GEM("jade", "green", 2, 450),
+ GEM("worthless piece of blue glass", "blue", 20, 0),
+ GEM("worthless piece of red glass", "red", 20, 0),
+ GEM("worthless piece of yellow glass", "yellow", 20, 0),
+ GEM("worthless piece of green glass", "green", 20, 0),
+ { NULL, NULL, NULL, 0, 0, ILLOBJ_SYM, 0, 0, 0, 0, 0, 0 }
+};
+
+char obj_symbols[] = {
+ ILLOBJ_SYM, AMULET_SYM, FOOD_SYM, WEAPON_SYM, TOOL_SYM,
+ BALL_SYM, CHAIN_SYM, ROCK_SYM, ARMOR_SYM, POTION_SYM, SCROLL_SYM,
+ WAND_SYM, RING_SYM, GEM_SYM, 0 };
+int bases[sizeof(obj_symbols)];
diff --git a/games/hack/def.permonst.h b/games/hack/def.permonst.h
new file mode 100644
index 000000000000..b19efc6ae4bc
--- /dev/null
+++ b/games/hack/def.permonst.h
@@ -0,0 +1,25 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.permonst.h - version 1.0.2 */
+
+struct permonst {
+ char *mname,mlet;
+ schar mlevel,mmove,ac,damn,damd;
+ unsigned pxlth;
+};
+
+extern struct permonst mons[];
+#define PM_ACID_BLOB &mons[7]
+#define PM_ZOMBIE &mons[13]
+#define PM_PIERCER &mons[17]
+#define PM_KILLER_BEE &mons[26]
+#define PM_WRAITH &mons[33]
+#define PM_MIMIC &mons[37]
+#define PM_VAMPIRE &mons[43]
+#define PM_CHAMELEON &mons[47]
+#define PM_DEMON &mons[54]
+#define PM_MINOTAUR &mons[55] /* last in mons array */
+#define PM_SHK &mons[56] /* very last */
+#define PM_GHOST &pm_ghost
+#define PM_EEL &pm_eel
+#define PM_WIZARD &pm_wizard
+#define CMNUM 55 /* number of common monsters */
diff --git a/games/hack/def.rm.h b/games/hack/def.rm.h
new file mode 100644
index 000000000000..f84921ca3365
--- /dev/null
+++ b/games/hack/def.rm.h
@@ -0,0 +1,52 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.rm.h - version 1.0.2 */
+
+/* Level location types */
+#define HWALL 1
+#define VWALL 2
+#define SDOOR 3
+#define SCORR 4
+#define LDOOR 5
+#define POOL 6 /* not yet fully implemented */
+ /* this should in fact be a bit like lit */
+#define DOOR 7
+#define CORR 8
+#define ROOM 9
+#define STAIRS 10
+
+/*
+ * Avoid using the level types in inequalities:
+ * these types are subject to change.
+ * Instead, use one of the macros below.
+ */
+#define IS_WALL(typ) ((typ) <= VWALL)
+#define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */
+#define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */
+#define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM or STAIRS */
+#define ZAP_POS(typ) ((typ) > DOOR)
+
+/*
+ * A few of the associated symbols are not hardwired.
+ */
+#ifdef QUEST
+#define CORR_SYM ':'
+#else
+#define CORR_SYM '#'
+#endif QUEST
+#define POOL_SYM '}'
+
+#define ERRCHAR '{'
+
+/*
+ * The structure describing a coordinate position.
+ * Before adding fields, remember that this will significantly affect
+ * the size of temporary files and save files.
+ */
+struct rm {
+ char scrsym;
+ unsigned typ:5;
+ unsigned new:1;
+ unsigned seen:1;
+ unsigned lit:1;
+};
+extern struct rm levl[COLNO][ROWNO];
diff --git a/games/hack/def.trap.h b/games/hack/def.trap.h
new file mode 100644
index 000000000000..26946add76bb
--- /dev/null
+++ b/games/hack/def.trap.h
@@ -0,0 +1,27 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.trap.h - version 1.0.2 */
+
+struct trap {
+ struct trap *ntrap;
+ xchar tx,ty;
+ unsigned ttyp:5;
+ unsigned tseen:1;
+ unsigned once:1;
+};
+
+extern struct trap *ftrap;
+struct trap *t_at();
+#define newtrap() (struct trap *) alloc(sizeof(struct trap))
+
+/* various kinds of traps */
+#define BEAR_TRAP 0
+#define ARROW_TRAP 1
+#define DART_TRAP 2
+#define TRAPDOOR 3
+#define TELEP_TRAP 4
+#define PIT 5
+#define SLP_GAS_TRAP 6
+#define PIERC 7
+#define MIMIC 8 /* used only in mklev.c */
+#define TRAPNUM 9 /* if not less than 32, change sizeof(ttyp) */
+ /* see also mtrapseen (bit map) */
diff --git a/games/hack/def.wseg.h b/games/hack/def.wseg.h
new file mode 100644
index 000000000000..0a2af7791d03
--- /dev/null
+++ b/games/hack/def.wseg.h
@@ -0,0 +1,13 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.wseg.h - version 1.0.2 */
+
+#ifndef NOWORM
+/* worm structure */
+struct wseg {
+ struct wseg *nseg;
+ xchar wx,wy;
+ unsigned wdispl:1;
+};
+
+#define newseg() (struct wseg *) alloc(sizeof(struct wseg))
+#endif NOWORM
diff --git a/games/hack/hack.6 b/games/hack/hack.6
new file mode 100644
index 000000000000..c4c46c30c2e0
--- /dev/null
+++ b/games/hack/hack.6
@@ -0,0 +1,155 @@
+.TH HACK 6 "31 March 1985"
+.UC 4
+.SH NAME
+hack \- Exploring The Dungeons of Doom
+.SH SYNOPSIS
+.B /usr/games/hack
+[
+.B \-d
+.I directory
+]
+[
+.B \-n
+]
+[
+.B \-u
+.I playername
+]
+.br
+.B /usr/games/hack
+[
+.B \-d
+.I directory
+]
+.B \-s
+[
+.B \-X
+]
+[
+.I playernames
+]
+.SH DESCRIPTION
+.PP
+.I Hack
+is a display oriented dungeons & dragons - like game.
+Both display and command structure resemble rogue.
+(For a game with the same structure but entirely different display -
+a real cave instead of dull rectangles - try Quest.)
+.PP
+To get started you really only need to know two commands. The command
+.B ?
+will give you a list of the available commands and the command
+.B /
+will identify the things you see on the screen.
+.PP
+To win the game (as opposed to merely playing to beat other people high
+scores) you must locate the Amulet of Yendor which is somewhere below
+the 20th level of the dungeon and get it out. Nobody has achieved this
+yet and if somebody does, he will probably go down in history as a hero
+among heros.
+.PP
+When the game ends, either by your death, when you quit, or if you escape
+from the caves,
+.I hack
+will give you (a fragment of) the list of top scorers. The scoring
+is based on many aspects of your behaviour but a rough estimate is
+obtained by taking the amount of gold you've found in the cave plus four
+times your (real) experience. Precious stones may be worth a lot of gold
+when brought to the exit.
+There is a 10% penalty for getting yourself killed.
+.PP
+The administration of the game is kept in the directory specified with the
+.B \-d
+option, or, if no such option is given, in the directory specified by
+the environment variable HACKDIR, or, if no such variable exists, in
+the current directory. This same directory contains several auxiliary
+files such as lockfiles and the list of topscorers and a subdirectory
+.I save
+where games are saved.
+The game administrator may however choose to install hack with a fixed
+playing ground, usually /usr/games/lib/hackdir.
+.PP
+The
+.B \-n
+option suppresses printing of the news.
+.PP
+The
+.B \-u
+.I playername
+option supplies the answer to the question "Who are you?".
+When
+.I playername
+has as suffix one of
+.B \-T \-S \-K \-F \-C \-W
+then this supplies the answer to the question "What kind of character ... ?".
+.PP
+The
+.B \-s
+option will print out the list of your scores. It may be followed by arguments
+.B \-X
+where X is one of the letters C, F, K, S, T, W to print the scores of
+Cavemen, Fighters, Knights, Speleologists, Tourists or Wizards.
+It may also be followed by one or more player names to print the scores of the
+players mentioned.
+.SH AUTHORS
+Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the
+original hack, very much like rogue (but full of bugs).
+.br
+Andries Brouwer continuously deformed their sources into the current
+version - in fact an entirely different game.
+.SH FILES
+.DT
+.ta \w'data, rumors\ \ \ 'u
+hack The hack program.
+.br
+data, rumors Data files used by hack.
+.br
+help, hh Help data files.
+.br
+record The list of topscorers.
+.br
+save A subdirectory containing the saved
+.br
+ games.
+.br
+bones_dd Descriptions of the ghost and
+.br
+ belongings of a deceased adventurer.
+.br
+xlock.dd Description of a dungeon level.
+.br
+safelock Lock file for xlock.
+.br
+record_lock Lock file for record.
+.SH ENVIRONMENT
+.DT
+.ta \w'HACKPAGER, PAGER\ \ \ 'u
+USER or LOGNAME Your login name.
+.br
+HOME Your home directory.
+.br
+SHELL Your shell.
+.br
+TERM The type of your terminal.
+.br
+HACKPAGER, PAGER Pager used instead of default pager.
+.br
+MAIL Mailbox file.
+.br
+MAILREADER Reader used instead of default
+.br
+ (probably /bin/mail or /usr/ucb/mail).
+.br
+HACKDIR Playground.
+.br
+HACKOPTIONS String predefining several hack options
+.br
+ (see help file).
+.br
+
+Several other environment variables are used in debugging (wizard) mode,
+like GENOCIDED, INVENT, MAGIC and SHOPTYPE.
+.SH BUGS
+.PP
+Probably infinite.
+Mail complaints to mcvax!aeb .
diff --git a/games/hack/hack.Decl.c b/games/hack/hack.Decl.c
new file mode 100644
index 000000000000..b2855ac93631
--- /dev/null
+++ b/games/hack/hack.Decl.c
@@ -0,0 +1,43 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.Decl.c - version 1.0.3 */
+
+#include "hack.h"
+char nul[40]; /* contains zeros */
+char plname[PL_NSIZ]; /* player name */
+char lock[PL_NSIZ+4] = "1lock"; /* long enough for login name .99 */
+
+boolean in_mklev, restoring;
+
+struct rm levl[COLNO][ROWNO]; /* level map */
+#ifndef QUEST
+#include "def.mkroom.h"
+struct mkroom rooms[MAXNROFROOMS+1];
+coord doors[DOORMAX];
+#endif QUEST
+struct monst *fmon = 0;
+struct trap *ftrap = 0;
+struct gold *fgold = 0;
+struct obj *fobj = 0, *fcobj = 0, *invent = 0, *uwep = 0, *uarm = 0,
+ *uarm2 = 0, *uarmh = 0, *uarms = 0, *uarmg = 0, *uright = 0,
+ *uleft = 0, *uchain = 0, *uball = 0;
+struct flag flags;
+struct you u;
+struct monst youmonst; /* dummy; used as return value for boomhit */
+
+xchar dlevel = 1;
+xchar xupstair, yupstair, xdnstair, ydnstair;
+char *save_cm = 0, *killer, *nomovemsg;
+
+long moves = 1;
+long wailmsg = 0;
+
+int multi = 0;
+char genocided[60];
+char fut_geno[60];
+
+xchar curx,cury;
+xchar seelx, seehx, seely, seehy; /* corners of lit room */
+
+coord bhitpos;
+
+char quitchars[] = " \r\n\033";
diff --git a/games/hack/hack.apply.c b/games/hack/hack.apply.c
new file mode 100644
index 000000000000..ebdf82adce54
--- /dev/null
+++ b/games/hack/hack.apply.c
@@ -0,0 +1,437 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.apply.c - version 1.0.3 */
+
+#include "hack.h"
+#include "def.edog.h"
+#include "def.mkroom.h"
+extern struct monst *bchit();
+extern struct obj *addinv();
+extern struct trap *maketrap();
+extern int (*occupation)();
+extern char *occtxt;
+extern char quitchars[];
+extern char pl_character[];
+
+static void use_camera(), use_ice_box(), use_whistle(), use_magic_whistle();
+static int use_pick_axe();
+
+doapply() {
+ register struct obj *obj;
+ register int res = 1;
+
+ obj = getobj("(", "use or apply");
+ if(!obj) return(0);
+
+ switch(obj->otyp){
+ case EXPENSIVE_CAMERA:
+ use_camera(obj); break;
+ case ICE_BOX:
+ use_ice_box(obj); break;
+ case PICK_AXE:
+ res = use_pick_axe(obj);
+ break;
+
+ case MAGIC_WHISTLE:
+ if(pl_character[0] == 'W' || u.ulevel > 9) {
+ use_magic_whistle(obj);
+ break;
+ }
+ /* fall into next case */
+ case WHISTLE:
+ use_whistle(obj);
+ break;
+
+ case CAN_OPENER:
+ if(!carrying(TIN)) {
+ pline("You have no can to open.");
+ goto xit;
+ }
+ pline("You cannot open a tin without eating its contents.");
+ pline("In order to eat, use the 'e' command.");
+ if(obj != uwep)
+ pline("Opening the tin will be much easier if you wield the can-opener.");
+ goto xit;
+
+ default:
+ pline("Sorry, I don't know how to use that.");
+ xit:
+ nomul(0);
+ return(0);
+ }
+ nomul(0);
+ return(res);
+}
+
+/* ARGSUSED */
+static void
+use_camera(obj) /* register */ struct obj *obj; {
+register struct monst *mtmp;
+ if(!getdir(1)){ /* ask: in what direction? */
+ flags.move = multi = 0;
+ return;
+ }
+ if(u.uswallow) {
+ pline("You take a picture of %s's stomach.", monnam(u.ustuck));
+ return;
+ }
+ if(u.dz) {
+ pline("You take a picture of the %s.",
+ (u.dz > 0) ? "floor" : "ceiling");
+ return;
+ }
+ if(mtmp = bchit(u.dx, u.dy, COLNO, '!')) {
+ if(mtmp->msleep){
+ mtmp->msleep = 0;
+ pline("The flash awakens %s.", monnam(mtmp)); /* a3 */
+ } else
+ if(mtmp->data->mlet != 'y')
+ if(mtmp->mcansee || mtmp->mblinded){
+ register int tmp = dist(mtmp->mx,mtmp->my);
+ register int tmp2;
+ if(cansee(mtmp->mx,mtmp->my))
+ pline("%s is blinded by the flash!", Monnam(mtmp));
+ setmangry(mtmp);
+ if(tmp < 9 && !mtmp->isshk && rn2(4)) {
+ mtmp->mflee = 1;
+ if(rn2(4)) mtmp->mfleetim = rnd(100);
+ }
+ if(tmp < 3) mtmp->mcansee = mtmp->mblinded = 0;
+ else {
+ tmp2 = mtmp->mblinded;
+ tmp2 += rnd(1 + 50/tmp);
+ if(tmp2 > 127) tmp2 = 127;
+ mtmp->mblinded = tmp2;
+ mtmp->mcansee = 0;
+ }
+ }
+ }
+}
+
+static
+struct obj *current_ice_box; /* a local variable of use_ice_box, to be
+ used by its local procedures in/ck_ice_box */
+static
+in_ice_box(obj) register struct obj *obj; {
+ if(obj == current_ice_box ||
+ (Punished && (obj == uball || obj == uchain))){
+ pline("You must be kidding.");
+ return(0);
+ }
+ if(obj->owornmask & (W_ARMOR | W_RING)) {
+ pline("You cannot refrigerate something you are wearing.");
+ return(0);
+ }
+ if(obj->owt + current_ice_box->owt > 70) {
+ pline("It won't fit.");
+ return(1); /* be careful! */
+ }
+ if(obj == uwep) {
+ if(uwep->cursed) {
+ pline("Your weapon is welded to your hand!");
+ return(0);
+ }
+ setuwep((struct obj *) 0);
+ }
+ current_ice_box->owt += obj->owt;
+ freeinv(obj);
+ obj->o_cnt_id = current_ice_box->o_id;
+ obj->nobj = fcobj;
+ fcobj = obj;
+ obj->age = moves - obj->age; /* actual age */
+ return(1);
+}
+
+static
+ck_ice_box(obj) register struct obj *obj; {
+ return(obj->o_cnt_id == current_ice_box->o_id);
+}
+
+static
+out_ice_box(obj) register struct obj *obj; {
+register struct obj *otmp;
+ if(obj == fcobj) fcobj = fcobj->nobj;
+ else {
+ for(otmp = fcobj; otmp->nobj != obj; otmp = otmp->nobj)
+ if(!otmp->nobj) panic("out_ice_box");
+ otmp->nobj = obj->nobj;
+ }
+ current_ice_box->owt -= obj->owt;
+ obj->age = moves - obj->age; /* simulated point of time */
+ (void) addinv(obj);
+}
+
+static void
+use_ice_box(obj) register struct obj *obj; {
+register int cnt = 0;
+register struct obj *otmp;
+ current_ice_box = obj; /* for use by in/out_ice_box */
+ for(otmp = fcobj; otmp; otmp = otmp->nobj)
+ if(otmp->o_cnt_id == obj->o_id)
+ cnt++;
+ if(!cnt) pline("Your ice-box is empty.");
+ else {
+ pline("Do you want to take something out of the ice-box? [yn] ");
+ if(readchar() == 'y')
+ if(askchain(fcobj, (char *) 0, 0, out_ice_box, ck_ice_box, 0))
+ return;
+ pline("That was all. Do you wish to put something in? [yn] ");
+ if(readchar() != 'y') return;
+ }
+ /* call getobj: 0: allow cnt; #: allow all types; %: expect food */
+ otmp = getobj("0#%", "put in");
+ if(!otmp || !in_ice_box(otmp))
+ flags.move = multi = 0;
+}
+
+static
+struct monst *
+bchit(ddx,ddy,range,sym) register int ddx,ddy,range; char sym; {
+ register struct monst *mtmp = (struct monst *) 0;
+ register int bchx = u.ux, bchy = u.uy;
+
+ if(sym) Tmp_at(-1, sym); /* open call */
+ while(range--) {
+ bchx += ddx;
+ bchy += ddy;
+ if(mtmp = m_at(bchx,bchy))
+ break;
+ if(!ZAP_POS(levl[bchx][bchy].typ)) {
+ bchx -= ddx;
+ bchy -= ddy;
+ break;
+ }
+ if(sym) Tmp_at(bchx, bchy);
+ }
+ if(sym) Tmp_at(-1, -1);
+ return(mtmp);
+}
+
+/* ARGSUSED */
+static void
+use_whistle(obj) struct obj *obj; {
+register struct monst *mtmp = fmon;
+ pline("You produce a high whistling sound.");
+ while(mtmp) {
+ if(dist(mtmp->mx,mtmp->my) < u.ulevel*20) {
+ if(mtmp->msleep)
+ mtmp->msleep = 0;
+ if(mtmp->mtame)
+ EDOG(mtmp)->whistletime = moves;
+ }
+ mtmp = mtmp->nmon;
+ }
+}
+
+/* ARGSUSED */
+static void
+use_magic_whistle(obj) struct obj *obj; {
+register struct monst *mtmp = fmon;
+ pline("You produce a strange whistling sound.");
+ while(mtmp) {
+ if(mtmp->mtame) mnexto(mtmp);
+ mtmp = mtmp->nmon;
+ }
+}
+
+static int dig_effort; /* effort expended on current pos */
+static uchar dig_level;
+static coord dig_pos;
+static boolean dig_down;
+
+static
+dig() {
+ register struct rm *lev;
+ register dpx = dig_pos.x, dpy = dig_pos.y;
+
+ /* perhaps a nymph stole his pick-axe while he was busy digging */
+ /* or perhaps he teleported away */
+ if(u.uswallow || !uwep || uwep->otyp != PICK_AXE ||
+ dig_level != dlevel ||
+ ((dig_down && (dpx != u.ux || dpy != u.uy)) ||
+ (!dig_down && dist(dpx,dpy) > 2)))
+ return(0);
+
+ dig_effort += 10 + abon() + uwep->spe + rn2(5);
+ if(dig_down) {
+ if(!xdnstair) {
+ pline("The floor here seems too hard to dig in.");
+ return(0);
+ }
+ if(dig_effort > 250) {
+ dighole();
+ return(0); /* done with digging */
+ }
+ if(dig_effort > 50) {
+ register struct trap *ttmp = t_at(dpx,dpy);
+
+ if(!ttmp) {
+ ttmp = maketrap(dpx,dpy,PIT);
+ ttmp->tseen = 1;
+ pline("You have dug a pit.");
+ u.utrap = rn1(4,2);
+ u.utraptype = TT_PIT;
+ return(0);
+ }
+ }
+ } else
+ if(dig_effort > 100) {
+ register char *digtxt;
+ register struct obj *obj;
+
+ lev = &levl[dpx][dpy];
+ if(obj = sobj_at(ENORMOUS_ROCK, dpx, dpy)) {
+ fracture_rock(obj);
+ digtxt = "The rock falls apart.";
+ } else if(!lev->typ || lev->typ == SCORR) {
+ lev->typ = CORR;
+ digtxt = "You succeeded in cutting away some rock.";
+ } else if(lev->typ == HWALL || lev->typ == VWALL
+ || lev->typ == SDOOR) {
+ lev->typ = xdnstair ? DOOR : ROOM;
+ digtxt = "You just made an opening in the wall.";
+ } else
+ digtxt = "Now what exactly was it that you were digging in?";
+ mnewsym(dpx, dpy);
+ prl(dpx, dpy);
+ pline(digtxt); /* after mnewsym & prl */
+ return(0);
+ } else {
+ if(IS_WALL(levl[dpx][dpy].typ)) {
+ register int rno = inroom(dpx,dpy);
+
+ if(rno >= 0 && rooms[rno].rtype >= 8) {
+ pline("This wall seems too hard to dig into.");
+ return(0);
+ }
+ }
+ pline("You hit the rock with all your might.");
+ }
+ return(1);
+}
+
+/* When will hole be finished? Very rough indication used by shopkeeper. */
+holetime() {
+ return( (occupation == dig) ? (250 - dig_effort)/20 : -1);
+}
+
+dighole()
+{
+ register struct trap *ttmp = t_at(u.ux, u.uy);
+
+ if(!xdnstair) {
+ pline("The floor here seems too hard to dig in.");
+ } else {
+ if(ttmp)
+ ttmp->ttyp = TRAPDOOR;
+ else
+ ttmp = maketrap(u.ux, u.uy, TRAPDOOR);
+ ttmp->tseen = 1;
+ pline("You've made a hole in the floor.");
+ if(!u.ustuck) {
+ if(inshop())
+ shopdig(1);
+ pline("You fall through ...");
+ if(u.utraptype == TT_PIT) {
+ u.utrap = 0;
+ u.utraptype = 0;
+ }
+ goto_level(dlevel+1, FALSE);
+ }
+ }
+}
+
+static
+use_pick_axe(obj)
+struct obj *obj;
+{
+ char dirsyms[12];
+ extern char sdir[];
+ register char *dsp = dirsyms, *sdp = sdir;
+ register struct monst *mtmp;
+ register struct rm *lev;
+ register int rx, ry, res = 0;
+
+ if(obj != uwep) {
+ if(uwep && uwep->cursed) {
+ /* Andreas Bormann - ihnp4!decvax!mcvax!unido!ab */
+ pline("Since your weapon is welded to your hand,");
+ pline("you cannot use that pick-axe.");
+ return(0);
+ }
+ pline("You now wield %s.", doname(obj));
+ setuwep(obj);
+ res = 1;
+ }
+ while(*sdp) {
+ (void) movecmd(*sdp); /* sets u.dx and u.dy and u.dz */
+ rx = u.ux + u.dx;
+ ry = u.uy + u.dy;
+ if(u.dz > 0 || (u.dz == 0 && isok(rx, ry) &&
+ (IS_ROCK(levl[rx][ry].typ)
+ || sobj_at(ENORMOUS_ROCK, rx, ry))))
+ *dsp++ = *sdp;
+ sdp++;
+ }
+ *dsp = 0;
+ pline("In what direction do you want to dig? [%s] ", dirsyms);
+ if(!getdir(0)) /* no txt */
+ return(res);
+ if(u.uswallow && attack(u.ustuck)) /* return(1) */;
+ else
+ if(u.dz < 0)
+ pline("You cannot reach the ceiling.");
+ else
+ if(u.dz == 0) {
+ if(Confusion)
+ confdir();
+ rx = u.ux + u.dx;
+ ry = u.uy + u.dy;
+ if((mtmp = m_at(rx, ry)) && attack(mtmp))
+ return(1);
+ if(!isok(rx, ry)) {
+ pline("Clash!");
+ return(1);
+ }
+ lev = &levl[rx][ry];
+ if(lev->typ == DOOR)
+ pline("Your %s against the door.",
+ aobjnam(obj, "clang"));
+ else if(!IS_ROCK(lev->typ)
+ && !sobj_at(ENORMOUS_ROCK, rx, ry)) {
+ /* ACCESSIBLE or POOL */
+ pline("You swing your %s through thin air.",
+ aobjnam(obj, (char *) 0));
+ } else {
+ if(dig_pos.x != rx || dig_pos.y != ry
+ || dig_level != dlevel || dig_down) {
+ dig_down = FALSE;
+ dig_pos.x = rx;
+ dig_pos.y = ry;
+ dig_level = dlevel;
+ dig_effort = 0;
+ pline("You start digging.");
+ } else
+ pline("You continue digging.");
+ occupation = dig;
+ occtxt = "digging";
+ }
+ } else if(Levitation) {
+ pline("You cannot reach the floor.");
+ } else {
+ if(dig_pos.x != u.ux || dig_pos.y != u.uy
+ || dig_level != dlevel || !dig_down) {
+ dig_down = TRUE;
+ dig_pos.x = u.ux;
+ dig_pos.y = u.uy;
+ dig_level = dlevel;
+ dig_effort = 0;
+ pline("You start digging in the floor.");
+ if(inshop())
+ shopdig(0);
+ } else
+ pline("You continue digging in the floor.");
+ occupation = dig;
+ occtxt = "digging";
+ }
+ return(1);
+}
diff --git a/games/hack/hack.bones.c b/games/hack/hack.bones.c
new file mode 100644
index 000000000000..d4a05b822212
--- /dev/null
+++ b/games/hack/hack.bones.c
@@ -0,0 +1,95 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.bones.c - version 1.0.3 */
+
+#include "hack.h"
+extern char plname[PL_NSIZ];
+extern long somegold();
+extern struct monst *makemon();
+extern struct permonst pm_ghost;
+
+char bones[] = "bones_xx";
+
+/* save bones and possessions of a deceased adventurer */
+savebones(){
+register fd;
+register struct obj *otmp;
+register struct trap *ttmp;
+register struct monst *mtmp;
+ if(dlevel <= 0 || dlevel > MAXLEVEL) return;
+ if(!rn2(1 + dlevel/2)) return; /* not so many ghosts on low levels */
+ bones[6] = '0' + (dlevel/10);
+ bones[7] = '0' + (dlevel%10);
+ if((fd = open(bones,0)) >= 0){
+ (void) close(fd);
+ return;
+ }
+ /* drop everything; the corpse's possessions are usually cursed */
+ otmp = invent;
+ while(otmp){
+ otmp->ox = u.ux;
+ otmp->oy = u.uy;
+ otmp->age = 0; /* very long ago */
+ otmp->owornmask = 0;
+ if(rn2(5)) otmp->cursed = 1;
+ if(!otmp->nobj){
+ otmp->nobj = fobj;
+ fobj = invent;
+ invent = 0; /* superfluous */
+ break;
+ }
+ otmp = otmp->nobj;
+ }
+ if(!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) return;
+ mtmp->mx = u.ux;
+ mtmp->my = u.uy;
+ mtmp->msleep = 1;
+ (void) strcpy((char *) mtmp->mextra, plname);
+ mkgold(somegold() + d(dlevel,30), u.ux, u.uy);
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon){
+ mtmp->m_id = 0;
+ if(mtmp->mtame) {
+ mtmp->mtame = 0;
+ mtmp->mpeaceful = 0;
+ }
+ mtmp->mlstmv = 0;
+ if(mtmp->mdispl) unpmon(mtmp);
+ }
+ for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
+ ttmp->tseen = 0;
+ for(otmp = fobj; otmp; otmp = otmp->nobj) {
+ otmp->o_id = 0;
+ /* otmp->o_cnt_id = 0; - superfluous */
+ otmp->onamelth = 0;
+ otmp->known = 0;
+ otmp->invlet = 0;
+ if(otmp->olet == AMULET_SYM && !otmp->spe) {
+ otmp->spe = -1; /* no longer the actual amulet */
+ otmp->cursed = 1; /* flag as gotten from a ghost */
+ }
+ }
+ if((fd = creat(bones, FMASK)) < 0) return;
+ savelev(fd,dlevel);
+ (void) close(fd);
+}
+
+getbones(){
+register fd,x,y,ok;
+ if(rn2(3)) return(0); /* only once in three times do we find bones */
+ bones[6] = '0' + dlevel/10;
+ bones[7] = '0' + dlevel%10;
+ if((fd = open(bones, 0)) < 0) return(0);
+ if((ok = uptodate(fd)) != 0){
+ getlev(fd, 0, dlevel);
+ for(x = 0; x < COLNO; x++) for(y = 0; y < ROWNO; y++)
+ levl[x][y].seen = levl[x][y].new = 0;
+ }
+ (void) close(fd);
+#ifdef WIZARD
+ if(!wizard) /* duvel!frans: don't remove bones while debugging */
+#endif WiZARD
+ if(unlink(bones) < 0){
+ pline("Cannot unlink %s .", bones);
+ return(0);
+ }
+ return(ok);
+}
diff --git a/games/hack/hack.c b/games/hack/hack.c
new file mode 100644
index 000000000000..5c8288144ed8
--- /dev/null
+++ b/games/hack/hack.c
@@ -0,0 +1,798 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+
+extern char news0();
+extern char *nomovemsg;
+extern char *exclam();
+extern struct obj *addinv();
+extern boolean hmon();
+
+/* called on movement:
+ 1. when throwing ball+chain far away
+ 2. when teleporting
+ 3. when walking out of a lit room
+ */
+unsee() {
+ register x,y;
+ register struct rm *lev;
+
+/*
+ if(u.udispl){
+ u.udispl = 0;
+ newsym(u.udisx, u.udisy);
+ }
+*/
+#ifndef QUEST
+ if(seehx){
+ seehx = 0;
+ } else
+#endif QUEST
+ for(x = u.ux-1; x < u.ux+2; x++)
+ for(y = u.uy-1; y < u.uy+2; y++) {
+ if(!isok(x, y)) continue;
+ lev = &levl[x][y];
+ if(!lev->lit && lev->scrsym == '.') {
+ lev->scrsym =' ';
+ lev->new = 1;
+ on_scr(x,y);
+ }
+ }
+}
+
+/* called:
+ in hack.eat.c: seeoff(0) - blind after eating rotten food
+ in hack.mon.c: seeoff(0) - blinded by a yellow light
+ in hack.mon.c: seeoff(1) - swallowed
+ in hack.do.c: seeoff(0) - blind after drinking potion
+ in hack.do.c: seeoff(1) - go up or down the stairs
+ in hack.trap.c:seeoff(1) - fall through trapdoor
+ */
+seeoff(mode) /* 1 to redo @, 0 to leave them */
+{ /* 1 means misc movement, 0 means blindness */
+ register x,y;
+ register struct rm *lev;
+
+ if(u.udispl && mode){
+ u.udispl = 0;
+ levl[u.udisx][u.udisy].scrsym = news0(u.udisx,u.udisy);
+ }
+#ifndef QUEST
+ if(seehx) {
+ seehx = 0;
+ } else
+#endif QUEST
+ if(!mode) {
+ for(x = u.ux-1; x < u.ux+2; x++)
+ for(y = u.uy-1; y < u.uy+2; y++) {
+ if(!isok(x, y)) continue;
+ lev = &levl[x][y];
+ if(!lev->lit && lev->scrsym == '.')
+ lev->seen = 0;
+ }
+ }
+}
+
+domove()
+{
+ xchar oldx,oldy;
+ register struct monst *mtmp;
+ register struct rm *tmpr,*ust;
+ struct trap *trap;
+ register struct obj *otmp;
+
+ u_wipe_engr(rnd(5));
+
+ if(inv_weight() > 0){
+ pline("You collapse under your load.");
+ nomul(0);
+ return;
+ }
+ if(u.uswallow) {
+ u.dx = u.dy = 0;
+ u.ux = u.ustuck->mx;
+ u.uy = u.ustuck->my;
+ } else {
+ if(Confusion) {
+ do {
+ confdir();
+ } while(!isok(u.ux+u.dx, u.uy+u.dy) ||
+ IS_ROCK(levl[u.ux+u.dx][u.uy+u.dy].typ));
+ }
+ if(!isok(u.ux+u.dx, u.uy+u.dy)){
+ nomul(0);
+ return;
+ }
+ }
+
+ ust = &levl[u.ux][u.uy];
+ oldx = u.ux;
+ oldy = u.uy;
+ if(!u.uswallow && (trap = t_at(u.ux+u.dx, u.uy+u.dy)) && trap->tseen)
+ nomul(0);
+ if(u.ustuck && !u.uswallow && (u.ux+u.dx != u.ustuck->mx ||
+ u.uy+u.dy != u.ustuck->my)) {
+ if(dist(u.ustuck->mx, u.ustuck->my) > 2){
+ /* perhaps it fled (or was teleported or ... ) */
+ u.ustuck = 0;
+ } else {
+ if(Blind) pline("You cannot escape from it!");
+ else pline("You cannot escape from %s!",
+ monnam(u.ustuck));
+ nomul(0);
+ return;
+ }
+ }
+ if(u.uswallow || (mtmp = m_at(u.ux+u.dx,u.uy+u.dy))) {
+ /* attack monster */
+
+ nomul(0);
+ gethungry();
+ if(multi < 0) return; /* we just fainted */
+
+ /* try to attack; note that it might evade */
+ if(attack(u.uswallow ? u.ustuck : mtmp))
+ return;
+ }
+ /* not attacking an animal, so we try to move */
+ if(u.utrap) {
+ if(u.utraptype == TT_PIT) {
+ pline("You are still in a pit.");
+ u.utrap--;
+ } else {
+ pline("You are caught in a beartrap.");
+ if((u.dx && u.dy) || !rn2(5)) u.utrap--;
+ }
+ return;
+ }
+ tmpr = &levl[u.ux+u.dx][u.uy+u.dy];
+ if(IS_ROCK(tmpr->typ) ||
+ (u.dx && u.dy && (tmpr->typ == DOOR || ust->typ == DOOR))){
+ flags.move = 0;
+ nomul(0);
+ return;
+ }
+ while(otmp = sobj_at(ENORMOUS_ROCK, u.ux+u.dx, u.uy+u.dy)) {
+ register xchar rx = u.ux+2*u.dx, ry = u.uy+2*u.dy;
+ register struct trap *ttmp;
+ nomul(0);
+ if(isok(rx,ry) && !IS_ROCK(levl[rx][ry].typ) &&
+ (levl[rx][ry].typ != DOOR || !(u.dx && u.dy)) &&
+ !sobj_at(ENORMOUS_ROCK, rx, ry)) {
+ if(m_at(rx,ry)) {
+ pline("You hear a monster behind the rock.");
+ pline("Perhaps that's why you cannot move it.");
+ goto cannot_push;
+ }
+ if(ttmp = t_at(rx,ry))
+ switch(ttmp->ttyp) {
+ case PIT:
+ pline("You push the rock into a pit!");
+ deltrap(ttmp);
+ delobj(otmp);
+ pline("It completely fills the pit!");
+ continue;
+ case TELEP_TRAP:
+ pline("You push the rock and suddenly it disappears!");
+ delobj(otmp);
+ continue;
+ }
+ if(levl[rx][ry].typ == POOL) {
+ levl[rx][ry].typ = ROOM;
+ mnewsym(rx,ry);
+ prl(rx,ry);
+ pline("You push the rock into the water.");
+ pline("Now you can cross the water!");
+ delobj(otmp);
+ continue;
+ }
+ otmp->ox = rx;
+ otmp->oy = ry;
+ /* pobj(otmp); */
+ if(cansee(rx,ry)) atl(rx,ry,otmp->olet);
+ if(Invisible) newsym(u.ux+u.dx, u.uy+u.dy);
+
+ { static long lastmovetime;
+ /* note: this var contains garbage initially and
+ after a restore */
+ if(moves > lastmovetime+2 || moves < lastmovetime)
+ pline("With great effort you move the enormous rock.");
+ lastmovetime = moves;
+ }
+ } else {
+ pline("You try to move the enormous rock, but in vain.");
+ cannot_push:
+ if((!invent || inv_weight()+90 <= 0) &&
+ (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][u.uy+u.dy].typ)
+ && IS_ROCK(levl[u.ux+u.dx][u.uy].typ)))){
+ pline("However, you can squeeze yourself into a small opening.");
+ break;
+ } else
+ return;
+ }
+ }
+ if(u.dx && u.dy && IS_ROCK(levl[u.ux][u.uy+u.dy].typ) &&
+ IS_ROCK(levl[u.ux+u.dx][u.uy].typ) &&
+ invent && inv_weight()+40 > 0) {
+ pline("You are carrying too much to get through.");
+ nomul(0);
+ return;
+ }
+ if(Punished &&
+ DIST(u.ux+u.dx, u.uy+u.dy, uchain->ox, uchain->oy) > 2){
+ if(carried(uball)) {
+ movobj(uchain, u.ux, u.uy);
+ goto nodrag;
+ }
+
+ if(DIST(u.ux+u.dx, u.uy+u.dy, uball->ox, uball->oy) < 3){
+ /* leave ball, move chain under/over ball */
+ movobj(uchain, uball->ox, uball->oy);
+ goto nodrag;
+ }
+
+ if(inv_weight() + (int) uball->owt/2 > 0) {
+ pline("You cannot %sdrag the heavy iron ball.",
+ invent ? "carry all that and also " : "");
+ nomul(0);
+ return;
+ }
+
+ movobj(uball, uchain->ox, uchain->oy);
+ unpobj(uball); /* BAH %% */
+ uchain->ox = u.ux;
+ uchain->oy = u.uy;
+ nomul(-2);
+ nomovemsg = "";
+ nodrag: ;
+ }
+ u.ux += u.dx;
+ u.uy += u.dy;
+ if(flags.run) {
+ if(tmpr->typ == DOOR ||
+ (xupstair == u.ux && yupstair == u.uy) ||
+ (xdnstair == u.ux && ydnstair == u.uy))
+ nomul(0);
+ }
+
+ if(tmpr->typ == POOL && !Levitation)
+ drown(); /* not necessarily fatal */
+
+/*
+ if(u.udispl) {
+ u.udispl = 0;
+ newsym(oldx,oldy);
+ }
+*/
+ if(!Blind) {
+#ifdef QUEST
+ setsee();
+#else
+ if(ust->lit) {
+ if(tmpr->lit) {
+ if(tmpr->typ == DOOR)
+ prl1(u.ux+u.dx,u.uy+u.dy);
+ else if(ust->typ == DOOR)
+ nose1(oldx-u.dx,oldy-u.dy);
+ } else {
+ unsee();
+ prl1(u.ux+u.dx,u.uy+u.dy);
+ }
+ } else {
+ if(tmpr->lit) setsee();
+ else {
+ prl1(u.ux+u.dx,u.uy+u.dy);
+ if(tmpr->typ == DOOR) {
+ if(u.dy) {
+ prl(u.ux-1,u.uy);
+ prl(u.ux+1,u.uy);
+ } else {
+ prl(u.ux,u.uy-1);
+ prl(u.ux,u.uy+1);
+ }
+ }
+ }
+ nose1(oldx-u.dx,oldy-u.dy);
+ }
+#endif QUEST
+ } else {
+ pru();
+ }
+ if(!flags.nopick) pickup(1);
+ if(trap) dotrap(trap); /* fall into pit, arrow trap, etc. */
+ (void) inshop();
+ if(!Blind) read_engr_at(u.ux,u.uy);
+}
+
+movobj(obj, ox, oy)
+register struct obj *obj;
+register int ox, oy;
+{
+ /* Some dirty programming to get display right */
+ freeobj(obj);
+ unpobj(obj);
+ obj->nobj = fobj;
+ fobj = obj;
+ obj->ox = ox;
+ obj->oy = oy;
+}
+
+dopickup(){
+ if(!g_at(u.ux,u.uy) && !o_at(u.ux,u.uy)) {
+ pline("There is nothing here to pick up.");
+ return(0);
+ }
+ if(Levitation) {
+ pline("You cannot reach the floor.");
+ return(1);
+ }
+ pickup(0);
+ return(1);
+}
+
+pickup(all)
+{
+ register struct gold *gold;
+ register struct obj *obj, *obj2;
+ register int wt;
+
+ if(Levitation) return;
+ while(gold = g_at(u.ux,u.uy)) {
+ pline("%ld gold piece%s.", gold->amount, plur(gold->amount));
+ u.ugold += gold->amount;
+ flags.botl = 1;
+ freegold(gold);
+ if(flags.run) nomul(0);
+ if(Invisible) newsym(u.ux,u.uy);
+ }
+
+ /* check for more than one object */
+ if(!all) {
+ register int ct = 0;
+
+ for(obj = fobj; obj; obj = obj->nobj)
+ if(obj->ox == u.ux && obj->oy == u.uy)
+ if(!Punished || obj != uchain)
+ ct++;
+ if(ct < 2)
+ all++;
+ else
+ pline("There are several objects here.");
+ }
+
+ for(obj = fobj; obj; obj = obj2) {
+ obj2 = obj->nobj; /* perhaps obj will be picked up */
+ if(obj->ox == u.ux && obj->oy == u.uy) {
+ if(flags.run) nomul(0);
+
+ /* do not pick up uchain */
+ if(Punished && obj == uchain)
+ continue;
+
+ if(!all) {
+ char c;
+
+ pline("Pick up %s ? [ynaq]", doname(obj));
+ while(!index("ynaq ", (c = readchar())))
+ bell();
+ if(c == 'q') return;
+ if(c == 'n') continue;
+ if(c == 'a') all = 1;
+ }
+
+ if(obj->otyp == DEAD_COCKATRICE && !uarmg){
+ pline("Touching the dead cockatrice is a fatal mistake.");
+ pline("You turn to stone.");
+ killer = "cockatrice cadaver";
+ done("died");
+ }
+
+ if(obj->otyp == SCR_SCARE_MONSTER){
+ if(!obj->spe) obj->spe = 1;
+ else {
+ /* Note: perhaps the 1st pickup failed: you cannot
+ carry anymore, and so we never dropped it -
+ let's assume that treading on it twice also
+ destroys the scroll */
+ pline("The scroll turns to dust as you pick it up.");
+ delobj(obj);
+ continue;
+ }
+ }
+
+ wt = inv_weight() + obj->owt;
+ if(wt > 0) {
+ if(obj->quan > 1) {
+ /* see how many we can lift */
+ extern struct obj *splitobj();
+ int savequan = obj->quan;
+ int iw = inv_weight();
+ int qq;
+ for(qq = 1; qq < savequan; qq++){
+ obj->quan = qq;
+ if(iw + weight(obj) > 0)
+ break;
+ }
+ obj->quan = savequan;
+ qq--;
+ /* we can carry qq of them */
+ if(!qq) goto too_heavy;
+ pline("You can only carry %s of the %s lying here.",
+ (qq == 1) ? "one" : "some",
+ doname(obj));
+ (void) splitobj(obj, qq);
+ /* note: obj2 is set already, so we'll never
+ * encounter the other half; if it should be
+ * otherwise then write
+ * obj2 = splitobj(obj,qq);
+ */
+ goto lift_some;
+ }
+ too_heavy:
+ pline("There %s %s here, but %s.",
+ (obj->quan == 1) ? "is" : "are",
+ doname(obj),
+ !invent ? "it is too heavy for you to lift"
+ : "you cannot carry anymore");
+ break;
+ }
+ lift_some:
+ if(inv_cnt() >= 52) {
+ pline("Your knapsack cannot accomodate anymore items.");
+ break;
+ }
+ if(wt > -5) pline("You have a little trouble lifting");
+ freeobj(obj);
+ if(Invisible) newsym(u.ux,u.uy);
+ addtobill(obj); /* sets obj->unpaid if necessary */
+ { int pickquan = obj->quan;
+ int mergquan;
+ if(!Blind) obj->dknown = 1; /* this is done by prinv(),
+ but addinv() needs it already for merging */
+ obj = addinv(obj); /* might merge it with other objects */
+ mergquan = obj->quan;
+ obj->quan = pickquan; /* to fool prinv() */
+ prinv(obj);
+ obj->quan = mergquan;
+ }
+ }
+ }
+}
+
+/* stop running if we see something interesting */
+/* turn around a corner if that is the only way we can proceed */
+/* do not turn left or right twice */
+lookaround(){
+register x,y,i,x0,y0,m0,i0 = 9;
+register int corrct = 0, noturn = 0;
+register struct monst *mtmp;
+#ifdef lint
+ /* suppress "used before set" message */
+ x0 = y0 = 0;
+#endif lint
+ if(Blind || flags.run == 0) return;
+ if(flags.run == 1 && levl[u.ux][u.uy].typ == ROOM) return;
+#ifdef QUEST
+ if(u.ux0 == u.ux+u.dx && u.uy0 == u.uy+u.dy) goto stop;
+#endif QUEST
+ for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++){
+ if(x == u.ux && y == u.uy) continue;
+ if(!levl[x][y].typ) continue;
+ if((mtmp = m_at(x,y)) && !mtmp->mimic &&
+ (!mtmp->minvis || See_invisible)){
+ if(!mtmp->mtame || (x == u.ux+u.dx && y == u.uy+u.dy))
+ goto stop;
+ } else mtmp = 0; /* invisible M cannot influence us */
+ if(x == u.ux-u.dx && y == u.uy-u.dy) continue;
+ switch(levl[x][y].scrsym){
+ case '|':
+ case '-':
+ case '.':
+ case ' ':
+ break;
+ case '+':
+ if(x != u.ux && y != u.uy) break;
+ if(flags.run != 1) goto stop;
+ /* fall into next case */
+ case CORR_SYM:
+ corr:
+ if(flags.run == 1 || flags.run == 3) {
+ i = DIST(x,y,u.ux+u.dx,u.uy+u.dy);
+ if(i > 2) break;
+ if(corrct == 1 && DIST(x,y,x0,y0) != 1)
+ noturn = 1;
+ if(i < i0) {
+ i0 = i;
+ x0 = x;
+ y0 = y;
+ m0 = mtmp ? 1 : 0;
+ }
+ }
+ corrct++;
+ break;
+ case '^':
+ if(flags.run == 1) goto corr; /* if you must */
+ if(x == u.ux+u.dx && y == u.uy+u.dy) goto stop;
+ break;
+ default: /* e.g. objects or trap or stairs */
+ if(flags.run == 1) goto corr;
+ if(mtmp) break; /* d */
+ stop:
+ nomul(0);
+ return;
+ }
+ }
+#ifdef QUEST
+ if(corrct > 0 && (flags.run == 4 || flags.run == 5)) goto stop;
+#endif QUEST
+ if(corrct > 1 && flags.run == 2) goto stop;
+ if((flags.run == 1 || flags.run == 3) && !noturn && !m0 && i0 &&
+ (corrct == 1 || (corrct == 2 && i0 == 1))) {
+ /* make sure that we do not turn too far */
+ if(i0 == 2) {
+ if(u.dx == y0-u.uy && u.dy == u.ux-x0)
+ i = 2; /* straight turn right */
+ else
+ i = -2; /* straight turn left */
+ } else if(u.dx && u.dy) {
+ if((u.dx == u.dy && y0 == u.uy) ||
+ (u.dx != u.dy && y0 != u.uy))
+ i = -1; /* half turn left */
+ else
+ i = 1; /* half turn right */
+ } else {
+ if((x0-u.ux == y0-u.uy && !u.dy) ||
+ (x0-u.ux != y0-u.uy && u.dy))
+ i = 1; /* half turn right */
+ else
+ i = -1; /* half turn left */
+ }
+ i += u.last_str_turn;
+ if(i <= 2 && i >= -2) {
+ u.last_str_turn = i;
+ u.dx = x0-u.ux, u.dy = y0-u.uy;
+ }
+ }
+}
+
+/* something like lookaround, but we are not running */
+/* react only to monsters that might hit us */
+monster_nearby() {
+register int x,y;
+register struct monst *mtmp;
+ if(!Blind)
+ for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++){
+ if(x == u.ux && y == u.uy) continue;
+ if((mtmp = m_at(x,y)) && !mtmp->mimic && !mtmp->mtame &&
+ !mtmp->mpeaceful && !index("Ea", mtmp->data->mlet) &&
+ !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */
+ (!mtmp->minvis || See_invisible))
+ return(1);
+ }
+ return(0);
+}
+
+#ifdef QUEST
+cansee(x,y) xchar x,y; {
+register int dx,dy,adx,ady,sdx,sdy,dmax,d;
+ if(Blind) return(0);
+ if(!isok(x,y)) return(0);
+ d = dist(x,y);
+ if(d < 3) return(1);
+ if(d > u.uhorizon*u.uhorizon) return(0);
+ if(!levl[x][y].lit)
+ return(0);
+ dx = x - u.ux; adx = abs(dx); sdx = sgn(dx);
+ dy = y - u.uy; ady = abs(dy); sdy = sgn(dy);
+ if(dx == 0 || dy == 0 || adx == ady){
+ dmax = (dx == 0) ? ady : adx;
+ for(d = 1; d <= dmax; d++)
+ if(!rroom(sdx*d,sdy*d))
+ return(0);
+ return(1);
+ } else if(ady > adx){
+ for(d = 1; d <= ady; d++){
+ if(!rroom(sdx*( (d*adx)/ady ), sdy*d) ||
+ !rroom(sdx*( (d*adx-1)/ady+1 ), sdy*d))
+ return(0);
+ }
+ return(1);
+ } else {
+ for(d = 1; d <= adx; d++){
+ if(!rroom(sdx*d, sdy*( (d*ady)/adx )) ||
+ !rroom(sdx*d, sdy*( (d*ady-1)/adx+1 )))
+ return(0);
+ }
+ return(1);
+ }
+}
+
+rroom(x,y) register int x,y; {
+ return(IS_ROOM(levl[u.ux+x][u.uy+y].typ));
+}
+
+#else
+
+cansee(x,y) xchar x,y; {
+ if(Blind || u.uswallow) return(0);
+ if(dist(x,y) < 3) return(1);
+ if(levl[x][y].lit && seelx <= x && x <= seehx && seely <= y &&
+ y <= seehy) return(1);
+ return(0);
+}
+#endif QUEST
+
+sgn(a) register int a; {
+ return((a > 0) ? 1 : (a == 0) ? 0 : -1);
+}
+
+#ifdef QUEST
+setsee()
+{
+ register x,y;
+
+ if(Blind) {
+ pru();
+ return;
+ }
+ for(y = u.uy-u.uhorizon; y <= u.uy+u.uhorizon; y++)
+ for(x = u.ux-u.uhorizon; x <= u.ux+u.uhorizon; x++) {
+ if(cansee(x,y))
+ prl(x,y);
+ }
+}
+
+#else
+
+setsee()
+{
+ register x,y;
+
+ if(Blind) {
+ pru();
+ return;
+ }
+ if(!levl[u.ux][u.uy].lit) {
+ seelx = u.ux-1;
+ seehx = u.ux+1;
+ seely = u.uy-1;
+ seehy = u.uy+1;
+ } else {
+ for(seelx = u.ux; levl[seelx-1][u.uy].lit; seelx--);
+ for(seehx = u.ux; levl[seehx+1][u.uy].lit; seehx++);
+ for(seely = u.uy; levl[u.ux][seely-1].lit; seely--);
+ for(seehy = u.uy; levl[u.ux][seehy+1].lit; seehy++);
+ }
+ for(y = seely; y <= seehy; y++)
+ for(x = seelx; x <= seehx; x++) {
+ prl(x,y);
+ }
+ if(!levl[u.ux][u.uy].lit) seehx = 0; /* seems necessary elsewhere */
+ else {
+ if(seely == u.uy) for(x = u.ux-1; x <= u.ux+1; x++) prl(x,seely-1);
+ if(seehy == u.uy) for(x = u.ux-1; x <= u.ux+1; x++) prl(x,seehy+1);
+ if(seelx == u.ux) for(y = u.uy-1; y <= u.uy+1; y++) prl(seelx-1,y);
+ if(seehx == u.ux) for(y = u.uy-1; y <= u.uy+1; y++) prl(seehx+1,y);
+ }
+}
+#endif QUEST
+
+nomul(nval)
+register nval;
+{
+ if(multi < 0) return;
+ multi = nval;
+ flags.mv = flags.run = 0;
+}
+
+abon()
+{
+ if(u.ustr == 3) return(-3);
+ else if(u.ustr < 6) return(-2);
+ else if(u.ustr < 8) return(-1);
+ else if(u.ustr < 17) return(0);
+ else if(u.ustr < 69) return(1); /* up to 18/50 */
+ else if(u.ustr < 118) return(2);
+ else return(3);
+}
+
+dbon()
+{
+ if(u.ustr < 6) return(-1);
+ else if(u.ustr < 16) return(0);
+ else if(u.ustr < 18) return(1);
+ else if(u.ustr == 18) return(2); /* up to 18 */
+ else if(u.ustr < 94) return(3); /* up to 18/75 */
+ else if(u.ustr < 109) return(4); /* up to 18/90 */
+ else if(u.ustr < 118) return(5); /* up to 18/99 */
+ else return(6);
+}
+
+losestr(num) /* may kill you; cause may be poison or monster like 'A' */
+register num;
+{
+ u.ustr -= num;
+ while(u.ustr < 3) {
+ u.ustr++;
+ u.uhp -= 6;
+ u.uhpmax -= 6;
+ }
+ flags.botl = 1;
+}
+
+losehp(n,knam)
+register n;
+register char *knam;
+{
+ u.uhp -= n;
+ if(u.uhp > u.uhpmax)
+ u.uhpmax = u.uhp; /* perhaps n was negative */
+ flags.botl = 1;
+ if(u.uhp < 1) {
+ killer = knam; /* the thing that killed you */
+ done("died");
+ }
+}
+
+losehp_m(n,mtmp)
+register n;
+register struct monst *mtmp;
+{
+ u.uhp -= n;
+ flags.botl = 1;
+ if(u.uhp < 1)
+ done_in_by(mtmp);
+}
+
+losexp() /* hit by V or W */
+{
+ register num;
+ extern long newuexp();
+
+ if(u.ulevel > 1)
+ pline("Goodbye level %u.", u.ulevel--);
+ else
+ u.uhp = -1;
+ num = rnd(10);
+ u.uhp -= num;
+ u.uhpmax -= num;
+ u.uexp = newuexp();
+ flags.botl = 1;
+}
+
+inv_weight(){
+register struct obj *otmp = invent;
+register int wt = (u.ugold + 500)/1000;
+register int carrcap;
+ if(Levitation) /* pugh@cornell */
+ carrcap = MAX_CARR_CAP;
+ else {
+ carrcap = 5*(((u.ustr > 18) ? 20 : u.ustr) + u.ulevel);
+ if(carrcap > MAX_CARR_CAP) carrcap = MAX_CARR_CAP;
+ if(Wounded_legs & LEFT_SIDE) carrcap -= 10;
+ if(Wounded_legs & RIGHT_SIDE) carrcap -= 10;
+ }
+ while(otmp){
+ wt += otmp->owt;
+ otmp = otmp->nobj;
+ }
+ return(wt - carrcap);
+}
+
+inv_cnt(){
+register struct obj *otmp = invent;
+register int ct = 0;
+ while(otmp){
+ ct++;
+ otmp = otmp->nobj;
+ }
+ return(ct);
+}
+
+long
+newuexp()
+{
+ return(10*(1L << (u.ulevel-1)));
+}
diff --git a/games/hack/hack.cmd.c b/games/hack/hack.cmd.c
new file mode 100644
index 000000000000..e36cfcca9d66
--- /dev/null
+++ b/games/hack/hack.cmd.c
@@ -0,0 +1,302 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.cmd.c - version 1.0.3 */
+
+#include "hack.h"
+#include "def.func_tab.h"
+
+int doredraw(),doredotopl(),dodrop(),dodrink(),doread(),dosearch(),dopickup(),
+doversion(),doweararm(),dowearring(),doremarm(),doremring(),dopay(),doapply(),
+dosave(),dowield(),ddoinv(),dozap(),ddocall(),dowhatis(),doengrave(),dotele(),
+dohelp(),doeat(),doddrop(),do_mname(),doidtrap(),doprwep(),doprarm(),
+doprring(),doprgold(),dodiscovered(),dotypeinv(),dolook(),doset(),
+doup(), dodown(), done1(), donull(), dothrow(), doextcmd(), dodip(), dopray();
+#ifdef SHELL
+int dosh();
+#endif SHELL
+#ifdef SUSPEND
+int dosuspend();
+#endif SUSPEND
+
+struct func_tab cmdlist[]={
+ '\020', doredotopl,
+ '\022', doredraw,
+ '\024', dotele,
+#ifdef SUSPEND
+ '\032', dosuspend,
+#endif SUSPEND
+ 'a', doapply,
+/* 'A' : UNUSED */
+/* 'b', 'B' : go sw */
+ 'c', ddocall,
+ 'C', do_mname,
+ 'd', dodrop,
+ 'D', doddrop,
+ 'e', doeat,
+ 'E', doengrave,
+/* 'f', 'F' : multiple go (might become 'fight') */
+/* 'g', 'G' : UNUSED */
+/* 'h', 'H' : go west */
+ 'I', dotypeinv, /* Robert Viduya */
+ 'i', ddoinv,
+/* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */
+/* 'o', doopen, */
+ 'O', doset,
+ 'p', dopay,
+ 'P', dowearring,
+ 'q', dodrink,
+ 'Q', done1,
+ 'r', doread,
+ 'R', doremring,
+ 's', dosearch,
+ 'S', dosave,
+ 't', dothrow,
+ 'T', doremarm,
+/* 'u', 'U' : go ne */
+ 'v', doversion,
+/* 'V' : UNUSED */
+ 'w', dowield,
+ 'W', doweararm,
+/* 'x', 'X' : UNUSED */
+/* 'y', 'Y' : go nw */
+ 'z', dozap,
+/* 'Z' : UNUSED */
+ '<', doup,
+ '>', dodown,
+ '/', dowhatis,
+ '?', dohelp,
+#ifdef SHELL
+ '!', dosh,
+#endif SHELL
+ '.', donull,
+ ' ', donull,
+ ',', dopickup,
+ ':', dolook,
+ '^', doidtrap,
+ '\\', dodiscovered, /* Robert Viduya */
+ WEAPON_SYM, doprwep,
+ ARMOR_SYM, doprarm,
+ RING_SYM, doprring,
+ '$', doprgold,
+ '#', doextcmd,
+ 0,0,0
+};
+
+struct ext_func_tab extcmdlist[] = {
+ "dip", dodip,
+ "pray", dopray,
+ (char *) 0, donull
+};
+
+extern char *parse(), lowc(), unctrl(), quitchars[];
+
+rhack(cmd)
+register char *cmd;
+{
+ register struct func_tab *tlist = cmdlist;
+ boolean firsttime = FALSE;
+ register res;
+
+ if(!cmd) {
+ firsttime = TRUE;
+ flags.nopick = 0;
+ cmd = parse();
+ }
+ if(!*cmd || (*cmd & 0377) == 0377 ||
+ (flags.no_rest_on_space && *cmd == ' ')){
+ bell();
+ flags.move = 0;
+ return; /* probably we just had an interrupt */
+ }
+ if(movecmd(*cmd)) {
+ walk:
+ if(multi) flags.mv = 1;
+ domove();
+ return;
+ }
+ if(movecmd(lowc(*cmd))) {
+ flags.run = 1;
+ rush:
+ if(firsttime){
+ if(!multi) multi = COLNO;
+ u.last_str_turn = 0;
+ }
+ flags.mv = 1;
+#ifdef QUEST
+ if(flags.run >= 4) finddir();
+ if(firsttime){
+ u.ux0 = u.ux + u.dx;
+ u.uy0 = u.uy + u.dy;
+ }
+#endif QUEST
+ domove();
+ return;
+ }
+ if((*cmd == 'f' && movecmd(cmd[1])) || movecmd(unctrl(*cmd))) {
+ flags.run = 2;
+ goto rush;
+ }
+ if(*cmd == 'F' && movecmd(lowc(cmd[1]))) {
+ flags.run = 3;
+ goto rush;
+ }
+ if(*cmd == 'm' && movecmd(cmd[1])) {
+ flags.run = 0;
+ flags.nopick = 1;
+ goto walk;
+ }
+ if(*cmd == 'M' && movecmd(lowc(cmd[1]))) {
+ flags.run = 1;
+ flags.nopick = 1;
+ goto rush;
+ }
+#ifdef QUEST
+ if(*cmd == cmd[1] && (*cmd == 'f' || *cmd == 'F')) {
+ flags.run = 4;
+ if(*cmd == 'F') flags.run += 2;
+ if(cmd[2] == '-') flags.run += 1;
+ goto rush;
+ }
+#endif QUEST
+ while(tlist->f_char) {
+ if(*cmd == tlist->f_char){
+ res = (*(tlist->f_funct))();
+ if(!res) {
+ flags.move = 0;
+ multi = 0;
+ }
+ return;
+ }
+ tlist++;
+ }
+ { char expcmd[10];
+ register char *cp = expcmd;
+ while(*cmd && cp-expcmd < sizeof(expcmd)-2) {
+ if(*cmd >= 040 && *cmd < 0177)
+ *cp++ = *cmd++;
+ else {
+ *cp++ = '^';
+ *cp++ = *cmd++ ^ 0100;
+ }
+ }
+ *cp++ = 0;
+ pline("Unknown command '%s'.", expcmd);
+ }
+ multi = flags.move = 0;
+}
+
+doextcmd() /* here after # - now read a full-word command */
+{
+ char buf[BUFSZ];
+ register struct ext_func_tab *efp = extcmdlist;
+
+ pline("# ");
+ getlin(buf);
+ clrlin();
+ if(buf[0] == '\033')
+ return(0);
+ while(efp->ef_txt) {
+ if(!strcmp(efp->ef_txt, buf))
+ return((*(efp->ef_funct))());
+ efp++;
+ }
+ pline("%s: unknown command.", buf);
+ return(0);
+}
+
+char
+lowc(sym)
+char sym;
+{
+ return( (sym >= 'A' && sym <= 'Z') ? sym+'a'-'A' : sym );
+}
+
+char
+unctrl(sym)
+char sym;
+{
+ return( (sym >= ('A' & 037) && sym <= ('Z' & 037)) ? sym + 0140 : sym );
+}
+
+/* 'rogue'-like direction commands */
+char sdir[] = "hykulnjb><";
+schar xdir[10] = { -1,-1, 0, 1, 1, 1, 0,-1, 0, 0 };
+schar ydir[10] = { 0,-1,-1,-1, 0, 1, 1, 1, 0, 0 };
+schar zdir[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1,-1 };
+
+movecmd(sym) /* also sets u.dz, but returns false for <> */
+char sym;
+{
+ register char *dp;
+
+ u.dz = 0;
+ if(!(dp = index(sdir, sym))) return(0);
+ u.dx = xdir[dp-sdir];
+ u.dy = ydir[dp-sdir];
+ u.dz = zdir[dp-sdir];
+ return(!u.dz);
+}
+
+getdir(s)
+boolean s;
+{
+ char dirsym;
+
+ if(s) pline("In what direction?");
+ dirsym = readchar();
+ if(!movecmd(dirsym) && !u.dz) {
+ if(!index(quitchars, dirsym))
+ pline("What a strange direction!");
+ return(0);
+ }
+ if(Confusion && !u.dz)
+ confdir();
+ return(1);
+}
+
+confdir()
+{
+ register x = rn2(8);
+ u.dx = xdir[x];
+ u.dy = ydir[x];
+}
+
+#ifdef QUEST
+finddir(){
+register int i, ui = u.di;
+ for(i = 0; i <= 8; i++){
+ if(flags.run & 1) ui++; else ui += 7;
+ ui %= 8;
+ if(i == 8){
+ pline("Not near a wall.");
+ flags.move = multi = 0;
+ return(0);
+ }
+ if(!isroom(u.ux+xdir[ui], u.uy+ydir[ui]))
+ break;
+ }
+ for(i = 0; i <= 8; i++){
+ if(flags.run & 1) ui += 7; else ui++;
+ ui %= 8;
+ if(i == 8){
+ pline("Not near a room.");
+ flags.move = multi = 0;
+ return(0);
+ }
+ if(isroom(u.ux+xdir[ui], u.uy+ydir[ui]))
+ break;
+ }
+ u.di = ui;
+ u.dx = xdir[ui];
+ u.dy = ydir[ui];
+}
+
+isroom(x,y) register x,y; { /* what about POOL? */
+ return(isok(x,y) && (levl[x][y].typ == ROOM ||
+ (levl[x][y].typ >= LDOOR && flags.run >= 6)));
+}
+#endif QUEST
+
+isok(x,y) register x,y; {
+ /* x corresponds to curx, so x==1 is the first column. Ach. %% */
+ return(x >= 1 && x <= COLNO-1 && y >= 0 && y <= ROWNO-1);
+}
diff --git a/games/hack/hack.do.c b/games/hack/hack.do.c
new file mode 100644
index 000000000000..1227eb923dd8
--- /dev/null
+++ b/games/hack/hack.do.c
@@ -0,0 +1,488 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.do.c - version 1.0.3 */
+
+/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */
+
+#include "hack.h"
+
+extern struct obj *splitobj(), *addinv();
+extern boolean hmon();
+extern boolean level_exists[];
+extern struct monst youmonst;
+extern char *Doname();
+extern char *nomovemsg;
+
+static int drop();
+
+dodrop() {
+ return(drop(getobj("0$#", "drop")));
+}
+
+static int
+drop(obj) register struct obj *obj; {
+ if(!obj) return(0);
+ if(obj->olet == '$') { /* pseudo object */
+ register long amount = OGOLD(obj);
+
+ if(amount == 0)
+ pline("You didn't drop any gold pieces.");
+ else {
+ mkgold(amount, u.ux, u.uy);
+ pline("You dropped %ld gold piece%s.",
+ amount, plur(amount));
+ if(Invisible) newsym(u.ux, u.uy);
+ }
+ free((char *) obj);
+ return(1);
+ }
+ if(obj->owornmask & (W_ARMOR | W_RING)){
+ pline("You cannot drop something you are wearing.");
+ return(0);
+ }
+ if(obj == uwep) {
+ if(uwep->cursed) {
+ pline("Your weapon is welded to your hand!");
+ return(0);
+ }
+ setuwep((struct obj *) 0);
+ }
+ pline("You dropped %s.", doname(obj));
+ dropx(obj);
+ return(1);
+}
+
+/* Called in several places - should not produce texts */
+dropx(obj)
+register struct obj *obj;
+{
+ freeinv(obj);
+ dropy(obj);
+}
+
+dropy(obj)
+register struct obj *obj;
+{
+ if(obj->otyp == CRYSKNIFE)
+ obj->otyp = WORM_TOOTH;
+ obj->ox = u.ux;
+ obj->oy = u.uy;
+ obj->nobj = fobj;
+ fobj = obj;
+ if(Invisible) newsym(u.ux,u.uy);
+ subfrombill(obj);
+ stackobj(obj);
+}
+
+/* drop several things */
+doddrop() {
+ return(ggetobj("drop", drop, 0));
+}
+
+dodown()
+{
+ if(u.ux != xdnstair || u.uy != ydnstair) {
+ pline("You can't go down here.");
+ return(0);
+ }
+ if(u.ustuck) {
+ pline("You are being held, and cannot go down.");
+ return(1);
+ }
+ if(Levitation) {
+ pline("You're floating high above the stairs.");
+ return(0);
+ }
+
+ goto_level(dlevel+1, TRUE);
+ return(1);
+}
+
+doup()
+{
+ if(u.ux != xupstair || u.uy != yupstair) {
+ pline("You can't go up here.");
+ return(0);
+ }
+ if(u.ustuck) {
+ pline("You are being held, and cannot go up.");
+ return(1);
+ }
+ if(!Levitation && inv_weight() + 5 > 0) {
+ pline("Your load is too heavy to climb the stairs.");
+ return(1);
+ }
+
+ goto_level(dlevel-1, TRUE);
+ return(1);
+}
+
+goto_level(newlevel, at_stairs)
+register int newlevel;
+register boolean at_stairs;
+{
+ register fd;
+ register boolean up = (newlevel < dlevel);
+
+ if(newlevel <= 0) done("escaped"); /* in fact < 0 is impossible */
+ if(newlevel > MAXLEVEL) newlevel = MAXLEVEL; /* strange ... */
+ if(newlevel == dlevel) return; /* this can happen */
+
+ glo(dlevel);
+ fd = creat(lock, FMASK);
+ if(fd < 0) {
+ /*
+ * This is not quite impossible: e.g., we may have
+ * exceeded our quota. If that is the case then we
+ * cannot leave this level, and cannot save either.
+ * Another possibility is that the directory was not
+ * writable.
+ */
+ pline("A mysterious force prevents you from going %s.",
+ up ? "up" : "down");
+ return;
+ }
+
+ if(Punished) unplacebc();
+ u.utrap = 0; /* needed in level_tele */
+ u.ustuck = 0; /* idem */
+ keepdogs();
+ seeoff(1);
+ if(u.uswallow) /* idem */
+ u.uswldtim = u.uswallow = 0;
+ flags.nscrinh = 1;
+ u.ux = FAR; /* hack */
+ (void) inshop(); /* probably was a trapdoor */
+
+ savelev(fd,dlevel);
+ (void) close(fd);
+
+ dlevel = newlevel;
+ if(maxdlevel < dlevel)
+ maxdlevel = dlevel;
+ glo(dlevel);
+
+ if(!level_exists[dlevel])
+ mklev();
+ else {
+ extern int hackpid;
+
+ if((fd = open(lock,0)) < 0) {
+ pline("Cannot open %s .", lock);
+ pline("Probably someone removed it.");
+ done("tricked");
+ }
+ getlev(fd, hackpid, dlevel);
+ (void) close(fd);
+ }
+
+ if(at_stairs) {
+ if(up) {
+ u.ux = xdnstair;
+ u.uy = ydnstair;
+ if(!u.ux) { /* entering a maze from below? */
+ u.ux = xupstair; /* this will confuse the player! */
+ u.uy = yupstair;
+ }
+ if(Punished && !Levitation){
+ pline("With great effort you climb the stairs.");
+ placebc(1);
+ }
+ } else {
+ u.ux = xupstair;
+ u.uy = yupstair;
+ if(inv_weight() + 5 > 0 || Punished){
+ pline("You fall down the stairs."); /* %% */
+ losehp(rnd(3), "fall");
+ if(Punished) {
+ if(uwep != uball && rn2(3)){
+ pline("... and are hit by the iron ball.");
+ losehp(rnd(20), "iron ball");
+ }
+ placebc(1);
+ }
+ selftouch("Falling, you");
+ }
+ }
+ { register struct monst *mtmp = m_at(u.ux, u.uy);
+ if(mtmp)
+ mnexto(mtmp);
+ }
+ } else { /* trapdoor or level_tele */
+ do {
+ u.ux = rnd(COLNO-1);
+ u.uy = rn2(ROWNO);
+ } while(levl[u.ux][u.uy].typ != ROOM ||
+ m_at(u.ux,u.uy));
+ if(Punished){
+ if(uwep != uball && !up /* %% */ && rn2(5)){
+ pline("The iron ball falls on your head.");
+ losehp(rnd(25), "iron ball");
+ }
+ placebc(1);
+ }
+ selftouch("Falling, you");
+ }
+ (void) inshop();
+ initrack();
+
+ losedogs();
+ { register struct monst *mtmp;
+ if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */
+ }
+ flags.nscrinh = 0;
+ setsee();
+ seeobjs(); /* make old cadavers disappear - riv05!a3 */
+ docrt();
+ pickup(1);
+ read_engr_at(u.ux,u.uy);
+}
+
+donull() {
+ return(1); /* Do nothing, but let other things happen */
+}
+
+dopray() {
+ nomovemsg = "You finished your prayer.";
+ nomul(-3);
+ return(1);
+}
+
+struct monst *bhit(), *boomhit();
+dothrow()
+{
+ register struct obj *obj;
+ register struct monst *mon;
+ register tmp;
+
+ obj = getobj("#)", "throw"); /* it is also possible to throw food */
+ /* (or jewels, or iron balls ... ) */
+ if(!obj || !getdir(1)) /* ask "in what direction?" */
+ return(0);
+ if(obj->owornmask & (W_ARMOR | W_RING)){
+ pline("You can't throw something you are wearing.");
+ return(0);
+ }
+
+ u_wipe_engr(2);
+
+ if(obj == uwep){
+ if(obj->cursed){
+ pline("Your weapon is welded to your hand.");
+ return(1);
+ }
+ if(obj->quan > 1)
+ setuwep(splitobj(obj, 1));
+ else
+ setuwep((struct obj *) 0);
+ }
+ else if(obj->quan > 1)
+ (void) splitobj(obj, 1);
+ freeinv(obj);
+ if(u.uswallow) {
+ mon = u.ustuck;
+ bhitpos.x = mon->mx;
+ bhitpos.y = mon->my;
+ } else if(u.dz) {
+ if(u.dz < 0) {
+ pline("%s hits the ceiling, then falls back on top of your head.",
+ Doname(obj)); /* note: obj->quan == 1 */
+ if(obj->olet == POTION_SYM)
+ potionhit(&youmonst, obj);
+ else {
+ if(uarmh) pline("Fortunately, you are wearing a helmet!");
+ losehp(uarmh ? 1 : rnd((int)(obj->owt)), "falling object");
+ dropy(obj);
+ }
+ } else {
+ pline("%s hits the floor.", Doname(obj));
+ if(obj->otyp == EXPENSIVE_CAMERA) {
+ pline("It is shattered in a thousand pieces!");
+ obfree(obj, Null(obj));
+ } else if(obj->otyp == EGG) {
+ pline("\"Splash!\"");
+ obfree(obj, Null(obj));
+ } else if(obj->olet == POTION_SYM) {
+ pline("The flask breaks, and you smell a peculiar odor ...");
+ potionbreathe(obj);
+ obfree(obj, Null(obj));
+ } else {
+ dropy(obj);
+ }
+ }
+ return(1);
+ } else if(obj->otyp == BOOMERANG) {
+ mon = boomhit(u.dx, u.dy);
+ if(mon == &youmonst) { /* the thing was caught */
+ (void) addinv(obj);
+ return(1);
+ }
+ } else {
+ if(obj->otyp == PICK_AXE && shkcatch(obj))
+ return(1);
+
+ mon = bhit(u.dx, u.dy, (obj->otyp == ICE_BOX) ? 1 :
+ (!Punished || obj != uball) ? 8 : !u.ustuck ? 5 : 1,
+ obj->olet,
+ (int (*)()) 0, (int (*)()) 0, obj);
+ }
+ if(mon) {
+ /* awake monster if sleeping */
+ wakeup(mon);
+
+ if(obj->olet == WEAPON_SYM) {
+ tmp = -1+u.ulevel+mon->data->ac+abon();
+ if(obj->otyp < ROCK) {
+ if(!uwep ||
+ uwep->otyp != obj->otyp+(BOW-ARROW))
+ tmp -= 4;
+ else {
+ tmp += uwep->spe;
+ }
+ } else
+ if(obj->otyp == BOOMERANG) tmp += 4;
+ tmp += obj->spe;
+ if(u.uswallow || tmp >= rnd(20)) {
+ if(hmon(mon,obj,1) == TRUE){
+ /* mon still alive */
+#ifndef NOWORM
+ cutworm(mon,bhitpos.x,bhitpos.y,obj->otyp);
+#endif NOWORM
+ } else mon = 0;
+ /* weapons thrown disappear sometimes */
+ if(obj->otyp < BOOMERANG && rn2(3)) {
+ /* check bill; free */
+ obfree(obj, (struct obj *) 0);
+ return(1);
+ }
+ } else miss(objects[obj->otyp].oc_name, mon);
+ } else if(obj->otyp == HEAVY_IRON_BALL) {
+ tmp = -1+u.ulevel+mon->data->ac+abon();
+ if(!Punished || obj != uball) tmp += 2;
+ if(u.utrap) tmp -= 2;
+ if(u.uswallow || tmp >= rnd(20)) {
+ if(hmon(mon,obj,1) == FALSE)
+ mon = 0; /* he died */
+ } else miss("iron ball", mon);
+ } else if(obj->olet == POTION_SYM && u.ulevel > rn2(15)) {
+ potionhit(mon, obj);
+ return(1);
+ } else {
+ if(cansee(bhitpos.x,bhitpos.y))
+ pline("You miss %s.",monnam(mon));
+ else pline("You miss it.");
+ if(obj->olet == FOOD_SYM && mon->data->mlet == 'd')
+ if(tamedog(mon,obj)) return(1);
+ if(obj->olet == GEM_SYM && mon->data->mlet == 'u' &&
+ !mon->mtame){
+ if(obj->dknown && objects[obj->otyp].oc_name_known){
+ if(objects[obj->otyp].g_val > 0){
+ u.uluck += 5;
+ goto valuable;
+ } else {
+ pline("%s is not interested in your junk.",
+ Monnam(mon));
+ }
+ } else { /* value unknown to @ */
+ u.uluck++;
+ valuable:
+ if(u.uluck > LUCKMAX) /* dan@ut-ngp */
+ u.uluck = LUCKMAX;
+ pline("%s graciously accepts your gift.",
+ Monnam(mon));
+ mpickobj(mon, obj);
+ rloc(mon);
+ return(1);
+ }
+ }
+ }
+ }
+ /* the code following might become part of dropy() */
+ if(obj->otyp == CRYSKNIFE)
+ obj->otyp = WORM_TOOTH;
+ obj->ox = bhitpos.x;
+ obj->oy = bhitpos.y;
+ obj->nobj = fobj;
+ fobj = obj;
+ /* prevent him from throwing articles to the exit and escaping */
+ /* subfrombill(obj); */
+ stackobj(obj);
+ if(Punished && obj == uball &&
+ (bhitpos.x != u.ux || bhitpos.y != u.uy)){
+ freeobj(uchain);
+ unpobj(uchain);
+ if(u.utrap){
+ if(u.utraptype == TT_PIT)
+ pline("The ball pulls you out of the pit!");
+ else {
+ register long side =
+ rn2(3) ? LEFT_SIDE : RIGHT_SIDE;
+ pline("The ball pulls you out of the bear trap.");
+ pline("Your %s leg is severely damaged.",
+ (side == LEFT_SIDE) ? "left" : "right");
+ set_wounded_legs(side, 500+rn2(1000));
+ losehp(2, "thrown ball");
+ }
+ u.utrap = 0;
+ }
+ unsee();
+ uchain->nobj = fobj;
+ fobj = uchain;
+ u.ux = uchain->ox = bhitpos.x - u.dx;
+ u.uy = uchain->oy = bhitpos.y - u.dy;
+ setsee();
+ (void) inshop();
+ }
+ if(cansee(bhitpos.x, bhitpos.y)) prl(bhitpos.x,bhitpos.y);
+ return(1);
+}
+
+/* split obj so that it gets size num */
+/* remainder is put in the object structure delivered by this call */
+struct obj *
+splitobj(obj, num) register struct obj *obj; register int num; {
+register struct obj *otmp;
+ otmp = newobj(0);
+ *otmp = *obj; /* copies whole structure */
+ otmp->o_id = flags.ident++;
+ otmp->onamelth = 0;
+ obj->quan = num;
+ obj->owt = weight(obj);
+ otmp->quan -= num;
+ otmp->owt = weight(otmp); /* -= obj->owt ? */
+ obj->nobj = otmp;
+ if(obj->unpaid) splitbill(obj,otmp);
+ return(otmp);
+}
+
+more_experienced(exp,rexp)
+register int exp, rexp;
+{
+ extern char pl_character[];
+
+ u.uexp += exp;
+ u.urexp += 4*exp + rexp;
+ if(exp) flags.botl = 1;
+ if(u.urexp >= ((pl_character[0] == 'W') ? 1000 : 2000))
+ flags.beginner = 0;
+}
+
+set_wounded_legs(side, timex)
+register long side;
+register int timex;
+{
+ if(!Wounded_legs || (Wounded_legs & TIMEOUT))
+ Wounded_legs |= side + timex;
+ else
+ Wounded_legs |= side;
+}
+
+heal_legs()
+{
+ if(Wounded_legs) {
+ if((Wounded_legs & BOTH_SIDES) == BOTH_SIDES)
+ pline("Your legs feel somewhat better.");
+ else
+ pline("Your leg feels somewhat better.");
+ Wounded_legs = 0;
+ }
+}
diff --git a/games/hack/hack.do_name.c b/games/hack/hack.do_name.c
new file mode 100644
index 000000000000..72ac62c86253
--- /dev/null
+++ b/games/hack/hack.do_name.c
@@ -0,0 +1,289 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.do_name.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+extern char plname[];
+
+coord
+getpos(force,goal) int force; char *goal; {
+register cx,cy,i,c;
+extern char sdir[]; /* defined in hack.c */
+extern schar xdir[], ydir[]; /* idem */
+extern char *visctrl(); /* see below */
+coord cc;
+ pline("(For instructions type a ?)");
+ cx = u.ux;
+ cy = u.uy;
+ curs(cx,cy+2);
+ while((c = readchar()) != '.'){
+ for(i=0; i<8; i++) if(sdir[i] == c){
+ if(1 <= cx + xdir[i] && cx + xdir[i] <= COLNO)
+ cx += xdir[i];
+ if(0 <= cy + ydir[i] && cy + ydir[i] <= ROWNO-1)
+ cy += ydir[i];
+ goto nxtc;
+ }
+ if(c == '?'){
+ pline("Use [hjkl] to move the cursor to %s.", goal);
+ pline("Type a . when you are at the right place.");
+ } else {
+ pline("Unknown direction: '%s' (%s).",
+ visctrl(c),
+ force ? "use hjkl or ." : "aborted");
+ if(force) goto nxtc;
+ cc.x = -1;
+ cc.y = 0;
+ return(cc);
+ }
+ nxtc: ;
+ curs(cx,cy+2);
+ }
+ cc.x = cx;
+ cc.y = cy;
+ return(cc);
+}
+
+do_mname(){
+char buf[BUFSZ];
+coord cc;
+register int cx,cy,lth,i;
+register struct monst *mtmp, *mtmp2;
+extern char *lmonnam();
+ cc = getpos(0, "the monster you want to name");
+ cx = cc.x;
+ cy = cc.y;
+ if(cx < 0) return(0);
+ mtmp = m_at(cx,cy);
+ if(!mtmp){
+ if(cx == u.ux && cy == u.uy)
+ pline("This ugly monster is called %s and cannot be renamed.",
+ plname);
+ else
+ pline("There is no monster there.");
+ return(1);
+ }
+ if(mtmp->mimic){
+ pline("I see no monster there.");
+ return(1);
+ }
+ if(!cansee(cx,cy)) {
+ pline("I cannot see a monster there.");
+ return(1);
+ }
+ pline("What do you want to call %s? ", lmonnam(mtmp));
+ getlin(buf);
+ clrlin();
+ if(!*buf || *buf == '\033')
+ return(1);
+ lth = strlen(buf)+1;
+ if(lth > 63){
+ buf[62] = 0;
+ lth = 63;
+ }
+ mtmp2 = newmonst(mtmp->mxlth + lth);
+ *mtmp2 = *mtmp;
+ for(i=0; i<mtmp->mxlth; i++)
+ ((char *) mtmp2->mextra)[i] = ((char *) mtmp->mextra)[i];
+ mtmp2->mnamelth = lth;
+ (void) strcpy(NAME(mtmp2), buf);
+ replmon(mtmp,mtmp2);
+ return(1);
+}
+
+/*
+ * This routine changes the address of obj . Be careful not to call it
+ * when there might be pointers around in unknown places. For now: only
+ * when obj is in the inventory.
+ */
+do_oname(obj) register struct obj *obj; {
+register struct obj *otmp, *otmp2;
+register lth;
+char buf[BUFSZ];
+ pline("What do you want to name %s? ", doname(obj));
+ getlin(buf);
+ clrlin();
+ if(!*buf || *buf == '\033')
+ return;
+ lth = strlen(buf)+1;
+ if(lth > 63){
+ buf[62] = 0;
+ lth = 63;
+ }
+ otmp2 = newobj(lth);
+ *otmp2 = *obj;
+ otmp2->onamelth = lth;
+ (void) strcpy(ONAME(otmp2), buf);
+
+ setworn((struct obj *) 0, obj->owornmask);
+ setworn(otmp2, otmp2->owornmask);
+
+ /* do freeinv(obj); etc. by hand in order to preserve
+ the position of this object in the inventory */
+ if(obj == invent) invent = otmp2;
+ else for(otmp = invent; ; otmp = otmp->nobj){
+ if(!otmp)
+ panic("Do_oname: cannot find obj.");
+ if(otmp->nobj == obj){
+ otmp->nobj = otmp2;
+ break;
+ }
+ }
+ /* obfree(obj, otmp2); /* now unnecessary: no pointers on bill */
+ free((char *) obj); /* let us hope nobody else saved a pointer */
+}
+
+ddocall()
+{
+ register struct obj *obj;
+
+ pline("Do you want to name an individual object? [ny] ");
+ switch(readchar()) {
+ case '\033':
+ break;
+ case 'y':
+ obj = getobj("#", "name");
+ if(obj) do_oname(obj);
+ break;
+ default:
+ obj = getobj("?!=/", "call");
+ if(obj) docall(obj);
+ }
+ return(0);
+}
+
+docall(obj)
+register struct obj *obj;
+{
+ char buf[BUFSZ];
+ struct obj otemp;
+ register char **str1;
+ extern char *xname();
+ register char *str;
+
+ otemp = *obj;
+ otemp.quan = 1;
+ otemp.onamelth = 0;
+ str = xname(&otemp);
+ pline("Call %s %s: ", index(vowels,*str) ? "an" : "a", str);
+ getlin(buf);
+ clrlin();
+ if(!*buf || *buf == '\033')
+ return;
+ str = newstring(strlen(buf)+1);
+ (void) strcpy(str,buf);
+ str1 = &(objects[obj->otyp].oc_uname);
+ if(*str1) free(*str1);
+ *str1 = str;
+}
+
+char *ghostnames[] = { /* these names should have length < PL_NSIZ */
+ "adri", "andries", "andreas", "bert", "david", "dirk", "emile",
+ "frans", "fred", "greg", "hether", "jay", "john", "jon", "kay",
+ "kenny", "maud", "michiel", "mike", "peter", "robert", "ron",
+ "tom", "wilmar"
+};
+
+char *
+xmonnam(mtmp, vb) register struct monst *mtmp; int vb; {
+static char buf[BUFSZ]; /* %% */
+extern char *shkname();
+ if(mtmp->mnamelth && !vb) {
+ (void) strcpy(buf, NAME(mtmp));
+ return(buf);
+ }
+ switch(mtmp->data->mlet) {
+ case ' ':
+ { register char *gn = (char *) mtmp->mextra;
+ if(!*gn) { /* might also look in scorefile */
+ gn = ghostnames[rn2(SIZE(ghostnames))];
+ if(!rn2(2)) (void)
+ strcpy((char *) mtmp->mextra, !rn2(5) ? plname : gn);
+ }
+ (void) sprintf(buf, "%s's ghost", gn);
+ }
+ break;
+ case '@':
+ if(mtmp->isshk) {
+ (void) strcpy(buf, shkname(mtmp));
+ break;
+ }
+ /* fall into next case */
+ default:
+ (void) sprintf(buf, "the %s%s",
+ mtmp->minvis ? "invisible " : "",
+ mtmp->data->mname);
+ }
+ if(vb && mtmp->mnamelth) {
+ (void) strcat(buf, " called ");
+ (void) strcat(buf, NAME(mtmp));
+ }
+ return(buf);
+}
+
+char *
+lmonnam(mtmp) register struct monst *mtmp; {
+ return(xmonnam(mtmp, 1));
+}
+
+char *
+monnam(mtmp) register struct monst *mtmp; {
+ return(xmonnam(mtmp, 0));
+}
+
+char *
+Monnam(mtmp) register struct monst *mtmp; {
+register char *bp = monnam(mtmp);
+ if('a' <= *bp && *bp <= 'z') *bp += ('A' - 'a');
+ return(bp);
+}
+
+char *
+amonnam(mtmp,adj)
+register struct monst *mtmp;
+register char *adj;
+{
+ register char *bp = monnam(mtmp);
+ static char buf[BUFSZ]; /* %% */
+
+ if(!strncmp(bp, "the ", 4)) bp += 4;
+ (void) sprintf(buf, "the %s %s", adj, bp);
+ return(buf);
+}
+
+char *
+Amonnam(mtmp, adj)
+register struct monst *mtmp;
+register char *adj;
+{
+ register char *bp = amonnam(mtmp,adj);
+
+ *bp = 'T';
+ return(bp);
+}
+
+char *
+Xmonnam(mtmp) register struct monst *mtmp; {
+register char *bp = Monnam(mtmp);
+ if(!strncmp(bp, "The ", 4)) {
+ bp += 2;
+ *bp = 'A';
+ }
+ return(bp);
+}
+
+char *
+visctrl(c)
+char c;
+{
+static char ccc[3];
+ if(c < 040) {
+ ccc[0] = '^';
+ ccc[1] = c + 0100;
+ ccc[2] = 0;
+ } else {
+ ccc[0] = c;
+ ccc[1] = 0;
+ }
+ return(ccc);
+}
diff --git a/games/hack/hack.do_wear.c b/games/hack/hack.do_wear.c
new file mode 100644
index 000000000000..423955d2940a
--- /dev/null
+++ b/games/hack/hack.do_wear.c
@@ -0,0 +1,336 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.do_wear.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+extern char *nomovemsg;
+extern char quitchars[];
+extern char *Doname();
+
+off_msg(otmp) register struct obj *otmp; {
+ pline("You were wearing %s.", doname(otmp));
+}
+
+doremarm() {
+ register struct obj *otmp;
+ if(!uarm && !uarmh && !uarms && !uarmg) {
+ pline("Not wearing any armor.");
+ return(0);
+ }
+ otmp = (!uarmh && !uarms && !uarmg) ? uarm :
+ (!uarms && !uarm && !uarmg) ? uarmh :
+ (!uarmh && !uarm && !uarmg) ? uarms :
+ (!uarmh && !uarm && !uarms) ? uarmg :
+ getobj("[", "take off");
+ if(!otmp) return(0);
+ if(!(otmp->owornmask & (W_ARMOR - W_ARM2))) {
+ pline("You can't take that off.");
+ return(0);
+ }
+ if( otmp == uarmg && uwep && uwep->cursed ) { /* myers@uwmacc */
+ pline("You seem not able to take off the gloves while holding your weapon.");
+ return(0);
+ }
+ (void) armoroff(otmp);
+ return(1);
+}
+
+doremring() {
+ if(!uleft && !uright){
+ pline("Not wearing any ring.");
+ return(0);
+ }
+ if(!uleft)
+ return(dorr(uright));
+ if(!uright)
+ return(dorr(uleft));
+ if(uleft && uright) while(1) {
+ char answer;
+
+ pline("What ring, Right or Left? [ rl?]");
+ if(index(quitchars, (answer = readchar())))
+ return(0);
+ switch(answer) {
+ case 'l':
+ case 'L':
+ return(dorr(uleft));
+ case 'r':
+ case 'R':
+ return(dorr(uright));
+ case '?':
+ (void) doprring();
+ /* might look at morc here %% */
+ }
+ }
+ /* NOTREACHED */
+#ifdef lint
+ return(0);
+#endif lint
+}
+
+dorr(otmp) register struct obj *otmp; {
+ if(cursed(otmp)) return(0);
+ ringoff(otmp);
+ off_msg(otmp);
+ return(1);
+}
+
+cursed(otmp) register struct obj *otmp; {
+ if(otmp->cursed){
+ pline("You can't. It appears to be cursed.");
+ return(1);
+ }
+ return(0);
+}
+
+armoroff(otmp) register struct obj *otmp; {
+register int delay = -objects[otmp->otyp].oc_delay;
+ if(cursed(otmp)) return(0);
+ setworn((struct obj *) 0, otmp->owornmask & W_ARMOR);
+ if(delay) {
+ nomul(delay);
+ switch(otmp->otyp) {
+ case HELMET:
+ nomovemsg = "You finished taking off your helmet.";
+ break;
+ case PAIR_OF_GLOVES:
+ nomovemsg = "You finished taking off your gloves";
+ break;
+ default:
+ nomovemsg = "You finished taking off your suit.";
+ }
+ } else {
+ off_msg(otmp);
+ }
+ return(1);
+}
+
+doweararm() {
+ register struct obj *otmp;
+ register int delay;
+ register int err = 0;
+ long mask = 0;
+
+ otmp = getobj("[", "wear");
+ if(!otmp) return(0);
+ if(otmp->owornmask & W_ARMOR) {
+ pline("You are already wearing that!");
+ return(0);
+ }
+ if(otmp->otyp == HELMET){
+ if(uarmh) {
+ pline("You are already wearing a helmet.");
+ err++;
+ } else
+ mask = W_ARMH;
+ } else if(otmp->otyp == SHIELD){
+ if(uarms) pline("You are already wearing a shield."), err++;
+ if(uwep && uwep->otyp == TWO_HANDED_SWORD)
+ pline("You cannot wear a shield and wield a two-handed sword."), err++;
+ if(!err) mask = W_ARMS;
+ } else if(otmp->otyp == PAIR_OF_GLOVES) {
+ if(uarmg) {
+ pline("You are already wearing gloves.");
+ err++;
+ } else
+ if(uwep && uwep->cursed) {
+ pline("You cannot wear gloves over your weapon.");
+ err++;
+ } else
+ mask = W_ARMG;
+ } else {
+ if(uarm) {
+ if(otmp->otyp != ELVEN_CLOAK || uarm2) {
+ pline("You are already wearing some armor.");
+ err++;
+ }
+ }
+ if(!err) mask = W_ARM;
+ }
+ if(otmp == uwep && uwep->cursed) {
+ if(!err++)
+ pline("%s is welded to your hand.", Doname(uwep));
+ }
+ if(err) return(0);
+ setworn(otmp, mask);
+ if(otmp == uwep)
+ setuwep((struct obj *) 0);
+ delay = -objects[otmp->otyp].oc_delay;
+ if(delay){
+ nomul(delay);
+ nomovemsg = "You finished your dressing manoeuvre.";
+ }
+ otmp->known = 1;
+ return(1);
+}
+
+dowearring() {
+ register struct obj *otmp;
+ long mask = 0;
+ long oldprop;
+
+ if(uleft && uright){
+ pline("There are no more ring-fingers to fill.");
+ return(0);
+ }
+ otmp = getobj("=", "wear");
+ if(!otmp) return(0);
+ if(otmp->owornmask & W_RING) {
+ pline("You are already wearing that!");
+ return(0);
+ }
+ if(otmp == uleft || otmp == uright) {
+ pline("You are already wearing that.");
+ return(0);
+ }
+ if(otmp == uwep && uwep->cursed) {
+ pline("%s is welded to your hand.", Doname(uwep));
+ return(0);
+ }
+ if(uleft) mask = RIGHT_RING;
+ else if(uright) mask = LEFT_RING;
+ else do {
+ char answer;
+
+ pline("What ring-finger, Right or Left? ");
+ if(index(quitchars, (answer = readchar())))
+ return(0);
+ switch(answer){
+ case 'l':
+ case 'L':
+ mask = LEFT_RING;
+ break;
+ case 'r':
+ case 'R':
+ mask = RIGHT_RING;
+ break;
+ }
+ } while(!mask);
+ setworn(otmp, mask);
+ if(otmp == uwep)
+ setuwep((struct obj *) 0);
+ oldprop = u.uprops[PROP(otmp->otyp)].p_flgs;
+ u.uprops[PROP(otmp->otyp)].p_flgs |= mask;
+ switch(otmp->otyp){
+ case RIN_LEVITATION:
+ if(!oldprop) float_up();
+ break;
+ case RIN_PROTECTION_FROM_SHAPE_CHANGERS:
+ rescham();
+ break;
+ case RIN_GAIN_STRENGTH:
+ u.ustr += otmp->spe;
+ u.ustrmax += otmp->spe;
+ if(u.ustr > 118) u.ustr = 118;
+ if(u.ustrmax > 118) u.ustrmax = 118;
+ flags.botl = 1;
+ break;
+ case RIN_INCREASE_DAMAGE:
+ u.udaminc += otmp->spe;
+ break;
+ }
+ prinv(otmp);
+ return(1);
+}
+
+ringoff(obj)
+register struct obj *obj;
+{
+register long mask;
+ mask = obj->owornmask & W_RING;
+ setworn((struct obj *) 0, obj->owornmask);
+ if(!(u.uprops[PROP(obj->otyp)].p_flgs & mask))
+ impossible("Strange... I didnt know you had that ring.");
+ u.uprops[PROP(obj->otyp)].p_flgs &= ~mask;
+ switch(obj->otyp) {
+ case RIN_FIRE_RESISTANCE:
+ /* Bad luck if the player is in hell... --jgm */
+ if (!Fire_resistance && dlevel >= 30) {
+ pline("The flames of Hell burn you to a crisp.");
+ killer = "stupidity in hell";
+ done("burned");
+ }
+ break;
+ case RIN_LEVITATION:
+ if(!Levitation) { /* no longer floating */
+ float_down();
+ }
+ break;
+ case RIN_GAIN_STRENGTH:
+ u.ustr -= obj->spe;
+ u.ustrmax -= obj->spe;
+ if(u.ustr > 118) u.ustr = 118;
+ if(u.ustrmax > 118) u.ustrmax = 118;
+ flags.botl = 1;
+ break;
+ case RIN_INCREASE_DAMAGE:
+ u.udaminc -= obj->spe;
+ break;
+ }
+}
+
+find_ac(){
+register int uac = 10;
+ if(uarm) uac -= ARM_BONUS(uarm);
+ if(uarm2) uac -= ARM_BONUS(uarm2);
+ if(uarmh) uac -= ARM_BONUS(uarmh);
+ if(uarms) uac -= ARM_BONUS(uarms);
+ if(uarmg) uac -= ARM_BONUS(uarmg);
+ if(uleft && uleft->otyp == RIN_PROTECTION) uac -= uleft->spe;
+ if(uright && uright->otyp == RIN_PROTECTION) uac -= uright->spe;
+ if(uac != u.uac){
+ u.uac = uac;
+ flags.botl = 1;
+ }
+}
+
+glibr(){
+register struct obj *otmp;
+int xfl = 0;
+ if(!uarmg) if(uleft || uright) {
+ /* Note: at present also cursed rings fall off */
+ pline("Your %s off your fingers.",
+ (uleft && uright) ? "rings slip" : "ring slips");
+ xfl++;
+ if((otmp = uleft) != Null(obj)){
+ ringoff(uleft);
+ dropx(otmp);
+ }
+ if((otmp = uright) != Null(obj)){
+ ringoff(uright);
+ dropx(otmp);
+ }
+ }
+ if((otmp = uwep) != Null(obj)){
+ /* Note: at present also cursed weapons fall */
+ setuwep((struct obj *) 0);
+ dropx(otmp);
+ pline("Your weapon %sslips from your hands.",
+ xfl ? "also " : "");
+ }
+}
+
+struct obj *
+some_armor(){
+register struct obj *otmph = uarm;
+ if(uarmh && (!otmph || !rn2(4))) otmph = uarmh;
+ if(uarmg && (!otmph || !rn2(4))) otmph = uarmg;
+ if(uarms && (!otmph || !rn2(4))) otmph = uarms;
+ return(otmph);
+}
+
+corrode_armor(){
+register struct obj *otmph = some_armor();
+ if(otmph){
+ if(otmph->rustfree ||
+ otmph->otyp == ELVEN_CLOAK ||
+ otmph->otyp == LEATHER_ARMOR ||
+ otmph->otyp == STUDDED_LEATHER_ARMOR) {
+ pline("Your %s not affected!",
+ aobjnam(otmph, "are"));
+ return;
+ }
+ pline("Your %s!", aobjnam(otmph, "corrode"));
+ otmph->spe--;
+ }
+}
diff --git a/games/hack/hack.dog.c b/games/hack/hack.dog.c
new file mode 100644
index 000000000000..aa4387abbee9
--- /dev/null
+++ b/games/hack/hack.dog.c
@@ -0,0 +1,413 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.dog.c - version 1.0.3 */
+
+#include "hack.h"
+#include "hack.mfndpos.h"
+extern struct monst *makemon();
+#include "def.edog.h"
+#include "def.mkroom.h"
+
+struct permonst li_dog =
+ { "little dog", 'd',2,18,6,1,6,sizeof(struct edog) };
+struct permonst dog =
+ { "dog", 'd',4,16,5,1,6,sizeof(struct edog) };
+struct permonst la_dog =
+ { "large dog", 'd',6,15,4,2,4,sizeof(struct edog) };
+
+
+makedog(){
+register struct monst *mtmp = makemon(&li_dog,u.ux,u.uy);
+ if(!mtmp) return; /* dogs were genocided */
+ initedog(mtmp);
+}
+
+initedog(mtmp) register struct monst *mtmp; {
+ mtmp->mtame = mtmp->mpeaceful = 1;
+ EDOG(mtmp)->hungrytime = 1000 + moves;
+ EDOG(mtmp)->eattime = 0;
+ EDOG(mtmp)->droptime = 0;
+ EDOG(mtmp)->dropdist = 10000;
+ EDOG(mtmp)->apport = 10;
+ EDOG(mtmp)->whistletime = 0;
+}
+
+/* attach the monsters that went down (or up) together with @ */
+struct monst *mydogs = 0;
+struct monst *fallen_down = 0; /* monsters that fell through a trapdoor */
+ /* they will appear on the next level @ goes to, even if he goes up! */
+
+losedogs(){
+register struct monst *mtmp;
+ while(mtmp = mydogs){
+ mydogs = mtmp->nmon;
+ mtmp->nmon = fmon;
+ fmon = mtmp;
+ mnexto(mtmp);
+ }
+ while(mtmp = fallen_down){
+ fallen_down = mtmp->nmon;
+ mtmp->nmon = fmon;
+ fmon = mtmp;
+ rloc(mtmp);
+ }
+}
+
+keepdogs(){
+register struct monst *mtmp;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(dist(mtmp->mx,mtmp->my) < 3 && follower(mtmp)
+ && !mtmp->msleep && !mtmp->mfroz) {
+ relmon(mtmp);
+ mtmp->nmon = mydogs;
+ mydogs = mtmp;
+ unpmon(mtmp);
+ keepdogs(); /* we destroyed the link, so use recursion */
+ return; /* (admittedly somewhat primitive) */
+ }
+}
+
+fall_down(mtmp) register struct monst *mtmp; {
+ relmon(mtmp);
+ mtmp->nmon = fallen_down;
+ fallen_down = mtmp;
+ unpmon(mtmp);
+ mtmp->mtame = 0;
+}
+
+/* return quality of food; the lower the better */
+#define DOGFOOD 0
+#define CADAVER 1
+#define ACCFOOD 2
+#define MANFOOD 3
+#define APPORT 4
+#define POISON 5
+#define UNDEF 6
+dogfood(obj) register struct obj *obj; {
+ switch(obj->olet) {
+ case FOOD_SYM:
+ return(
+ (obj->otyp == TRIPE_RATION) ? DOGFOOD :
+ (obj->otyp < CARROT) ? ACCFOOD :
+ (obj->otyp < CORPSE) ? MANFOOD :
+ (poisonous(obj) || obj->age + 50 <= moves ||
+ obj->otyp == DEAD_COCKATRICE)
+ ? POISON : CADAVER
+ );
+ default:
+ if(!obj->cursed) return(APPORT);
+ /* fall into next case */
+ case BALL_SYM:
+ case CHAIN_SYM:
+ case ROCK_SYM:
+ return(UNDEF);
+ }
+}
+
+/* return 0 (no move), 1 (move) or 2 (dead) */
+dog_move(mtmp, after) register struct monst *mtmp; {
+register int nx,ny,omx,omy,appr,nearer,j;
+int udist,chi,i,whappr;
+register struct monst *mtmp2;
+register struct permonst *mdat = mtmp->data;
+register struct edog *edog = EDOG(mtmp);
+struct obj *obj;
+struct trap *trap;
+xchar cnt,chcnt,nix,niy;
+schar dogroom,uroom;
+xchar gx,gy,gtyp,otyp; /* current goal */
+coord poss[9];
+int info[9];
+#define GDIST(x,y) ((x-gx)*(x-gx) + (y-gy)*(y-gy))
+#define DDIST(x,y) ((x-omx)*(x-omx) + (y-omy)*(y-omy))
+
+ if(moves <= edog->eattime) return(0); /* dog is still eating */
+ omx = mtmp->mx;
+ omy = mtmp->my;
+ whappr = (moves - EDOG(mtmp)->whistletime < 5);
+ if(moves > edog->hungrytime + 500 && !mtmp->mconf){
+ mtmp->mconf = 1;
+ mtmp->mhpmax /= 3;
+ if(mtmp->mhp > mtmp->mhpmax)
+ mtmp->mhp = mtmp->mhpmax;
+ if(cansee(omx,omy))
+ pline("%s is confused from hunger.", Monnam(mtmp));
+ else pline("You feel worried about %s.", monnam(mtmp));
+ } else
+ if(moves > edog->hungrytime + 750 || mtmp->mhp < 1){
+ if(cansee(omx,omy))
+ pline("%s dies from hunger.", Monnam(mtmp));
+ else
+ pline("You have a sad feeling for a moment, then it passes.");
+ mondied(mtmp);
+ return(2);
+ }
+ dogroom = inroom(omx,omy);
+ uroom = inroom(u.ux,u.uy);
+ udist = dist(omx,omy);
+
+ /* maybe we tamed him while being swallowed --jgm */
+ if(!udist) return(0);
+
+ /* if we are carrying sth then we drop it (perhaps near @) */
+ /* Note: if apport == 1 then our behaviour is independent of udist */
+ if(mtmp->minvent){
+ if(!rn2(udist) || !rn2((int) edog->apport))
+ if(rn2(10) < edog->apport){
+ relobj(mtmp, (int) mtmp->minvis);
+ if(edog->apport > 1) edog->apport--;
+ edog->dropdist = udist; /* hpscdi!jon */
+ edog->droptime = moves;
+ }
+ } else {
+ if(obj = o_at(omx,omy)) if(!index("0_", obj->olet)){
+ if((otyp = dogfood(obj)) <= CADAVER){
+ nix = omx;
+ niy = omy;
+ goto eatobj;
+ }
+ if(obj->owt < 10*mtmp->data->mlevel)
+ if(rn2(20) < edog->apport+3)
+ if(rn2(udist) || !rn2((int) edog->apport)){
+ freeobj(obj);
+ unpobj(obj);
+ /* if(levl[omx][omy].scrsym == obj->olet)
+ newsym(omx,omy); */
+ mpickobj(mtmp,obj);
+ }
+ }
+ }
+
+ /* first we look for food */
+ gtyp = UNDEF; /* no goal as yet */
+#ifdef LINT
+ gx = gy = 0; /* suppress 'used before set' message */
+#endif LINT
+ for(obj = fobj; obj; obj = obj->nobj) {
+ otyp = dogfood(obj);
+ if(otyp > gtyp || otyp == UNDEF) continue;
+ if(inroom(obj->ox,obj->oy) != dogroom) continue;
+ if(otyp < MANFOOD &&
+ (dogroom >= 0 || DDIST(obj->ox,obj->oy) < 10)) {
+ if(otyp < gtyp || (otyp == gtyp &&
+ DDIST(obj->ox,obj->oy) < DDIST(gx,gy))){
+ gx = obj->ox;
+ gy = obj->oy;
+ gtyp = otyp;
+ }
+ } else
+ if(gtyp == UNDEF && dogroom >= 0 &&
+ uroom == dogroom &&
+ !mtmp->minvent && edog->apport > rn2(8)){
+ gx = obj->ox;
+ gy = obj->oy;
+ gtyp = APPORT;
+ }
+ }
+ if(gtyp == UNDEF ||
+ (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)){
+ if(dogroom < 0 || dogroom == uroom){
+ gx = u.ux;
+ gy = u.uy;
+#ifndef QUEST
+ } else {
+ int tmp = rooms[dogroom].fdoor;
+ cnt = rooms[dogroom].doorct;
+
+ gx = gy = FAR; /* random, far away */
+ while(cnt--){
+ if(dist(gx,gy) >
+ dist(doors[tmp].x, doors[tmp].y)){
+ gx = doors[tmp].x;
+ gy = doors[tmp].y;
+ }
+ tmp++;
+ }
+ /* here gx == FAR e.g. when dog is in a vault */
+ if(gx == FAR || (gx == omx && gy == omy)){
+ gx = u.ux;
+ gy = u.uy;
+ }
+#endif QUEST
+ }
+ appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
+ if(after && udist <= 4 && gx == u.ux && gy == u.uy)
+ return(0);
+ if(udist > 1){
+ if(!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
+ whappr ||
+ (mtmp->minvent && rn2((int) edog->apport)))
+ appr = 1;
+ }
+ /* if you have dog food he'll follow you more closely */
+ if(appr == 0){
+ obj = invent;
+ while(obj){
+ if(obj->otyp == TRIPE_RATION){
+ appr = 1;
+ break;
+ }
+ obj = obj->nobj;
+ }
+ }
+ } else appr = 1; /* gtyp != UNDEF */
+ if(mtmp->mconf) appr = 0;
+
+ if(gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)){
+ extern coord *gettrack();
+ register coord *cp;
+ cp = gettrack(omx,omy);
+ if(cp){
+ gx = cp->x;
+ gy = cp->y;
+ }
+ }
+
+ nix = omx;
+ niy = omy;
+ cnt = mfndpos(mtmp,poss,info,ALLOW_M | ALLOW_TRAPS);
+ chcnt = 0;
+ chi = -1;
+ for(i=0; i<cnt; i++){
+ nx = poss[i].x;
+ ny = poss[i].y;
+ if(info[i] & ALLOW_M){
+ mtmp2 = m_at(nx,ny);
+ if(mtmp2->data->mlevel >= mdat->mlevel+2 ||
+ mtmp2->data->mlet == 'c')
+ continue;
+ if(after) return(0); /* hit only once each move */
+
+ if(hitmm(mtmp, mtmp2) == 1 && rn2(4) &&
+ mtmp2->mlstmv != moves &&
+ hitmm(mtmp2,mtmp) == 2) return(2);
+ return(0);
+ }
+
+ /* dog avoids traps */
+ /* but perhaps we have to pass a trap in order to follow @ */
+ if((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))){
+ if(!trap->tseen && rn2(40)) continue;
+ if(rn2(10)) continue;
+ }
+
+ /* dog eschewes cursed objects */
+ /* but likes dog food */
+ obj = fobj;
+ while(obj){
+ if(obj->ox != nx || obj->oy != ny)
+ goto nextobj;
+ if(obj->cursed) goto nxti;
+ if(obj->olet == FOOD_SYM &&
+ (otyp = dogfood(obj)) < MANFOOD &&
+ (otyp < ACCFOOD || edog->hungrytime <= moves)){
+ /* Note: our dog likes the food so much that he
+ might eat it even when it conceals a cursed object */
+ nix = nx;
+ niy = ny;
+ chi = i;
+ eatobj:
+ edog->eattime =
+ moves + obj->quan * objects[obj->otyp].oc_delay;
+ if(edog->hungrytime < moves)
+ edog->hungrytime = moves;
+ edog->hungrytime +=
+ 5*obj->quan * objects[obj->otyp].nutrition;
+ mtmp->mconf = 0;
+ if(cansee(nix,niy))
+ pline("%s ate %s.", Monnam(mtmp), doname(obj));
+ /* perhaps this was a reward */
+ if(otyp != CADAVER)
+ edog->apport += 200/(edog->dropdist+moves-edog->droptime);
+ delobj(obj);
+ goto newdogpos;
+ }
+ nextobj:
+ obj = obj->nobj;
+ }
+
+ for(j=0; j<MTSZ && j<cnt-1; j++)
+ if(nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
+ if(rn2(4*(cnt-j))) goto nxti;
+
+/* Some stupid C compilers cannot compute the whole expression at once. */
+ nearer = GDIST(nx,ny);
+ nearer -= GDIST(nix,niy);
+ nearer *= appr;
+ if((nearer == 0 && !rn2(++chcnt)) || nearer<0 ||
+ (nearer > 0 && !whappr &&
+ ((omx == nix && omy == niy && !rn2(3))
+ || !rn2(12))
+ )){
+ nix = nx;
+ niy = ny;
+ if(nearer < 0) chcnt = 0;
+ chi = i;
+ }
+ nxti: ;
+ }
+newdogpos:
+ if(nix != omx || niy != omy){
+ if(info[chi] & ALLOW_U){
+ (void) hitu(mtmp, d(mdat->damn, mdat->damd)+1);
+ return(0);
+ }
+ mtmp->mx = nix;
+ mtmp->my = niy;
+ for(j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1];
+ mtmp->mtrack[0].x = omx;
+ mtmp->mtrack[0].y = omy;
+ }
+ if(mintrap(mtmp) == 2) /* he died */
+ return(2);
+ pmon(mtmp);
+ return(1);
+}
+
+/* return roomnumber or -1 */
+inroom(x,y) xchar x,y; {
+#ifndef QUEST
+ register struct mkroom *croom = &rooms[0];
+ while(croom->hx >= 0){
+ if(croom->hx >= x-1 && croom->lx <= x+1 &&
+ croom->hy >= y-1 && croom->ly <= y+1)
+ return(croom - rooms);
+ croom++;
+ }
+#endif QUEST
+ return(-1); /* not in room or on door */
+}
+
+tamedog(mtmp, obj)
+register struct monst *mtmp;
+register struct obj *obj;
+{
+ register struct monst *mtmp2;
+
+ if(flags.moonphase == FULL_MOON && night() && rn2(6))
+ return(0);
+
+ /* If we cannot tame him, at least he's no longer afraid. */
+ mtmp->mflee = 0;
+ mtmp->mfleetim = 0;
+ if(mtmp->mtame || mtmp->mfroz ||
+#ifndef NOWORM
+ mtmp->wormno ||
+#endif NOWORM
+ mtmp->isshk || mtmp->isgd || index(" &@12", mtmp->data->mlet))
+ return(0); /* no tame long worms? */
+ if(obj) {
+ if(dogfood(obj) >= MANFOOD) return(0);
+ if(cansee(mtmp->mx,mtmp->my)){
+ pline("%s devours the %s.", Monnam(mtmp),
+ objects[obj->otyp].oc_name);
+ }
+ obfree(obj, (struct obj *) 0);
+ }
+ mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
+ *mtmp2 = *mtmp;
+ mtmp2->mxlth = sizeof(struct edog);
+ if(mtmp->mnamelth) (void) strcpy(NAME(mtmp2), NAME(mtmp));
+ initedog(mtmp2);
+ replmon(mtmp,mtmp2);
+ return(1);
+}
diff --git a/games/hack/hack.eat.c b/games/hack/hack.eat.c
new file mode 100644
index 000000000000..f1a767779af1
--- /dev/null
+++ b/games/hack/hack.eat.c
@@ -0,0 +1,459 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.eat.c - version 1.0.3 */
+
+#include "hack.h"
+char POISONOUS[] = "ADKSVabhks";
+extern char *nomovemsg;
+extern int (*afternmv)();
+extern int (*occupation)();
+extern char *occtxt;
+extern struct obj *splitobj(), *addinv();
+
+/* hunger texts used on bottom line (each 8 chars long) */
+#define SATIATED 0
+#define NOT_HUNGRY 1
+#define HUNGRY 2
+#define WEAK 3
+#define FAINTING 4
+#define FAINTED 5
+#define STARVED 6
+
+char *hu_stat[] = {
+ "Satiated",
+ " ",
+ "Hungry ",
+ "Weak ",
+ "Fainting",
+ "Fainted ",
+ "Starved "
+};
+
+init_uhunger(){
+ u.uhunger = 900;
+ u.uhs = NOT_HUNGRY;
+}
+
+#define TTSZ SIZE(tintxts)
+struct { char *txt; int nut; } tintxts[] = {
+ "It contains first quality peaches - what a surprise!", 40,
+ "It contains salmon - not bad!", 60,
+ "It contains apple juice - perhaps not what you hoped for.", 20,
+ "It contains some nondescript substance, tasting awfully.", 500,
+ "It contains rotten meat. You vomit.", -50,
+ "It turns out to be empty.", 0
+};
+
+static struct {
+ struct obj *tin;
+ int usedtime, reqtime;
+} tin;
+
+opentin(){
+ register int r;
+
+ if(!carried(tin.tin)) /* perhaps it was stolen? */
+ return(0); /* %% probably we should use tinoid */
+ if(tin.usedtime++ >= 50) {
+ pline("You give up your attempt to open the tin.");
+ return(0);
+ }
+ if(tin.usedtime < tin.reqtime)
+ return(1); /* still busy */
+
+ pline("You succeed in opening the tin.");
+ useup(tin.tin);
+ r = rn2(2*TTSZ);
+ if(r < TTSZ){
+ pline(tintxts[r].txt);
+ lesshungry(tintxts[r].nut);
+ if(r == 1) /* SALMON */ {
+ Glib = rnd(15);
+ pline("Eating salmon made your fingers very slippery.");
+ }
+ } else {
+ pline("It contains spinach - this makes you feel like Popeye!");
+ lesshungry(600);
+ if(u.ustr < 118)
+ u.ustr += rnd( ((u.ustr < 17) ? 19 : 118) - u.ustr);
+ if(u.ustr > u.ustrmax) u.ustrmax = u.ustr;
+ flags.botl = 1;
+ }
+ return(0);
+}
+
+Meatdone(){
+ u.usym = '@';
+ prme();
+}
+
+doeat(){
+ register struct obj *otmp;
+ register struct objclass *ftmp;
+ register tmp;
+
+ /* Is there some food (probably a heavy corpse) here on the ground? */
+ if(!Levitation)
+ for(otmp = fobj; otmp; otmp = otmp->nobj) {
+ if(otmp->ox == u.ux && otmp->oy == u.uy &&
+ otmp->olet == FOOD_SYM) {
+ pline("There %s %s here; eat %s? [ny] ",
+ (otmp->quan == 1) ? "is" : "are",
+ doname(otmp),
+ (otmp->quan == 1) ? "it" : "one");
+ if(readchar() == 'y') {
+ if(otmp->quan != 1)
+ (void) splitobj(otmp, 1);
+ freeobj(otmp);
+ otmp = addinv(otmp);
+ addtobill(otmp);
+ goto gotit;
+ }
+ }
+ }
+ otmp = getobj("%", "eat");
+ if(!otmp) return(0);
+gotit:
+ if(otmp->otyp == TIN){
+ if(uwep) {
+ switch(uwep->otyp) {
+ case CAN_OPENER:
+ tmp = 1;
+ break;
+ case DAGGER:
+ case CRYSKNIFE:
+ tmp = 3;
+ break;
+ case PICK_AXE:
+ case AXE:
+ tmp = 6;
+ break;
+ default:
+ goto no_opener;
+ }
+ pline("Using your %s you try to open the tin.",
+ aobjnam(uwep, (char *) 0));
+ } else {
+ no_opener:
+ pline("It is not so easy to open this tin.");
+ if(Glib) {
+ pline("The tin slips out of your hands.");
+ if(otmp->quan > 1) {
+ register struct obj *obj;
+ extern struct obj *splitobj();
+
+ obj = splitobj(otmp, 1);
+ if(otmp == uwep) setuwep(obj);
+ }
+ dropx(otmp);
+ return(1);
+ }
+ tmp = 10 + rn2(1 + 500/((int)(u.ulevel + u.ustr)));
+ }
+ tin.reqtime = tmp;
+ tin.usedtime = 0;
+ tin.tin = otmp;
+ occupation = opentin;
+ occtxt = "opening the tin";
+ return(1);
+ }
+ ftmp = &objects[otmp->otyp];
+ multi = -ftmp->oc_delay;
+ if(otmp->otyp >= CORPSE && eatcorpse(otmp)) goto eatx;
+ if(!rn2(7) && otmp->otyp != FORTUNE_COOKIE) {
+ pline("Blecch! Rotten food!");
+ if(!rn2(4)) {
+ pline("You feel rather light headed.");
+ Confusion += d(2,4);
+ } else if(!rn2(4)&& !Blind) {
+ pline("Everything suddenly goes dark.");
+ Blind = d(2,10);
+ seeoff(0);
+ } else if(!rn2(3)) {
+ if(Blind)
+ pline("The world spins and you slap against the floor.");
+ else
+ pline("The world spins and goes dark.");
+ nomul(-rnd(10));
+ nomovemsg = "You are conscious again.";
+ }
+ lesshungry(ftmp->nutrition / 4);
+ } else {
+ if(u.uhunger >= 1500) {
+ pline("You choke over your food.");
+ pline("You die...");
+ killer = ftmp->oc_name;
+ done("choked");
+ }
+ switch(otmp->otyp){
+ case FOOD_RATION:
+ if(u.uhunger <= 200)
+ pline("That food really hit the spot!");
+ else if(u.uhunger <= 700)
+ pline("That satiated your stomach!");
+ else {
+ pline("You're having a hard time getting all that food down.");
+ multi -= 2;
+ }
+ lesshungry(ftmp->nutrition);
+ if(multi < 0) nomovemsg = "You finished your meal.";
+ break;
+ case TRIPE_RATION:
+ pline("Yak - dog food!");
+ more_experienced(1,0);
+ flags.botl = 1;
+ if(rn2(2)){
+ pline("You vomit.");
+ morehungry(20);
+ if(Sick) {
+ Sick = 0; /* David Neves */
+ pline("What a relief!");
+ }
+ } else lesshungry(ftmp->nutrition);
+ break;
+ default:
+ if(otmp->otyp >= CORPSE)
+ pline("That %s tasted terrible!",ftmp->oc_name);
+ else
+ pline("That %s was delicious!",ftmp->oc_name);
+ lesshungry(ftmp->nutrition);
+ if(otmp->otyp == DEAD_LIZARD && (Confusion > 2))
+ Confusion = 2;
+ else
+#ifdef QUEST
+ if(otmp->otyp == CARROT && !Blind){
+ u.uhorizon++;
+ setsee();
+ pline("Your vision improves.");
+ } else
+#endif QUEST
+ if(otmp->otyp == FORTUNE_COOKIE) {
+ if(Blind) {
+ pline("This cookie has a scrap of paper inside!");
+ pline("What a pity, that you cannot read it!");
+ } else
+ outrumor();
+ } else
+ if(otmp->otyp == LUMP_OF_ROYAL_JELLY) {
+ /* This stuff seems to be VERY healthy! */
+ if(u.ustrmax < 118) u.ustrmax++;
+ if(u.ustr < u.ustrmax) u.ustr++;
+ u.uhp += rnd(20);
+ if(u.uhp > u.uhpmax) {
+ if(!rn2(17)) u.uhpmax++;
+ u.uhp = u.uhpmax;
+ }
+ heal_legs();
+ }
+ break;
+ }
+ }
+eatx:
+ if(multi<0 && !nomovemsg){
+ static char msgbuf[BUFSZ];
+ (void) sprintf(msgbuf, "You finished eating the %s.",
+ ftmp->oc_name);
+ nomovemsg = msgbuf;
+ }
+ useup(otmp);
+ return(1);
+}
+
+/* called in hack.main.c */
+gethungry(){
+ --u.uhunger;
+ if(moves % 2) {
+ if(Regeneration) u.uhunger--;
+ if(Hunger) u.uhunger--;
+ /* a3: if(Hunger & LEFT_RING) u.uhunger--;
+ if(Hunger & RIGHT_RING) u.uhunger--;
+ etc. */
+ }
+ if(moves % 20 == 0) { /* jimt@asgb */
+ if(uleft) u.uhunger--;
+ if(uright) u.uhunger--;
+ }
+ newuhs(TRUE);
+}
+
+/* called after vomiting and after performing feats of magic */
+morehungry(num) register num; {
+ u.uhunger -= num;
+ newuhs(TRUE);
+}
+
+/* called after eating something (and after drinking fruit juice) */
+lesshungry(num) register num; {
+ u.uhunger += num;
+ newuhs(FALSE);
+}
+
+unfaint(){
+ u.uhs = FAINTING;
+ flags.botl = 1;
+}
+
+newuhs(incr) boolean incr; {
+ register int newhs, h = u.uhunger;
+
+ newhs = (h > 1000) ? SATIATED :
+ (h > 150) ? NOT_HUNGRY :
+ (h > 50) ? HUNGRY :
+ (h > 0) ? WEAK : FAINTING;
+
+ if(newhs == FAINTING) {
+ if(u.uhs == FAINTED)
+ newhs = FAINTED;
+ if(u.uhs <= WEAK || rn2(20-u.uhunger/10) >= 19) {
+ if(u.uhs != FAINTED && multi >= 0 /* %% */) {
+ pline("You faint from lack of food.");
+ nomul(-10+(u.uhunger/10));
+ nomovemsg = "You regain consciousness.";
+ afternmv = unfaint;
+ newhs = FAINTED;
+ }
+ } else
+ if(u.uhunger < -(int)(200 + 25*u.ulevel)) {
+ u.uhs = STARVED;
+ flags.botl = 1;
+ bot();
+ pline("You die from starvation.");
+ done("starved");
+ }
+ }
+
+ if(newhs != u.uhs) {
+ if(newhs >= WEAK && u.uhs < WEAK)
+ losestr(1); /* this may kill you -- see below */
+ else
+ if(newhs < WEAK && u.uhs >= WEAK && u.ustr < u.ustrmax)
+ losestr(-1);
+ switch(newhs){
+ case HUNGRY:
+ pline((!incr) ? "You only feel hungry now." :
+ (u.uhunger < 145) ? "You feel hungry." :
+ "You are beginning to feel hungry.");
+ break;
+ case WEAK:
+ pline((!incr) ? "You feel weak now." :
+ (u.uhunger < 45) ? "You feel weak." :
+ "You are beginning to feel weak.");
+ break;
+ }
+ u.uhs = newhs;
+ flags.botl = 1;
+ if(u.uhp < 1) {
+ pline("You die from hunger and exhaustion.");
+ killer = "exhaustion";
+ done("starved");
+ }
+ }
+}
+
+#define CORPSE_I_TO_C(otyp) (char) ((otyp >= DEAD_ACID_BLOB)\
+ ? 'a' + (otyp - DEAD_ACID_BLOB)\
+ : '@' + (otyp - DEAD_HUMAN))
+poisonous(otmp)
+register struct obj *otmp;
+{
+ return(index(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0);
+}
+
+/* returns 1 if some text was printed */
+eatcorpse(otmp) register struct obj *otmp; {
+register char let = CORPSE_I_TO_C(otmp->otyp);
+register tp = 0;
+ if(let != 'a' && moves > otmp->age + 50 + rn2(100)) {
+ tp++;
+ pline("Ulch -- that meat was tainted!");
+ pline("You get very sick.");
+ Sick = 10 + rn2(10);
+ u.usick_cause = objects[otmp->otyp].oc_name;
+ } else if(index(POISONOUS, let) && rn2(5)){
+ tp++;
+ pline("Ecch -- that must have been poisonous!");
+ if(!Poison_resistance){
+ losestr(rnd(4));
+ losehp(rnd(15), "poisonous corpse");
+ } else
+ pline("You don't seem affected by the poison.");
+ } else if(index("ELNOPQRUuxz", let) && rn2(5)){
+ tp++;
+ pline("You feel sick.");
+ losehp(rnd(8), "cadaver");
+ }
+ switch(let) {
+ case 'L':
+ case 'N':
+ case 't':
+ Teleportation |= INTRINSIC;
+ break;
+ case 'W':
+ pluslvl();
+ break;
+ case 'n':
+ u.uhp = u.uhpmax;
+ flags.botl = 1;
+ /* fall into next case */
+ case '@':
+ pline("You cannibal! You will be sorry for this!");
+ /* not tp++; */
+ /* fall into next case */
+ case 'd':
+ Aggravate_monster |= INTRINSIC;
+ break;
+ case 'I':
+ if(!Invis) {
+ Invis = 50+rn2(100);
+ if(!See_invisible)
+ newsym(u.ux, u.uy);
+ } else {
+ Invis |= INTRINSIC;
+ See_invisible |= INTRINSIC;
+ }
+ /* fall into next case */
+ case 'y':
+#ifdef QUEST
+ u.uhorizon++;
+#endif QUEST
+ /* fall into next case */
+ case 'B':
+ Confusion = 50;
+ break;
+ case 'D':
+ Fire_resistance |= INTRINSIC;
+ break;
+ case 'E':
+ Telepat |= INTRINSIC;
+ break;
+ case 'F':
+ case 'Y':
+ Cold_resistance |= INTRINSIC;
+ break;
+ case 'k':
+ case 's':
+ Poison_resistance |= INTRINSIC;
+ break;
+ case 'c':
+ pline("You turn to stone.");
+ killer = "dead cockatrice";
+ done("died");
+ /* NOTREACHED */
+ case 'a':
+ if(Stoned) {
+ pline("What a pity - you just destroyed a future piece of art!");
+ tp++;
+ Stoned = 0;
+ }
+ break;
+ case 'M':
+ pline("You cannot resist the temptation to mimic a treasure chest.");
+ tp++;
+ nomul(-30);
+ afternmv = Meatdone;
+ nomovemsg = "You now again prefer mimicking a human.";
+ u.usym = '$';
+ prme();
+ break;
+ }
+ return(tp);
+}
diff --git a/games/hack/hack.end.c b/games/hack/hack.end.c
new file mode 100644
index 000000000000..0ddd2bd0edb7
--- /dev/null
+++ b/games/hack/hack.end.c
@@ -0,0 +1,642 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.end.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+#include <signal.h>
+#define Sprintf (void) sprintf
+extern char plname[], pl_character[];
+extern char *itoa(), *ordin(), *eos();
+
+xchar maxdlevel = 1;
+
+void
+done1()
+{
+ (void) signal(SIGINT,SIG_IGN);
+ pline("Really quit?");
+ if(readchar() != 'y') {
+ (void) signal(SIGINT,done1);
+ clrlin();
+ (void) fflush(stdout);
+ if(multi > 0) nomul(0);
+ return;
+ }
+ done("quit");
+ /* NOTREACHED */
+}
+
+int done_stopprint;
+int done_hup;
+
+void
+done_intr(){
+ done_stopprint++;
+ (void) signal(SIGINT, SIG_IGN);
+ (void) signal(SIGQUIT, SIG_IGN);
+}
+
+void
+done_hangup(){
+ done_hup++;
+ (void) signal(SIGHUP, SIG_IGN);
+ done_intr();
+}
+
+done_in_by(mtmp) register struct monst *mtmp; {
+static char buf[BUFSZ];
+ pline("You die ...");
+ if(mtmp->data->mlet == ' '){
+ Sprintf(buf, "the ghost of %s", (char *) mtmp->mextra);
+ killer = buf;
+ } else if(mtmp->mnamelth) {
+ Sprintf(buf, "%s called %s",
+ mtmp->data->mname, NAME(mtmp));
+ killer = buf;
+ } else if(mtmp->minvis) {
+ Sprintf(buf, "invisible %s", mtmp->data->mname);
+ killer = buf;
+ } else killer = mtmp->data->mname;
+ done("died");
+}
+
+/* called with arg "died", "drowned", "escaped", "quit", "choked", "panicked",
+ "burned", "starved" or "tricked" */
+/* Be careful not to call panic from here! */
+done(st1)
+register char *st1;
+{
+
+#ifdef WIZARD
+ if(wizard && *st1 == 'd'){
+ u.uswldtim = 0;
+ if(u.uhpmax < 0) u.uhpmax = 100; /* arbitrary */
+ u.uhp = u.uhpmax;
+ pline("For some reason you are still alive.");
+ flags.move = 0;
+ if(multi > 0) multi = 0; else multi = -1;
+ flags.botl = 1;
+ return;
+ }
+#endif WIZARD
+ (void) signal(SIGINT, done_intr);
+ (void) signal(SIGQUIT, done_intr);
+ (void) signal(SIGHUP, done_hangup);
+ if(*st1 == 'q' && u.uhp < 1){
+ st1 = "died";
+ killer = "quit while already on Charon's boat";
+ }
+ if(*st1 == 's') killer = "starvation"; else
+ if(*st1 == 'd' && st1[1] == 'r') killer = "drowning"; else
+ if(*st1 == 'p') killer = "panic"; else
+ if(*st1 == 't') killer = "trickery"; else
+ if(!index("bcd", *st1)) killer = st1;
+ paybill();
+ clearlocks();
+ if(flags.toplin == 1) more();
+ if(index("bcds", *st1)){
+#ifdef WIZARD
+ if(!wizard)
+#endif WIZARD
+ savebones();
+ if(!flags.notombstone)
+ outrip();
+ }
+ if(*st1 == 'c') killer = st1; /* after outrip() */
+ settty((char *) 0); /* does a clear_screen() */
+ if(!done_stopprint)
+ printf("Goodbye %s %s...\n\n", pl_character, plname);
+ { long int tmp;
+ tmp = u.ugold - u.ugold0;
+ if(tmp < 0)
+ tmp = 0;
+ if(*st1 == 'd' || *st1 == 'b')
+ tmp -= tmp/10;
+ u.urexp += tmp;
+ u.urexp += 50 * maxdlevel;
+ if(maxdlevel > 20)
+ u.urexp += 1000*((maxdlevel > 30) ? 10 : maxdlevel - 20);
+ }
+ if(*st1 == 'e') {
+ extern struct monst *mydogs;
+ register struct monst *mtmp;
+ register struct obj *otmp;
+ register int i;
+ register unsigned worthlessct = 0;
+ boolean has_amulet = FALSE;
+
+ killer = st1;
+ keepdogs();
+ mtmp = mydogs;
+ if(mtmp) {
+ if(!done_stopprint) printf("You");
+ while(mtmp) {
+ if(!done_stopprint)
+ printf(" and %s", monnam(mtmp));
+ if(mtmp->mtame)
+ u.urexp += mtmp->mhp;
+ mtmp = mtmp->nmon;
+ }
+ if(!done_stopprint)
+ printf("\nescaped from the dungeon with %ld points,\n",
+ u.urexp);
+ } else
+ if(!done_stopprint)
+ printf("You escaped from the dungeon with %ld points,\n",
+ u.urexp);
+ for(otmp = invent; otmp; otmp = otmp->nobj) {
+ if(otmp->olet == GEM_SYM){
+ objects[otmp->otyp].oc_name_known = 1;
+ i = otmp->quan*objects[otmp->otyp].g_val;
+ if(i == 0) {
+ worthlessct += otmp->quan;
+ continue;
+ }
+ u.urexp += i;
+ if(!done_stopprint)
+ printf("\t%s (worth %d Zorkmids),\n",
+ doname(otmp), i);
+ } else if(otmp->olet == AMULET_SYM) {
+ otmp->known = 1;
+ i = (otmp->spe < 0) ? 2 : 5000;
+ u.urexp += i;
+ if(!done_stopprint)
+ printf("\t%s (worth %d Zorkmids),\n",
+ doname(otmp), i);
+ if(otmp->spe >= 0) {
+ has_amulet = TRUE;
+ killer = "escaped (with amulet)";
+ }
+ }
+ }
+ if(worthlessct) if(!done_stopprint)
+ printf("\t%u worthless piece%s of coloured glass,\n",
+ worthlessct, plur(worthlessct));
+ if(has_amulet) u.urexp *= 2;
+ } else
+ if(!done_stopprint)
+ printf("You %s on dungeon level %d with %ld points,\n",
+ st1, dlevel, u.urexp);
+ if(!done_stopprint)
+ printf("and %ld piece%s of gold, after %ld move%s.\n",
+ u.ugold, plur(u.ugold), moves, plur(moves));
+ if(!done_stopprint)
+ printf("You were level %u with a maximum of %d hit points when you %s.\n",
+ u.ulevel, u.uhpmax, st1);
+ if(*st1 == 'e' && !done_stopprint){
+ getret(); /* all those pieces of coloured glass ... */
+ cls();
+ }
+#ifdef WIZARD
+ if(!wizard)
+#endif WIZARD
+ topten();
+ if(done_stopprint) printf("\n\n");
+ exit(0);
+}
+
+#define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry))
+#define NAMSZ 8
+#define DTHSZ 40
+#define PERSMAX 1
+#define POINTSMIN 1 /* must be > 0 */
+#define ENTRYMAX 100 /* must be >= 10 */
+#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */
+struct toptenentry {
+ struct toptenentry *tt_next;
+ long int points;
+ int level,maxlvl,hp,maxhp;
+ int uid;
+ char plchar;
+ char sex;
+ char name[NAMSZ+1];
+ char death[DTHSZ+1];
+ char date[7]; /* yymmdd */
+} *tt_head;
+
+topten(){
+ int uid = getuid();
+ int rank, rank0 = -1, rank1 = 0;
+ int occ_cnt = PERSMAX;
+ register struct toptenentry *t0, *t1, *tprev;
+ char *recfile = RECORD;
+ char *reclock = "record_lock";
+ int sleepct = 300;
+ FILE *rfile;
+ register flg = 0;
+ extern char *getdate();
+#define HUP if(!done_hup)
+ while(link(recfile, reclock) == -1) {
+ HUP perror(reclock);
+ if(!sleepct--) {
+ HUP puts("I give up. Sorry.");
+ HUP puts("Perhaps there is an old record_lock around?");
+ return;
+ }
+ HUP printf("Waiting for access to record file. (%d)\n",
+ sleepct);
+ HUP (void) fflush(stdout);
+ sleep(1);
+ }
+ if(!(rfile = fopen(recfile,"r"))){
+ HUP puts("Cannot open record file!");
+ goto unlock;
+ }
+ HUP (void) putchar('\n');
+
+ /* create a new 'topten' entry */
+ t0 = newttentry();
+ t0->level = dlevel;
+ t0->maxlvl = maxdlevel;
+ t0->hp = u.uhp;
+ t0->maxhp = u.uhpmax;
+ t0->points = u.urexp;
+ t0->plchar = pl_character[0];
+ t0->sex = (flags.female ? 'F' : 'M');
+ t0->uid = uid;
+ (void) strncpy(t0->name, plname, NAMSZ);
+ (t0->name)[NAMSZ] = 0;
+ (void) strncpy(t0->death, killer, DTHSZ);
+ (t0->death)[DTHSZ] = 0;
+ (void) strcpy(t0->date, getdate());
+
+ /* assure minimum number of points */
+ if(t0->points < POINTSMIN)
+ t0->points = 0;
+
+ t1 = tt_head = newttentry();
+ tprev = 0;
+ /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
+ for(rank = 1; ; ) {
+ if(fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
+ t1->date, &t1->uid,
+ &t1->level, &t1->maxlvl,
+ &t1->hp, &t1->maxhp, &t1->points,
+ &t1->plchar, &t1->sex, t1->name, t1->death) != 11
+ || t1->points < POINTSMIN)
+ t1->points = 0;
+ if(rank0 < 0 && t1->points < t0->points) {
+ rank0 = rank++;
+ if(tprev == 0)
+ tt_head = t0;
+ else
+ tprev->tt_next = t0;
+ t0->tt_next = t1;
+ occ_cnt--;
+ flg++; /* ask for a rewrite */
+ } else
+ tprev = t1;
+ if(t1->points == 0) break;
+ if(
+#ifdef PERS_IS_UID
+ t1->uid == t0->uid &&
+#else
+ strncmp(t1->name, t0->name, NAMSZ) == 0 &&
+#endif PERS_IS_UID
+ t1->plchar == t0->plchar && --occ_cnt <= 0){
+ if(rank0 < 0){
+ rank0 = 0;
+ rank1 = rank;
+ HUP printf("You didn't beat your previous score of %ld points.\n\n",
+ t1->points);
+ }
+ if(occ_cnt < 0){
+ flg++;
+ continue;
+ }
+ }
+ if(rank <= ENTRYMAX){
+ t1 = t1->tt_next = newttentry();
+ rank++;
+ }
+ if(rank > ENTRYMAX){
+ t1->points = 0;
+ break;
+ }
+ }
+ if(flg) { /* rewrite record file */
+ (void) fclose(rfile);
+ if(!(rfile = fopen(recfile,"w"))){
+ HUP puts("Cannot write record file\n");
+ goto unlock;
+ }
+
+ if(!done_stopprint) if(rank0 > 0){
+ if(rank0 <= 10)
+ puts("You made the top ten list!\n");
+ else
+ printf("You reached the %d%s place on the top %d list.\n\n",
+ rank0, ordin(rank0), ENTRYMAX);
+ }
+ }
+ if(rank0 == 0) rank0 = rank1;
+ if(rank0 <= 0) rank0 = rank;
+ if(!done_stopprint) outheader();
+ t1 = tt_head;
+ for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
+ if(flg) fprintf(rfile,"%6s %d %d %d %d %d %ld %c%c %s,%s\n",
+ t1->date, t1->uid,
+ t1->level, t1->maxlvl,
+ t1->hp, t1->maxhp, t1->points,
+ t1->plchar, t1->sex, t1->name, t1->death);
+ if(done_stopprint) continue;
+ if(rank > flags.end_top &&
+ (rank < rank0-flags.end_around || rank > rank0+flags.end_around)
+ && (!flags.end_own ||
+#ifdef PERS_IS_UID
+ t1->uid != t0->uid ))
+#else
+ strncmp(t1->name, t0->name, NAMSZ)))
+#endif PERS_IS_UID
+ continue;
+ if(rank == rank0-flags.end_around &&
+ rank0 > flags.end_top+flags.end_around+1 &&
+ !flags.end_own)
+ (void) putchar('\n');
+ if(rank != rank0)
+ (void) outentry(rank, t1, 0);
+ else if(!rank1)
+ (void) outentry(rank, t1, 1);
+ else {
+ int t0lth = outentry(0, t0, -1);
+ int t1lth = outentry(rank, t1, t0lth);
+ if(t1lth > t0lth) t0lth = t1lth;
+ (void) outentry(0, t0, t0lth);
+ }
+ }
+ if(rank0 >= rank) if(!done_stopprint)
+ (void) outentry(0, t0, 1);
+ (void) fclose(rfile);
+unlock:
+ (void) unlink(reclock);
+}
+
+outheader() {
+char linebuf[BUFSZ];
+register char *bp;
+ (void) strcpy(linebuf, "Number Points Name");
+ bp = eos(linebuf);
+ while(bp < linebuf + COLNO - 9) *bp++ = ' ';
+ (void) strcpy(bp, "Hp [max]");
+ puts(linebuf);
+}
+
+/* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */
+int
+outentry(rank,t1,so) register struct toptenentry *t1; {
+boolean quit = FALSE, killed = FALSE, starv = FALSE;
+char linebuf[BUFSZ];
+ linebuf[0] = 0;
+ if(rank) Sprintf(eos(linebuf), "%3d", rank);
+ else Sprintf(eos(linebuf), " ");
+ Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name);
+ if(t1->plchar == 'X') Sprintf(eos(linebuf), " ");
+ else Sprintf(eos(linebuf), "-%c ", t1->plchar);
+ if(!strncmp("escaped", t1->death, 7)) {
+ if(!strcmp(" (with amulet)", t1->death+7))
+ Sprintf(eos(linebuf), "escaped the dungeon with amulet");
+ else
+ Sprintf(eos(linebuf), "escaped the dungeon [max level %d]",
+ t1->maxlvl);
+ } else {
+ if(!strncmp(t1->death,"quit",4)) {
+ quit = TRUE;
+ if(t1->maxhp < 3*t1->hp && t1->maxlvl < 4)
+ Sprintf(eos(linebuf), "cravenly gave up");
+ else
+ Sprintf(eos(linebuf), "quit");
+ }
+ else if(!strcmp(t1->death,"choked"))
+ Sprintf(eos(linebuf), "choked on %s food",
+ (t1->sex == 'F') ? "her" : "his");
+ else if(!strncmp(t1->death,"starv",5))
+ Sprintf(eos(linebuf), "starved to death"), starv = TRUE;
+ else Sprintf(eos(linebuf), "was killed"), killed = TRUE;
+ Sprintf(eos(linebuf), " on%s level %d",
+ (killed || starv) ? "" : " dungeon", t1->level);
+ if(t1->maxlvl != t1->level)
+ Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
+ if(quit && t1->death[4]) Sprintf(eos(linebuf), t1->death + 4);
+ }
+ if(killed) Sprintf(eos(linebuf), " by %s%s",
+ (!strncmp(t1->death, "trick", 5) || !strncmp(t1->death, "the ", 4))
+ ? "" :
+ index(vowels,*t1->death) ? "an " : "a ",
+ t1->death);
+ Sprintf(eos(linebuf), ".");
+ if(t1->maxhp) {
+ register char *bp = eos(linebuf);
+ char hpbuf[10];
+ int hppos;
+ Sprintf(hpbuf, (t1->hp > 0) ? itoa(t1->hp) : "-");
+ hppos = COLNO - 7 - strlen(hpbuf);
+ if(bp <= linebuf + hppos) {
+ while(bp < linebuf + hppos) *bp++ = ' ';
+ (void) strcpy(bp, hpbuf);
+ Sprintf(eos(bp), " [%d]", t1->maxhp);
+ }
+ }
+ if(so == 0) puts(linebuf);
+ else if(so > 0) {
+ register char *bp = eos(linebuf);
+ if(so >= COLNO) so = COLNO-1;
+ while(bp < linebuf + so) *bp++ = ' ';
+ *bp = 0;
+ standoutbeg();
+ fputs(linebuf,stdout);
+ standoutend();
+ (void) putchar('\n');
+ }
+ return(strlen(linebuf));
+}
+
+char *
+itoa(a) int a; {
+static char buf[12];
+ Sprintf(buf,"%d",a);
+ return(buf);
+}
+
+char *
+ordin(n) int n; {
+register int d = n%10;
+ return((d==0 || d>3 || n/10==1) ? "th" : (d==1) ? "st" :
+ (d==2) ? "nd" : "rd");
+}
+
+clearlocks(){
+register x;
+ (void) signal(SIGHUP,SIG_IGN);
+ for(x = maxdlevel; x >= 0; x--) {
+ glo(x);
+ (void) unlink(lock); /* not all levels need be present */
+ }
+}
+
+#ifdef NOSAVEONHANGUP
+hangup()
+{
+ (void) signal(SIGINT, SIG_IGN);
+ clearlocks();
+ exit(1);
+}
+#endif NOSAVEONHANGUP
+
+char *
+eos(s)
+register char *s;
+{
+ while(*s) s++;
+ return(s);
+}
+
+/* it is the callers responsibility to check that there is room for c */
+charcat(s,c) register char *s, c; {
+ while(*s) s++;
+ *s++ = c;
+ *s = 0;
+}
+
+/*
+ * Called with args from main if argc >= 0. In this case, list scores as
+ * requested. Otherwise, find scores for the current player (and list them
+ * if argc == -1).
+ */
+prscore(argc,argv) int argc; char **argv; {
+ extern char *hname;
+ char **players;
+ int playerct;
+ int rank;
+ register struct toptenentry *t1, *t2;
+ char *recfile = RECORD;
+ FILE *rfile;
+ register flg = 0;
+ register int i;
+#ifdef nonsense
+ long total_score = 0L;
+ char totchars[10];
+ int totcharct = 0;
+#endif nonsense
+ int outflg = (argc >= -1);
+#ifdef PERS_IS_UID
+ int uid = -1;
+#else
+ char *player0;
+#endif PERS_IS_UID
+
+ if(!(rfile = fopen(recfile,"r"))){
+ puts("Cannot open record file!");
+ return;
+ }
+
+ if(argc > 1 && !strncmp(argv[1], "-s", 2)){
+ if(!argv[1][2]){
+ argc--;
+ argv++;
+ } else if(!argv[1][3] && index("CFKSTWX", argv[1][2])) {
+ argv[1]++;
+ argv[1][0] = '-';
+ } else argv[1] += 2;
+ }
+ if(argc <= 1){
+#ifdef PERS_IS_UID
+ uid = getuid();
+ playerct = 0;
+#else
+ player0 = plname;
+ if(!*player0)
+ player0 = "hackplayer";
+ playerct = 1;
+ players = &player0;
+#endif PERS_IS_UID
+ } else {
+ playerct = --argc;
+ players = ++argv;
+ }
+ if(outflg) putchar('\n');
+
+ t1 = tt_head = newttentry();
+ for(rank = 1; ; rank++) {
+ if(fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]",
+ t1->date, &t1->uid,
+ &t1->level, &t1->maxlvl,
+ &t1->hp, &t1->maxhp, &t1->points,
+ &t1->plchar, &t1->sex, t1->name, t1->death) != 11)
+ t1->points = 0;
+ if(t1->points == 0) break;
+#ifdef PERS_IS_UID
+ if(!playerct && t1->uid == uid)
+ flg++;
+ else
+#endif PERS_IS_UID
+ for(i = 0; i < playerct; i++){
+ if(strcmp(players[i], "all") == 0 ||
+ strncmp(t1->name, players[i], NAMSZ) == 0 ||
+ (players[i][0] == '-' &&
+ players[i][1] == t1->plchar &&
+ players[i][2] == 0) ||
+ (digit(players[i][0]) && rank <= atoi(players[i])))
+ flg++;
+ }
+ t1 = t1->tt_next = newttentry();
+ }
+ (void) fclose(rfile);
+ if(!flg) {
+ if(outflg) {
+ printf("Cannot find any entries for ");
+ if(playerct < 1) printf("you.\n");
+ else {
+ if(playerct > 1) printf("any of ");
+ for(i=0; i<playerct; i++)
+ printf("%s%s", players[i], (i<playerct-1)?", ":".\n");
+ printf("Call is: %s -s [playernames]\n", hname);
+ }
+ }
+ return;
+ }
+
+ if(outflg) outheader();
+ t1 = tt_head;
+ for(rank = 1; t1->points != 0; rank++, t1 = t2) {
+ t2 = t1->tt_next;
+#ifdef PERS_IS_UID
+ if(!playerct && t1->uid == uid)
+ goto outwithit;
+ else
+#endif PERS_IS_UID
+ for(i = 0; i < playerct; i++){
+ if(strcmp(players[i], "all") == 0 ||
+ strncmp(t1->name, players[i], NAMSZ) == 0 ||
+ (players[i][0] == '-' &&
+ players[i][1] == t1->plchar &&
+ players[i][2] == 0) ||
+ (digit(players[i][0]) && rank <= atoi(players[i]))){
+ outwithit:
+ if(outflg)
+ (void) outentry(rank, t1, 0);
+#ifdef nonsense
+ total_score += t1->points;
+ if(totcharct < sizeof(totchars)-1)
+ totchars[totcharct++] = t1->plchar;
+#endif nonsense
+ break;
+ }
+ }
+ free((char *) t1);
+ }
+#ifdef nonsense
+ totchars[totcharct] = 0;
+
+ /* We would like to determine whether he is experienced. However,
+ the information collected here only tells about the scores/roles
+ that got into the topten (top 100?). We should maintain a
+ .hacklog or something in his home directory. */
+ flags.beginner = (total_score < 6000);
+ for(i=0; i<6; i++)
+ if(!index(totchars, "CFKSTWX"[i])) {
+ flags.beginner = 1;
+ if(!pl_character[0]) pl_character[0] = "CFKSTWX"[i];
+ break;
+ }
+#endif nonsense
+}
diff --git a/games/hack/hack.engrave.c b/games/hack/hack.engrave.c
new file mode 100644
index 000000000000..dc16c39f952c
--- /dev/null
+++ b/games/hack/hack.engrave.c
@@ -0,0 +1,306 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.engrave.c - version 1.0.3 */
+
+#include "hack.h"
+
+extern char *nomovemsg;
+extern char nul[];
+extern struct obj zeroobj;
+struct engr {
+ struct engr *nxt_engr;
+ char *engr_txt;
+ xchar engr_x, engr_y;
+ unsigned engr_lth; /* for save & restore; not length of text */
+ long engr_time; /* moment engraving was (will be) finished */
+ xchar engr_type;
+#define DUST 1
+#define ENGRAVE 2
+#define BURN 3
+} *head_engr;
+
+struct engr *
+engr_at(x,y) register xchar x,y; {
+register struct engr *ep = head_engr;
+ while(ep) {
+ if(x == ep->engr_x && y == ep->engr_y)
+ return(ep);
+ ep = ep->nxt_engr;
+ }
+ return((struct engr *) 0);
+}
+
+sengr_at(s,x,y) register char *s; register xchar x,y; {
+register struct engr *ep = engr_at(x,y);
+register char *t;
+register int n;
+ if(ep && ep->engr_time <= moves) {
+ t = ep->engr_txt;
+/*
+ if(!strcmp(s,t)) return(1);
+*/
+ n = strlen(s);
+ while(*t) {
+ if(!strncmp(s,t,n)) return(1);
+ t++;
+ }
+ }
+ return(0);
+}
+
+u_wipe_engr(cnt)
+register int cnt;
+{
+ if(!u.uswallow && !Levitation)
+ wipe_engr_at(u.ux, u.uy, cnt);
+}
+
+wipe_engr_at(x,y,cnt) register xchar x,y,cnt; {
+register struct engr *ep = engr_at(x,y);
+register int lth,pos;
+char ch;
+ if(ep){
+ if((ep->engr_type != DUST) || Levitation) {
+ cnt = rn2(1 + 50/(cnt+1)) ? 0 : 1;
+ }
+ lth = strlen(ep->engr_txt);
+ if(lth && cnt > 0 ) {
+ while(cnt--) {
+ pos = rn2(lth);
+ if((ch = ep->engr_txt[pos]) == ' ')
+ continue;
+ ep->engr_txt[pos] = (ch != '?') ? '?' : ' ';
+ }
+ }
+ while(lth && ep->engr_txt[lth-1] == ' ')
+ ep->engr_txt[--lth] = 0;
+ while(ep->engr_txt[0] == ' ')
+ ep->engr_txt++;
+ if(!ep->engr_txt[0]) del_engr(ep);
+ }
+}
+
+read_engr_at(x,y) register int x,y; {
+register struct engr *ep = engr_at(x,y);
+ if(ep && ep->engr_txt[0]) {
+ switch(ep->engr_type) {
+ case DUST:
+ pline("Something is written here in the dust.");
+ break;
+ case ENGRAVE:
+ pline("Something is engraved here on the floor.");
+ break;
+ case BURN:
+ pline("Some text has been burned here in the floor.");
+ break;
+ default:
+ impossible("Something is written in a very strange way.");
+ }
+ pline("You read: \"%s\".", ep->engr_txt);
+ }
+}
+
+make_engr_at(x,y,s)
+register int x,y;
+register char *s;
+{
+ register struct engr *ep;
+
+ if(ep = engr_at(x,y))
+ del_engr(ep);
+ ep = (struct engr *)
+ alloc((unsigned)(sizeof(struct engr) + strlen(s) + 1));
+ ep->nxt_engr = head_engr;
+ head_engr = ep;
+ ep->engr_x = x;
+ ep->engr_y = y;
+ ep->engr_txt = (char *)(ep + 1);
+ (void) strcpy(ep->engr_txt, s);
+ ep->engr_time = 0;
+ ep->engr_type = DUST;
+ ep->engr_lth = strlen(s) + 1;
+}
+
+doengrave(){
+register int len;
+register char *sp;
+register struct engr *ep, *oep = engr_at(u.ux,u.uy);
+char buf[BUFSZ];
+xchar type;
+int spct; /* number of leading spaces */
+register struct obj *otmp;
+ multi = 0;
+
+ if(u.uswallow) {
+ pline("You're joking. Hahaha!"); /* riv05!a3 */
+ return(0);
+ }
+
+ /* one may write with finger, weapon or wand */
+ otmp = getobj("#-)/", "write with");
+ if(!otmp) return(0);
+
+ if(otmp == &zeroobj)
+ otmp = 0;
+ if(otmp && otmp->otyp == WAN_FIRE && otmp->spe) {
+ type = BURN;
+ otmp->spe--;
+ } else {
+ /* first wield otmp */
+ if(otmp != uwep) {
+ if(uwep && uwep->cursed) {
+ /* Andreas Bormann */
+ pline("Since your weapon is welded to your hand,");
+ pline("you use the %s.", aobjnam(uwep, (char *) 0));
+ otmp = uwep;
+ } else {
+ if(!otmp)
+ pline("You are now empty-handed.");
+ else if(otmp->cursed)
+ pline("The %s %s to your hand!",
+ aobjnam(otmp, "weld"),
+ (otmp->quan == 1) ? "itself" : "themselves");
+ else
+ pline("You now wield %s.", doname(otmp));
+ setuwep(otmp);
+ }
+ }
+
+ if(!otmp)
+ type = DUST;
+ else
+ if(otmp->otyp == DAGGER || otmp->otyp == TWO_HANDED_SWORD ||
+ otmp->otyp == CRYSKNIFE ||
+ otmp->otyp == LONG_SWORD || otmp->otyp == AXE) {
+ type = ENGRAVE;
+ if((int)otmp->spe <= -3) {
+ type = DUST;
+ pline("Your %s too dull for engraving.",
+ aobjnam(otmp, "are"));
+ if(oep && oep->engr_type != DUST) return(1);
+ }
+ } else type = DUST;
+ }
+ if(Levitation && type != BURN){ /* riv05!a3 */
+ pline("You can't reach the floor!");
+ return(1);
+ }
+ if(oep && oep->engr_type == DUST){
+ pline("You wipe out the message that was written here.");
+ del_engr(oep);
+ oep = 0;
+ }
+ if(type == DUST && oep){
+ pline("You cannot wipe out the message that is %s in the rock.",
+ (oep->engr_type == BURN) ? "burned" : "engraved");
+ return(1);
+ }
+
+ pline("What do you want to %s on the floor here? ",
+ (type == ENGRAVE) ? "engrave" : (type == BURN) ? "burn" : "write");
+ getlin(buf);
+ clrlin();
+ spct = 0;
+ sp = buf;
+ while(*sp == ' ') spct++, sp++;
+ len = strlen(sp);
+ if(!len || *buf == '\033') {
+ if(type == BURN) otmp->spe++;
+ return(0);
+ }
+
+ switch(type) {
+ case DUST:
+ case BURN:
+ if(len > 15) {
+ multi = -(len/10);
+ nomovemsg = "You finished writing.";
+ }
+ break;
+ case ENGRAVE: /* here otmp != 0 */
+ { int len2 = (otmp->spe + 3) * 2 + 1;
+
+ pline("Your %s dull.", aobjnam(otmp, "get"));
+ if(len2 < len) {
+ len = len2;
+ sp[len] = 0;
+ otmp->spe = -3;
+ nomovemsg = "You cannot engrave more.";
+ } else {
+ otmp->spe -= len/2;
+ nomovemsg = "You finished engraving.";
+ }
+ multi = -len;
+ }
+ break;
+ }
+ if(oep) len += strlen(oep->engr_txt) + spct;
+ ep = (struct engr *) alloc((unsigned)(sizeof(struct engr) + len + 1));
+ ep->nxt_engr = head_engr;
+ head_engr = ep;
+ ep->engr_x = u.ux;
+ ep->engr_y = u.uy;
+ sp = (char *)(ep + 1); /* (char *)ep + sizeof(struct engr) */
+ ep->engr_txt = sp;
+ if(oep) {
+ (void) strcpy(sp, oep->engr_txt);
+ (void) strcat(sp, buf);
+ del_engr(oep);
+ } else
+ (void) strcpy(sp, buf);
+ ep->engr_lth = len+1;
+ ep->engr_type = type;
+ ep->engr_time = moves-multi;
+
+ /* kludge to protect pline against excessively long texts */
+ if(len > BUFSZ-20) sp[BUFSZ-20] = 0;
+
+ return(1);
+}
+
+save_engravings(fd) int fd; {
+register struct engr *ep = head_engr;
+ while(ep) {
+ if(!ep->engr_lth || !ep->engr_txt[0]){
+ ep = ep->nxt_engr;
+ continue;
+ }
+ bwrite(fd, (char *) & (ep->engr_lth), sizeof(ep->engr_lth));
+ bwrite(fd, (char *) ep, sizeof(struct engr) + ep->engr_lth);
+ ep = ep->nxt_engr;
+ }
+ bwrite(fd, (char *) nul, sizeof(unsigned));
+ head_engr = 0;
+}
+
+rest_engravings(fd) int fd; {
+register struct engr *ep;
+unsigned lth;
+ head_engr = 0;
+ while(1) {
+ mread(fd, (char *) &lth, sizeof(unsigned));
+ if(lth == 0) return;
+ ep = (struct engr *) alloc(sizeof(struct engr) + lth);
+ mread(fd, (char *) ep, sizeof(struct engr) + lth);
+ ep->nxt_engr = head_engr;
+ ep->engr_txt = (char *) (ep + 1); /* Andreas Bormann */
+ head_engr = ep;
+ }
+}
+
+del_engr(ep) register struct engr *ep; {
+register struct engr *ept;
+ if(ep == head_engr)
+ head_engr = ep->nxt_engr;
+ else {
+ for(ept = head_engr; ept; ept = ept->nxt_engr) {
+ if(ept->nxt_engr == ep) {
+ ept->nxt_engr = ep->nxt_engr;
+ goto fnd;
+ }
+ }
+ impossible("Error in del_engr?");
+ return;
+ fnd: ;
+ }
+ free((char *) ep);
+}
diff --git a/games/hack/hack.fight.c b/games/hack/hack.fight.c
new file mode 100644
index 000000000000..ede886d16931
--- /dev/null
+++ b/games/hack/hack.fight.c
@@ -0,0 +1,358 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.fight.c - version 1.0.3 */
+
+#include "hack.h"
+extern struct permonst li_dog, dog, la_dog;
+extern char *exclam(), *xname();
+extern struct obj *mkobj_at();
+
+static boolean far_noise;
+static long noisetime;
+
+/* hitmm returns 0 (miss), 1 (hit), or 2 (kill) */
+hitmm(magr,mdef) register struct monst *magr,*mdef; {
+register struct permonst *pa = magr->data, *pd = mdef->data;
+int hit;
+schar tmp;
+boolean vis;
+ if(index("Eauy", pa->mlet)) return(0);
+ if(magr->mfroz) return(0); /* riv05!a3 */
+ tmp = pd->ac + pa->mlevel;
+ if(mdef->mconf || mdef->mfroz || mdef->msleep){
+ tmp += 4;
+ if(mdef->msleep) mdef->msleep = 0;
+ }
+ hit = (tmp > rnd(20));
+ if(hit) mdef->msleep = 0;
+ vis = (cansee(magr->mx,magr->my) && cansee(mdef->mx,mdef->my));
+ if(vis){
+ char buf[BUFSZ];
+ if(mdef->mimic) seemimic(mdef);
+ if(magr->mimic) seemimic(magr);
+ (void) sprintf(buf,"%s %s", Monnam(magr),
+ hit ? "hits" : "misses");
+ pline("%s %s.", buf, monnam(mdef));
+ } else {
+ boolean far = (dist(magr->mx, magr->my) > 15);
+ if(far != far_noise || moves-noisetime > 10) {
+ far_noise = far;
+ noisetime = moves;
+ pline("You hear some noises%s.",
+ far ? " in the distance" : "");
+ }
+ }
+ if(hit){
+ if(magr->data->mlet == 'c' && !magr->cham) {
+ magr->mhpmax += 3;
+ if(vis) pline("%s is turned to stone!", Monnam(mdef));
+ else if(mdef->mtame)
+ pline("You have a peculiarly sad feeling for a moment, then it passes.");
+ monstone(mdef);
+ hit = 2;
+ } else
+ if((mdef->mhp -= d(pa->damn,pa->damd)) < 1) {
+ magr->mhpmax += 1 + rn2(pd->mlevel+1);
+ if(magr->mtame && magr->mhpmax > 8*pa->mlevel){
+ if(pa == &li_dog) magr->data = pa = &dog;
+ else if(pa == &dog) magr->data = pa = &la_dog;
+ }
+ if(vis) pline("%s is killed!", Monnam(mdef));
+ else if(mdef->mtame)
+ pline("You have a sad feeling for a moment, then it passes.");
+ mondied(mdef);
+ hit = 2;
+ }
+ }
+ return(hit);
+}
+
+/* drop (perhaps) a cadaver and remove monster */
+mondied(mdef) register struct monst *mdef; {
+register struct permonst *pd = mdef->data;
+ if(letter(pd->mlet) && rn2(3)){
+ (void) mkobj_at(pd->mlet,mdef->mx,mdef->my);
+ if(cansee(mdef->mx,mdef->my)){
+ unpmon(mdef);
+ atl(mdef->mx,mdef->my,fobj->olet);
+ }
+ stackobj(fobj);
+ }
+ mondead(mdef);
+}
+
+/* drop a rock and remove monster */
+monstone(mdef) register struct monst *mdef; {
+ extern char mlarge[];
+ if(index(mlarge, mdef->data->mlet))
+ mksobj_at(ENORMOUS_ROCK, mdef->mx, mdef->my);
+ else
+ mksobj_at(ROCK, mdef->mx, mdef->my);
+ if(cansee(mdef->mx, mdef->my)){
+ unpmon(mdef);
+ atl(mdef->mx,mdef->my,fobj->olet);
+ }
+ mondead(mdef);
+}
+
+
+fightm(mtmp) register struct monst *mtmp; {
+register struct monst *mon;
+ for(mon = fmon; mon; mon = mon->nmon) if(mon != mtmp) {
+ if(DIST(mon->mx,mon->my,mtmp->mx,mtmp->my) < 3)
+ if(rn2(4))
+ return(hitmm(mtmp,mon));
+ }
+ return(-1);
+}
+
+/* u is hit by sth, but not a monster */
+thitu(tlev,dam,name)
+register tlev,dam;
+register char *name;
+{
+char buf[BUFSZ];
+ setan(name,buf);
+ if(u.uac + tlev <= rnd(20)) {
+ if(Blind) pline("It misses.");
+ else pline("You are almost hit by %s!", buf);
+ return(0);
+ } else {
+ if(Blind) pline("You are hit!");
+ else pline("You are hit by %s!", buf);
+ losehp(dam,name);
+ return(1);
+ }
+}
+
+char mlarge[] = "bCDdegIlmnoPSsTUwY',&";
+
+boolean
+hmon(mon,obj,thrown) /* return TRUE if mon still alive */
+register struct monst *mon;
+register struct obj *obj;
+register thrown;
+{
+ register tmp;
+ boolean hittxt = FALSE;
+
+ if(!obj){
+ tmp = rnd(2); /* attack with bare hands */
+ if(mon->data->mlet == 'c' && !uarmg){
+ pline("You hit the cockatrice with your bare hands.");
+ pline("You turn to stone ...");
+ done_in_by(mon);
+ }
+ } else if(obj->olet == WEAPON_SYM || obj->otyp == PICK_AXE) {
+ if(obj == uwep && (obj->otyp > SPEAR || obj->otyp < BOOMERANG))
+ tmp = rnd(2);
+ else {
+ if(index(mlarge, mon->data->mlet)) {
+ tmp = rnd(objects[obj->otyp].wldam);
+ if(obj->otyp == TWO_HANDED_SWORD) tmp += d(2,6);
+ else if(obj->otyp == FLAIL) tmp += rnd(4);
+ } else {
+ tmp = rnd(objects[obj->otyp].wsdam);
+ }
+ tmp += obj->spe;
+ if(!thrown && obj == uwep && obj->otyp == BOOMERANG
+ && !rn2(3)){
+ pline("As you hit %s, the boomerang breaks into splinters.",
+ monnam(mon));
+ freeinv(obj);
+ setworn((struct obj *) 0, obj->owornmask);
+ obfree(obj, (struct obj *) 0);
+ tmp++;
+ }
+ }
+ if(mon->data->mlet == 'O' && obj->otyp == TWO_HANDED_SWORD &&
+ !strcmp(ONAME(obj), "Orcrist"))
+ tmp += rnd(10);
+ } else switch(obj->otyp) {
+ case HEAVY_IRON_BALL:
+ tmp = rnd(25); break;
+ case EXPENSIVE_CAMERA:
+ pline("You succeed in destroying your camera. Congratulations!");
+ freeinv(obj);
+ if(obj->owornmask)
+ setworn((struct obj *) 0, obj->owornmask);
+ obfree(obj, (struct obj *) 0);
+ return(TRUE);
+ case DEAD_COCKATRICE:
+ pline("You hit %s with the cockatrice corpse.",
+ monnam(mon));
+ if(mon->data->mlet == 'c') {
+ tmp = 1;
+ hittxt = TRUE;
+ break;
+ }
+ pline("%s is turned to stone!", Monnam(mon));
+ killed(mon);
+ return(FALSE);
+ case CLOVE_OF_GARLIC: /* no effect against demons */
+ if(index(UNDEAD, mon->data->mlet))
+ mon->mflee = 1;
+ tmp = 1;
+ break;
+ default:
+ /* non-weapons can damage because of their weight */
+ /* (but not too much) */
+ tmp = obj->owt/10;
+ if(tmp < 1) tmp = 1;
+ else tmp = rnd(tmp);
+ if(tmp > 6) tmp = 6;
+ }
+
+ /****** NOTE: perhaps obj is undefined!! (if !thrown && BOOMERANG) */
+
+ tmp += u.udaminc + dbon();
+ if(u.uswallow) {
+ if((tmp -= u.uswldtim) <= 0) {
+ pline("Your arms are no longer able to hit.");
+ return(TRUE);
+ }
+ }
+ if(tmp < 1) tmp = 1;
+ mon->mhp -= tmp;
+ if(mon->mhp < 1) {
+ killed(mon);
+ return(FALSE);
+ }
+ if(mon->mtame && (!mon->mflee || mon->mfleetim)) {
+ mon->mflee = 1; /* Rick Richardson */
+ mon->mfleetim += 10*rnd(tmp);
+ }
+
+ if(!hittxt) {
+ if(thrown)
+ /* this assumes that we cannot throw plural things */
+ hit( xname(obj) /* or: objects[obj->otyp].oc_name */,
+ mon, exclam(tmp) );
+ else if(Blind)
+ pline("You hit it.");
+ else
+ pline("You hit %s%s", monnam(mon), exclam(tmp));
+ }
+
+ if(u.umconf && !thrown) {
+ if(!Blind) {
+ pline("Your hands stop glowing blue.");
+ if(!mon->mfroz && !mon->msleep)
+ pline("%s appears confused.",Monnam(mon));
+ }
+ mon->mconf = 1;
+ u.umconf = 0;
+ }
+ return(TRUE); /* mon still alive */
+}
+
+/* try to attack; return FALSE if monster evaded */
+/* u.dx and u.dy must be set */
+attack(mtmp)
+register struct monst *mtmp;
+{
+ schar tmp;
+ boolean malive = TRUE;
+ register struct permonst *mdat;
+ mdat = mtmp->data;
+
+ u_wipe_engr(3); /* andrew@orca: prevent unlimited pick-axe attacks */
+
+ if(mdat->mlet == 'L' && !mtmp->mfroz && !mtmp->msleep &&
+ !mtmp->mconf && mtmp->mcansee && !rn2(7) &&
+ (m_move(mtmp, 0) == 2 /* he died */ || /* he moved: */
+ mtmp->mx != u.ux+u.dx || mtmp->my != u.uy+u.dy))
+ return(FALSE);
+
+ if(mtmp->mimic){
+ if(!u.ustuck && !mtmp->mflee) u.ustuck = mtmp;
+ switch(levl[u.ux+u.dx][u.uy+u.dy].scrsym){
+ case '+':
+ pline("The door actually was a Mimic.");
+ break;
+ case '$':
+ pline("The chest was a Mimic!");
+ break;
+ default:
+ pline("Wait! That's a Mimic!");
+ }
+ wakeup(mtmp); /* clears mtmp->mimic */
+ return(TRUE);
+ }
+
+ wakeup(mtmp);
+
+ if(mtmp->mhide && mtmp->mundetected){
+ register struct obj *obj;
+
+ mtmp->mundetected = 0;
+ if((obj = o_at(mtmp->mx,mtmp->my)) && !Blind)
+ pline("Wait! There's a %s hiding under %s!",
+ mdat->mname, doname(obj));
+ return(TRUE);
+ }
+
+ tmp = u.uluck + u.ulevel + mdat->ac + abon();
+ if(uwep) {
+ if(uwep->olet == WEAPON_SYM || uwep->otyp == PICK_AXE)
+ tmp += uwep->spe;
+ if(uwep->otyp == TWO_HANDED_SWORD) tmp -= 1;
+ else if(uwep->otyp == DAGGER) tmp += 2;
+ else if(uwep->otyp == CRYSKNIFE) tmp += 3;
+ else if(uwep->otyp == SPEAR &&
+ index("XDne", mdat->mlet)) tmp += 2;
+ }
+ if(mtmp->msleep) {
+ mtmp->msleep = 0;
+ tmp += 2;
+ }
+ if(mtmp->mfroz) {
+ tmp += 4;
+ if(!rn2(10)) mtmp->mfroz = 0;
+ }
+ if(mtmp->mflee) tmp += 2;
+ if(u.utrap) tmp -= 3;
+
+ /* with a lot of luggage, your agility diminishes */
+ tmp -= (inv_weight() + 40)/20;
+
+ if(tmp <= rnd(20) && !u.uswallow){
+ if(Blind) pline("You miss it.");
+ else pline("You miss %s.",monnam(mtmp));
+ } else {
+ /* we hit the monster; be careful: it might die! */
+
+ if((malive = hmon(mtmp,uwep,0)) == TRUE) {
+ /* monster still alive */
+ if(!rn2(25) && mtmp->mhp < mtmp->mhpmax/2) {
+ mtmp->mflee = 1;
+ if(!rn2(3)) mtmp->mfleetim = rnd(100);
+ if(u.ustuck == mtmp && !u.uswallow)
+ u.ustuck = 0;
+ }
+#ifndef NOWORM
+ if(mtmp->wormno)
+ cutworm(mtmp, u.ux+u.dx, u.uy+u.dy,
+ uwep ? uwep->otyp : 0);
+#endif NOWORM
+ }
+ if(mdat->mlet == 'a') {
+ if(rn2(2)) {
+ pline("You are splashed by the blob's acid!");
+ losehp_m(rnd(6), mtmp);
+ if(!rn2(30)) corrode_armor();
+ }
+ if(!rn2(6)) corrode_weapon();
+ }
+ }
+ if(malive && mdat->mlet == 'E' && canseemon(mtmp)
+ && !mtmp->mcan && rn2(3)) {
+ if(mtmp->mcansee) {
+ pline("You are frozen by the floating eye's gaze!");
+ nomul((u.ulevel > 6 || rn2(4)) ? rn1(20,-21) : -200);
+ } else {
+ pline("The blinded floating eye cannot defend itself.");
+ if(!rn2(500)) if((int)u.uluck > LUCKMIN) u.uluck--;
+ }
+ }
+ return(TRUE);
+}
diff --git a/games/hack/hack.fix b/games/hack/hack.fix
new file mode 100644
index 000000000000..01e6460247d7
--- /dev/null
+++ b/games/hack/hack.fix
@@ -0,0 +1,113 @@
+/***** unido:net.games.hack / ab / 7:23 pm Sep 13, 1985*/
+
+Recently hack (1.0.3) crashed with core dumps during some good games.
+The crashes occured in the onbill-routine. After investigating the core
+dump I found that the shopkeeper's bill was still to be paid. Normaly
+if you leave a shop the bill will be cleared and onbill() would not
+check it. But under certain conditions you can leave a shop without
+clearing the bill. The conditions are:
+
+ 1. You have to rob a shop in order to make the shopkeeper
+ follow you.
+
+ 2. After leaving the shop being followed by the shopkeeper
+ you must return to the shop...
+
+ 3. ...and then leave the unguarded shop again.
+ - The shopkeeper mustn't be present!
+
+If you climb the stairs to the previous level, chances are that your
+bill now contains much more items than allowed. If so the next call to
+onbill() will dump the core.
+
+Following is a context diff to fix the bug. Actually just the last hunk
+does the fix [it deletes two lines which have been inserted in 1.0.3],
+but I think the other fix was intended by the now deleted lines.
+
+ Andreas
+
+--
+Andreas Bormann ab@unido.UUCP
+University of Dortmund N 51 29' 05" E 07 24' 42"
+West Germany
+
+------ the diff follows:
+
+*** hack.shk.c.orig Sun Aug 4 12:07:51 1985
+--- hack.shk.c Fri Sep 13 14:29:52 1985
+***************
+*** 133,139
+ /* Did we just leave a shop? */
+ if(u.uinshop &&
+ (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) {
+- u.uinshop = 0;
+ if(shopkeeper) {
+ if(ESHK(shopkeeper)->billct) {
+ pline("Somehow you escaped the shop without paying!");
+
+--- 133,138 -----
+ /* Did we just leave a shop? */
+ if(u.uinshop &&
+ (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) {
+ if(shopkeeper) {
+ if(ESHK(shopkeeper)->billct) {
+ if(inroom(shopkeeper->mx, shopkeeper->my)
+***************
+*** 136,142
+ u.uinshop = 0;
+ if(shopkeeper) {
+ if(ESHK(shopkeeper)->billct) {
+! pline("Somehow you escaped the shop without paying!");
+ addupbill();
+ pline("You stole for a total worth of %ld zorkmids.",
+ total);
+
+--- 135,143 -----
+ (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) {
+ if(shopkeeper) {
+ if(ESHK(shopkeeper)->billct) {
+! if(inroom(shopkeeper->mx, shopkeeper->my)
+! == u.uinshop - 1) /* ab@unido */
+! pline("Somehow you escaped the shop without paying!");
+ addupbill();
+ pline("You stole for a total worth of %ld zorkmids.",
+ total);
+***************
+*** 149,154
+ shopkeeper = 0;
+ shlevel = 0;
+ }
+ }
+
+ /* Did we just enter a zoo of some kind? */
+
+--- 150,156 -----
+ shopkeeper = 0;
+ shlevel = 0;
+ }
++ u.uinshop = 0;
+ }
+
+ /* Did we just enter a zoo of some kind? */
+***************
+*** 183,190
+ findshk(roomno);
+ if(!shopkeeper) {
+ rooms[roomno].rtype = 0;
+- u.uinshop = 0;
+- } else if(inroom(shopkeeper->mx, shopkeeper->my) != roomno) {
+ u.uinshop = 0;
+ } else if(!u.uinshop){
+ if(!ESHK(shopkeeper)->visitct ||
+
+--- 185,190 -----
+ findshk(roomno);
+ if(!shopkeeper) {
+ rooms[roomno].rtype = 0;
+ u.uinshop = 0;
+ } else if(!u.uinshop){
+ if(!ESHK(shopkeeper)->visitct ||
+/* ---------- */
+
+
+
diff --git a/games/hack/hack.h b/games/hack/hack.h
new file mode 100644
index 000000000000..58c028379ee1
--- /dev/null
+++ b/games/hack/hack.h
@@ -0,0 +1,160 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.h - version 1.0.3 */
+
+#include "config.h"
+#include <string.h>
+
+#ifndef BSD
+#define index strchr
+#define rindex strrchr
+#endif BSD
+
+#define Null(type) ((struct type *) 0)
+
+#include "def.objclass.h"
+
+typedef struct {
+ xchar x,y;
+} coord;
+
+#include "def.monst.h" /* uses coord */
+#include "def.gold.h"
+#include "def.trap.h"
+#include "def.obj.h"
+#include "def.flag.h"
+
+#define plur(x) (((x) == 1) ? "" : "s")
+
+#define BUFSZ 256 /* for getlin buffers */
+#define PL_NSIZ 32 /* name of player, ghost, shopkeeper */
+
+#include "def.rm.h"
+#include "def.permonst.h"
+
+extern long *alloc();
+
+extern xchar xdnstair, ydnstair, xupstair, yupstair; /* stairs up and down. */
+
+extern xchar dlevel;
+#define newstring(x) (char *) alloc((unsigned)(x))
+#include "hack.onames.h"
+
+#define ON 1
+#define OFF 0
+
+extern struct obj *invent, *uwep, *uarm, *uarm2, *uarmh, *uarms, *uarmg,
+ *uleft, *uright, *fcobj;
+extern struct obj *uchain; /* defined iff PUNISHED */
+extern struct obj *uball; /* defined if PUNISHED */
+struct obj *o_at(), *getobj(), *sobj_at();
+
+struct prop {
+#define TIMEOUT 007777 /* mask */
+#define LEFT_RING W_RINGL /* 010000L */
+#define RIGHT_RING W_RINGR /* 020000L */
+#define INTRINSIC 040000L
+#define LEFT_SIDE LEFT_RING
+#define RIGHT_SIDE RIGHT_RING
+#define BOTH_SIDES (LEFT_SIDE | RIGHT_SIDE)
+ long p_flgs;
+ int (*p_tofn)(); /* called after timeout */
+};
+
+struct you {
+ xchar ux, uy;
+ schar dx, dy, dz; /* direction of move (or zap or ... ) */
+#ifdef QUEST
+ schar di; /* direction of FF */
+ xchar ux0, uy0; /* initial position FF */
+#endif QUEST
+ xchar udisx, udisy; /* last display pos */
+ char usym; /* usually '@' */
+ schar uluck;
+#define LUCKMAX 10 /* on moonlit nights 11 */
+#define LUCKMIN (-10)
+ int last_str_turn:3; /* 0: none, 1: half turn, 2: full turn */
+ /* +: turn right, -: turn left */
+ unsigned udispl:1; /* @ on display */
+ unsigned ulevel:4; /* 1 - 14 */
+#ifdef QUEST
+ unsigned uhorizon:7;
+#endif QUEST
+ unsigned utrap:3; /* trap timeout */
+ unsigned utraptype:1; /* defined if utrap nonzero */
+#define TT_BEARTRAP 0
+#define TT_PIT 1
+ unsigned uinshop:6; /* used only in shk.c - (roomno+1) of shop */
+
+
+/* perhaps these #define's should also be generated by makedefs */
+#define TELEPAT LAST_RING /* not a ring */
+#define Telepat u.uprops[TELEPAT].p_flgs
+#define FAST (LAST_RING+1) /* not a ring */
+#define Fast u.uprops[FAST].p_flgs
+#define CONFUSION (LAST_RING+2) /* not a ring */
+#define Confusion u.uprops[CONFUSION].p_flgs
+#define INVIS (LAST_RING+3) /* not a ring */
+#define Invis u.uprops[INVIS].p_flgs
+#define Invisible (Invis && !See_invisible)
+#define GLIB (LAST_RING+4) /* not a ring */
+#define Glib u.uprops[GLIB].p_flgs
+#define PUNISHED (LAST_RING+5) /* not a ring */
+#define Punished u.uprops[PUNISHED].p_flgs
+#define SICK (LAST_RING+6) /* not a ring */
+#define Sick u.uprops[SICK].p_flgs
+#define BLIND (LAST_RING+7) /* not a ring */
+#define Blind u.uprops[BLIND].p_flgs
+#define WOUNDED_LEGS (LAST_RING+8) /* not a ring */
+#define Wounded_legs u.uprops[WOUNDED_LEGS].p_flgs
+#define STONED (LAST_RING+9) /* not a ring */
+#define Stoned u.uprops[STONED].p_flgs
+#define PROP(x) (x-RIN_ADORNMENT) /* convert ring to index in uprops */
+ unsigned umconf:1;
+ char *usick_cause;
+ struct prop uprops[LAST_RING+10];
+
+ unsigned uswallow:1; /* set if swallowed by a monster */
+ unsigned uswldtim:4; /* time you have been swallowed */
+ unsigned uhs:3; /* hunger state - see hack.eat.c */
+ schar ustr,ustrmax;
+ schar udaminc;
+ schar uac;
+ int uhp,uhpmax;
+ long int ugold,ugold0,uexp,urexp;
+ int uhunger; /* refd only in eat.c and shk.c */
+ int uinvault;
+ struct monst *ustuck;
+ int nr_killed[CMNUM+2]; /* used for experience bookkeeping */
+};
+
+extern struct you u;
+
+extern char *traps[];
+extern char *monnam(), *Monnam(), *amonnam(), *Amonnam(),
+ *doname(), *aobjnam();
+extern char readchar();
+extern char vowels[];
+
+extern xchar curx,cury; /* cursor location on screen */
+
+extern coord bhitpos; /* place where thrown weapon falls to the ground */
+
+extern xchar seehx,seelx,seehy,seely; /* where to see*/
+extern char *save_cm,*killer;
+
+extern xchar dlevel, maxdlevel; /* dungeon level */
+
+extern long moves;
+
+extern int multi;
+
+
+extern char lock[];
+
+
+#define DIST(x1,y1,x2,y2) (((x1)-(x2))*((x1)-(x2)) + ((y1)-(y2))*((y1)-(y2)))
+
+#define PL_CSIZ 20 /* sizeof pl_character */
+#define MAX_CARR_CAP 120 /* so that boulders can be heavier */
+#define MAXLEVEL 40
+#define FAR (COLNO+2) /* position outside screen */
diff --git a/games/hack/hack.invent.c b/games/hack/hack.invent.c
new file mode 100644
index 000000000000..c4620ca129cb
--- /dev/null
+++ b/games/hack/hack.invent.c
@@ -0,0 +1,863 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.invent.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+extern struct obj *splitobj();
+extern struct obj zeroobj;
+extern char morc;
+extern char quitchars[];
+char *xprname();
+
+#ifndef NOWORM
+#include "def.wseg.h"
+extern struct wseg *wsegs[32];
+#endif NOWORM
+
+#define NOINVSYM '#'
+
+static int lastinvnr = 51; /* 0 ... 51 */
+static
+assigninvlet(otmp)
+register struct obj *otmp;
+{
+ boolean inuse[52];
+ register int i;
+ register struct obj *obj;
+
+ for(i = 0; i < 52; i++) inuse[i] = FALSE;
+ for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) {
+ i = obj->invlet;
+ if('a' <= i && i <= 'z') inuse[i - 'a'] = TRUE; else
+ if('A' <= i && i <= 'Z') inuse[i - 'A' + 26] = TRUE;
+ if(i == otmp->invlet) otmp->invlet = 0;
+ }
+ if((i = otmp->invlet) &&
+ (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
+ return;
+ for(i = lastinvnr+1; i != lastinvnr; i++) {
+ if(i == 52) { i = -1; continue; }
+ if(!inuse[i]) break;
+ }
+ otmp->invlet = (inuse[i] ? NOINVSYM :
+ (i < 26) ? ('a'+i) : ('A'+i-26));
+ lastinvnr = i;
+}
+
+struct obj *
+addinv(obj)
+register struct obj *obj;
+{
+ register struct obj *otmp;
+
+ /* merge or attach to end of chain */
+ if(!invent) {
+ invent = obj;
+ otmp = 0;
+ } else
+ for(otmp = invent; /* otmp */; otmp = otmp->nobj) {
+ if(merged(otmp, obj, 0))
+ return(otmp);
+ if(!otmp->nobj) {
+ otmp->nobj = obj;
+ break;
+ }
+ }
+ obj->nobj = 0;
+
+ if(flags.invlet_constant) {
+ assigninvlet(obj);
+ /*
+ * The ordering of the chain is nowhere significant
+ * so in case you prefer some other order than the
+ * historical one, change the code below.
+ */
+ if(otmp) { /* find proper place in chain */
+ otmp->nobj = 0;
+ if((invent->invlet ^ 040) > (obj->invlet ^ 040)) {
+ obj->nobj = invent;
+ invent = obj;
+ } else
+ for(otmp = invent; ; otmp = otmp->nobj) {
+ if(!otmp->nobj ||
+ (otmp->nobj->invlet ^ 040) > (obj->invlet ^ 040)){
+ obj->nobj = otmp->nobj;
+ otmp->nobj = obj;
+ break;
+ }
+ }
+ }
+ }
+
+ return(obj);
+}
+
+useup(obj)
+register struct obj *obj;
+{
+ if(obj->quan > 1){
+ obj->quan--;
+ obj->owt = weight(obj);
+ } else {
+ setnotworn(obj);
+ freeinv(obj);
+ obfree(obj, (struct obj *) 0);
+ }
+}
+
+freeinv(obj)
+register struct obj *obj;
+{
+ register struct obj *otmp;
+
+ if(obj == invent)
+ invent = invent->nobj;
+ else {
+ for(otmp = invent; otmp->nobj != obj; otmp = otmp->nobj)
+ if(!otmp->nobj) panic("freeinv");
+ otmp->nobj = obj->nobj;
+ }
+}
+
+/* destroy object in fobj chain (if unpaid, it remains on the bill) */
+delobj(obj) register struct obj *obj; {
+ freeobj(obj);
+ unpobj(obj);
+ obfree(obj, (struct obj *) 0);
+}
+
+/* unlink obj from chain starting with fobj */
+freeobj(obj) register struct obj *obj; {
+ register struct obj *otmp;
+
+ if(obj == fobj) fobj = fobj->nobj;
+ else {
+ for(otmp = fobj; otmp->nobj != obj; otmp = otmp->nobj)
+ if(!otmp) panic("error in freeobj");
+ otmp->nobj = obj->nobj;
+ }
+}
+
+/* Note: freegold throws away its argument! */
+freegold(gold) register struct gold *gold; {
+ register struct gold *gtmp;
+
+ if(gold == fgold) fgold = gold->ngold;
+ else {
+ for(gtmp = fgold; gtmp->ngold != gold; gtmp = gtmp->ngold)
+ if(!gtmp) panic("error in freegold");
+ gtmp->ngold = gold->ngold;
+ }
+ free((char *) gold);
+}
+
+deltrap(trap)
+register struct trap *trap;
+{
+ register struct trap *ttmp;
+
+ if(trap == ftrap)
+ ftrap = ftrap->ntrap;
+ else {
+ for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ;
+ ttmp->ntrap = trap->ntrap;
+ }
+ free((char *) trap);
+}
+
+struct wseg *m_atseg;
+
+struct monst *
+m_at(x,y)
+register x,y;
+{
+ register struct monst *mtmp;
+#ifndef NOWORM
+ register struct wseg *wtmp;
+#endif NOWORM
+
+ m_atseg = 0;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon){
+ if(mtmp->mx == x && mtmp->my == y)
+ return(mtmp);
+#ifndef NOWORM
+ if(mtmp->wormno){
+ for(wtmp = wsegs[mtmp->wormno]; wtmp; wtmp = wtmp->nseg)
+ if(wtmp->wx == x && wtmp->wy == y){
+ m_atseg = wtmp;
+ return(mtmp);
+ }
+ }
+#endif NOWORM
+ }
+ return(0);
+}
+
+struct obj *
+o_at(x,y)
+register x,y;
+{
+ register struct obj *otmp;
+
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(otmp->ox == x && otmp->oy == y) return(otmp);
+ return(0);
+}
+
+struct obj *
+sobj_at(n,x,y)
+register n,x,y;
+{
+ register struct obj *otmp;
+
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(otmp->ox == x && otmp->oy == y && otmp->otyp == n)
+ return(otmp);
+ return(0);
+}
+
+carried(obj) register struct obj *obj; {
+register struct obj *otmp;
+ for(otmp = invent; otmp; otmp = otmp->nobj)
+ if(otmp == obj) return(1);
+ return(0);
+}
+
+carrying(type)
+register int type;
+{
+ register struct obj *otmp;
+
+ for(otmp = invent; otmp; otmp = otmp->nobj)
+ if(otmp->otyp == type)
+ return(TRUE);
+ return(FALSE);
+}
+
+struct obj *
+o_on(id, objchn) unsigned int id; register struct obj *objchn; {
+ while(objchn) {
+ if(objchn->o_id == id) return(objchn);
+ objchn = objchn->nobj;
+ }
+ return((struct obj *) 0);
+}
+
+struct trap *
+t_at(x,y)
+register x,y;
+{
+ register struct trap *trap = ftrap;
+ while(trap) {
+ if(trap->tx == x && trap->ty == y) return(trap);
+ trap = trap->ntrap;
+ }
+ return(0);
+}
+
+struct gold *
+g_at(x,y)
+register x,y;
+{
+ register struct gold *gold = fgold;
+ while(gold) {
+ if(gold->gx == x && gold->gy == y) return(gold);
+ gold = gold->ngold;
+ }
+ return(0);
+}
+
+/* make dummy object structure containing gold - for temporary use only */
+struct obj *
+mkgoldobj(q)
+register long q;
+{
+ register struct obj *otmp;
+
+ otmp = newobj(0);
+ /* should set o_id etc. but otmp will be freed soon */
+ otmp->olet = '$';
+ u.ugold -= q;
+ OGOLD(otmp) = q;
+ flags.botl = 1;
+ return(otmp);
+}
+
+/*
+ * getobj returns:
+ * struct obj *xxx: object to do something with.
+ * (struct obj *) 0 error return: no object.
+ * &zeroobj explicitly no object (as in w-).
+ */
+struct obj *
+getobj(let,word)
+register char *let,*word;
+{
+ register struct obj *otmp;
+ register char ilet,ilet1,ilet2;
+ char buf[BUFSZ];
+ char lets[BUFSZ];
+ register int foo = 0, foo2;
+ register char *bp = buf;
+ xchar allowcnt = 0; /* 0, 1 or 2 */
+ boolean allowgold = FALSE;
+ boolean allowall = FALSE;
+ boolean allownone = FALSE;
+ xchar foox = 0;
+ long cnt;
+
+ if(*let == '0') let++, allowcnt = 1;
+ if(*let == '$') let++, allowgold = TRUE;
+ if(*let == '#') let++, allowall = TRUE;
+ if(*let == '-') let++, allownone = TRUE;
+ if(allownone) *bp++ = '-';
+ if(allowgold) *bp++ = '$';
+ if(bp > buf && bp[-1] == '-') *bp++ = ' ';
+
+ ilet = 'a';
+ for(otmp = invent; otmp; otmp = otmp->nobj){
+ if(!*let || index(let, otmp->olet)) {
+ bp[foo++] = flags.invlet_constant ? otmp->invlet : ilet;
+
+ /* ugly check: remove inappropriate things */
+ if((!strcmp(word, "take off") &&
+ !(otmp->owornmask & (W_ARMOR - W_ARM2)))
+ || (!strcmp(word, "wear") &&
+ (otmp->owornmask & (W_ARMOR | W_RING)))
+ || (!strcmp(word, "wield") &&
+ (otmp->owornmask & W_WEP))) {
+ foo--;
+ foox++;
+ }
+ }
+ if(ilet == 'z') ilet = 'A'; else ilet++;
+ }
+ bp[foo] = 0;
+ if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0;
+ (void) strcpy(lets, bp); /* necessary since we destroy buf */
+ if(foo > 5) { /* compactify string */
+ foo = foo2 = 1;
+ ilet2 = bp[0];
+ ilet1 = bp[1];
+ while(ilet = bp[++foo2] = bp[++foo]){
+ if(ilet == ilet1+1){
+ if(ilet1 == ilet2+1)
+ bp[foo2 - 1] = ilet1 = '-';
+ else if(ilet2 == '-') {
+ bp[--foo2] = ++ilet1;
+ continue;
+ }
+ }
+ ilet2 = ilet1;
+ ilet1 = ilet;
+ }
+ }
+ if(!foo && !allowall && !allowgold && !allownone) {
+ pline("You don't have anything %sto %s.",
+ foox ? "else " : "", word);
+ return(0);
+ }
+ for(;;) {
+ if(!buf[0])
+ pline("What do you want to %s [*]? ", word);
+ else
+ pline("What do you want to %s [%s or ?*]? ",
+ word, buf);
+
+ cnt = 0;
+ ilet = readchar();
+ while(digit(ilet) && allowcnt) {
+ if (cnt < 100000000)
+ cnt = 10*cnt + (ilet - '0');
+ else
+ cnt = 999999999;
+ allowcnt = 2; /* signal presence of cnt */
+ ilet = readchar();
+ }
+ if(digit(ilet)) {
+ pline("No count allowed with this command.");
+ continue;
+ }
+ if(index(quitchars,ilet))
+ return((struct obj *)0);
+ if(ilet == '-') {
+ return(allownone ? &zeroobj : (struct obj *) 0);
+ }
+ if(ilet == '$') {
+ if(!allowgold){
+ pline("You cannot %s gold.", word);
+ continue;
+ }
+ if(!(allowcnt == 2 && cnt < u.ugold))
+ cnt = u.ugold;
+ return(mkgoldobj(cnt));
+ }
+ if(ilet == '?') {
+ doinv(lets);
+ if(!(ilet = morc)) continue;
+ /* he typed a letter (not a space) to more() */
+ } else if(ilet == '*') {
+ doinv((char *) 0);
+ if(!(ilet = morc)) continue;
+ /* ... */
+ }
+ if(flags.invlet_constant) {
+ for(otmp = invent; otmp; otmp = otmp->nobj)
+ if(otmp->invlet == ilet) break;
+ } else {
+ if(ilet >= 'A' && ilet <= 'Z') ilet += 'z'-'A'+1;
+ ilet -= 'a';
+ for(otmp = invent; otmp && ilet;
+ ilet--, otmp = otmp->nobj) ;
+ }
+ if(!otmp) {
+ pline("You don't have that object.");
+ continue;
+ }
+ if(cnt < 0 || otmp->quan < cnt) {
+ pline("You don't have that many! [You have %u]"
+ , otmp->quan);
+ continue;
+ }
+ break;
+ }
+ if(!allowall && let && !index(let,otmp->olet)) {
+ pline("That is a silly thing to %s.",word);
+ return(0);
+ }
+ if(allowcnt == 2) { /* cnt given */
+ if(cnt == 0) return(0);
+ if(cnt != otmp->quan) {
+ register struct obj *obj;
+ obj = splitobj(otmp, (int) cnt);
+ if(otmp == uwep) setuwep(obj);
+ }
+ }
+ return(otmp);
+}
+
+ckunpaid(otmp) register struct obj *otmp; {
+ return( otmp->unpaid );
+}
+
+/* interactive version of getobj - used for Drop and Identify */
+/* return the number of times fn was called successfully */
+ggetobj(word, fn, max)
+char *word;
+int (*fn)(), max;
+{
+char buf[BUFSZ];
+register char *ip;
+register char sym;
+register int oletct = 0, iletct = 0;
+register boolean allflag = FALSE;
+char olets[20], ilets[20];
+int (*ckfn)() = (int (*)()) 0;
+xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */
+ if(!invent && !allowgold){
+ pline("You have nothing to %s.", word);
+ return(0);
+ } else {
+ register struct obj *otmp = invent;
+ register int uflg = 0;
+
+ if(allowgold) ilets[iletct++] = '$';
+ ilets[iletct] = 0;
+ while(otmp) {
+ if(!index(ilets, otmp->olet)){
+ ilets[iletct++] = otmp->olet;
+ ilets[iletct] = 0;
+ }
+ if(otmp->unpaid) uflg = 1;
+ otmp = otmp->nobj;
+ }
+ ilets[iletct++] = ' ';
+ if(uflg) ilets[iletct++] = 'u';
+ if(invent) ilets[iletct++] = 'a';
+ ilets[iletct] = 0;
+ }
+ pline("What kinds of thing do you want to %s? [%s] ",
+ word, ilets);
+ getlin(buf);
+ if(buf[0] == '\033') {
+ clrlin();
+ return(0);
+ }
+ ip = buf;
+ olets[0] = 0;
+ while(sym = *ip++){
+ if(sym == ' ') continue;
+ if(sym == '$') {
+ if(allowgold == 1)
+ (*fn)(mkgoldobj(u.ugold));
+ else if(!u.ugold)
+ pline("You have no gold.");
+ allowgold = 2;
+ } else
+ if(sym == 'a' || sym == 'A') allflag = TRUE; else
+ if(sym == 'u' || sym == 'U') ckfn = ckunpaid; else
+ if(index("!%?[()=*/\"0", sym)){
+ if(!index(olets, sym)){
+ olets[oletct++] = sym;
+ olets[oletct] = 0;
+ }
+ }
+ else pline("You don't have any %c's.", sym);
+ }
+ if(allowgold == 2 && !oletct)
+ return(1); /* he dropped gold (or at least tried to) */
+ else
+ return(askchain(invent, olets, allflag, fn, ckfn, max));
+}
+
+/*
+ * Walk through the chain starting at objchn and ask for all objects
+ * with olet in olets (if nonNULL) and satisfying ckfn (if nonNULL)
+ * whether the action in question (i.e., fn) has to be performed.
+ * If allflag then no questions are asked. Max gives the max nr of
+ * objects to be treated. Return the number of objects treated.
+ */
+askchain(objchn, olets, allflag, fn, ckfn, max)
+struct obj *objchn;
+register char *olets;
+int allflag;
+int (*fn)(), (*ckfn)();
+int max;
+{
+register struct obj *otmp, *otmp2;
+register char sym, ilet;
+register int cnt = 0;
+ ilet = 'a'-1;
+ for(otmp = objchn; otmp; otmp = otmp2){
+ if(ilet == 'z') ilet = 'A'; else ilet++;
+ otmp2 = otmp->nobj;
+ if(olets && *olets && !index(olets, otmp->olet)) continue;
+ if(ckfn && !(*ckfn)(otmp)) continue;
+ if(!allflag) {
+ pline(xprname(otmp, ilet));
+ addtopl(" [nyaq]? ");
+ sym = readchar();
+ }
+ else sym = 'y';
+
+ switch(sym){
+ case 'a':
+ allflag = 1;
+ case 'y':
+ cnt += (*fn)(otmp);
+ if(--max == 0) goto ret;
+ case 'n':
+ default:
+ break;
+ case 'q':
+ goto ret;
+ }
+ }
+ pline(cnt ? "That was all." : "No applicable objects.");
+ret:
+ return(cnt);
+}
+
+obj_to_let(obj) /* should of course only be called for things in invent */
+register struct obj *obj;
+{
+ register struct obj *otmp;
+ register char ilet;
+
+ if(flags.invlet_constant)
+ return(obj->invlet);
+ ilet = 'a';
+ for(otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
+ if(++ilet > 'z') ilet = 'A';
+ return(otmp ? ilet : NOINVSYM);
+}
+
+prinv(obj)
+register struct obj *obj;
+{
+ pline(xprname(obj, obj_to_let(obj)));
+}
+
+static char *
+xprname(obj,let)
+register struct obj *obj;
+register char let;
+{
+ static char li[BUFSZ];
+
+ (void) sprintf(li, "%c - %s.",
+ flags.invlet_constant ? obj->invlet : let,
+ doname(obj));
+ return(li);
+}
+
+ddoinv()
+{
+ doinv((char *) 0);
+ return(0);
+}
+
+/* called with 0 or "": all objects in inventory */
+/* otherwise: all objects with (serial) letter in lets */
+doinv(lets)
+register char *lets;
+{
+ register struct obj *otmp;
+ register char ilet;
+ int ct = 0;
+ char any[BUFSZ];
+
+ morc = 0; /* just to be sure */
+
+ if(!invent){
+ pline("Not carrying anything.");
+ return;
+ }
+
+ cornline(0, (char *) 0);
+ ilet = 'a';
+ for(otmp = invent; otmp; otmp = otmp->nobj) {
+ if(flags.invlet_constant) ilet = otmp->invlet;
+ if(!lets || !*lets || index(lets, ilet)) {
+ cornline(1, xprname(otmp, ilet));
+ any[ct++] = ilet;
+ }
+ if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A';
+ }
+ any[ct] = 0;
+ cornline(2, any);
+}
+
+dotypeinv () /* free after Robert Viduya */
+/* Changed to one type only, so he doesnt have to type cr */
+{
+ char c, ilet;
+ char stuff[BUFSZ];
+ register int stct;
+ register struct obj *otmp;
+ boolean billx = inshop() && doinvbill(0);
+ boolean unpd = FALSE;
+
+ if (!invent && !u.ugold && !billx) {
+ pline ("You aren't carrying anything.");
+ return(0);
+ }
+
+ stct = 0;
+ if(u.ugold) stuff[stct++] = '$';
+ stuff[stct] = 0;
+ for(otmp = invent; otmp; otmp = otmp->nobj) {
+ if (!index (stuff, otmp->olet)) {
+ stuff[stct++] = otmp->olet;
+ stuff[stct] = 0;
+ }
+ if(otmp->unpaid)
+ unpd = TRUE;
+ }
+ if(unpd) stuff[stct++] = 'u';
+ if(billx) stuff[stct++] = 'x';
+ stuff[stct] = 0;
+
+ if(stct > 1) {
+ pline ("What type of object [%s] do you want an inventory of? ",
+ stuff);
+ c = readchar();
+ if(index(quitchars,c)) return(0);
+ } else
+ c = stuff[0];
+
+ if(c == '$')
+ return(doprgold());
+
+ if(c == 'x' || c == 'X') {
+ if(billx)
+ (void) doinvbill(1);
+ else
+ pline("No used-up objects on the shopping bill.");
+ return(0);
+ }
+
+ if((c == 'u' || c == 'U') && !unpd) {
+ pline("You are not carrying any unpaid objects.");
+ return(0);
+ }
+
+ stct = 0;
+ ilet = 'a';
+ for (otmp = invent; otmp; otmp = otmp -> nobj) {
+ if(flags.invlet_constant) ilet = otmp->invlet;
+ if (c == otmp -> olet || (c == 'u' && otmp -> unpaid))
+ stuff[stct++] = ilet;
+ if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A';
+ }
+ stuff[stct] = '\0';
+ if(stct == 0)
+ pline("You have no such objects.");
+ else
+ doinv (stuff);
+
+ return(0);
+}
+
+/* look at what is here */
+dolook() {
+ register struct obj *otmp, *otmp0;
+ register struct gold *gold;
+ char *verb = Blind ? "feel" : "see";
+ int ct = 0;
+
+ if(!u.uswallow) {
+ if(Blind) {
+ pline("You try to feel what is lying here on the floor.");
+ if(Levitation) { /* ab@unido */
+ pline("You cannot reach the floor!");
+ return(1);
+ }
+ }
+ otmp0 = o_at(u.ux, u.uy);
+ gold = g_at(u.ux, u.uy);
+ }
+
+ if(u.uswallow || (!otmp0 && !gold)) {
+ pline("You %s no objects here.", verb);
+ return(!!Blind);
+ }
+
+ cornline(0, "Things that are here:");
+ for(otmp = otmp0; otmp; otmp = otmp->nobj) {
+ if(otmp->ox == u.ux && otmp->oy == u.uy) {
+ ct++;
+ cornline(1, doname(otmp));
+ if(Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) {
+ pline("Touching the dead cockatrice is a fatal mistake ...");
+ pline("You die ...");
+ killer = "dead cockatrice";
+ done("died");
+ }
+ }
+ }
+
+ if(gold) {
+ char gbuf[30];
+
+ (void) sprintf(gbuf, "%ld gold piece%s",
+ gold->amount, plur(gold->amount));
+ if(!ct++)
+ pline("You %s here %s.", verb, gbuf);
+ else
+ cornline(1, gbuf);
+ }
+
+ if(ct == 1 && !gold) {
+ pline("You %s here %s.", verb, doname(otmp0));
+ cornline(3, (char *) 0);
+ }
+ if(ct > 1)
+ cornline(2, (char *) 0);
+ return(!!Blind);
+}
+
+stackobj(obj) register struct obj *obj; {
+register struct obj *otmp = fobj;
+ for(otmp = fobj; otmp; otmp = otmp->nobj) if(otmp != obj)
+ if(otmp->ox == obj->ox && otmp->oy == obj->oy &&
+ merged(obj,otmp,1))
+ return;
+}
+
+/* merge obj with otmp and delete obj if types agree */
+merged(otmp,obj,lose) register struct obj *otmp, *obj; {
+ if(obj->otyp == otmp->otyp &&
+ obj->unpaid == otmp->unpaid &&
+ obj->spe == otmp->spe &&
+ obj->dknown == otmp->dknown &&
+ obj->cursed == otmp->cursed &&
+ (index("%*?!", obj->olet) ||
+ (obj->known == otmp->known &&
+ (obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) {
+ otmp->quan += obj->quan;
+ otmp->owt += obj->owt;
+ if(lose) freeobj(obj);
+ obfree(obj,otmp); /* free(obj), bill->otmp */
+ return(1);
+ } else return(0);
+}
+
+/*
+ * Gold is no longer displayed; in fact, when you have a lot of money,
+ * it may take a while before you have counted it all.
+ * [Bug: d$ and pickup still tell you how much it was.]
+ */
+extern int (*occupation)();
+extern char *occtxt;
+static long goldcounted;
+
+countgold(){
+ if((goldcounted += 100*(u.ulevel + 1)) >= u.ugold) {
+ long eps = 0;
+ if(!rn2(2)) eps = rnd((int) (u.ugold/100 + 1));
+ pline("You probably have about %ld gold pieces.",
+ u.ugold + eps);
+ return(0); /* done */
+ }
+ return(1); /* continue */
+}
+
+doprgold(){
+ if(!u.ugold)
+ pline("You do not carry any gold.");
+ else if(u.ugold <= 500)
+ pline("You are carrying %ld gold pieces.", u.ugold);
+ else {
+ pline("You sit down in order to count your gold pieces.");
+ goldcounted = 500;
+ occupation = countgold;
+ occtxt = "counting your gold";
+ }
+ return(1);
+}
+
+/* --- end of gold counting section --- */
+
+doprwep(){
+ if(!uwep) pline("You are empty handed.");
+ else prinv(uwep);
+ return(0);
+}
+
+doprarm(){
+ if(!uarm && !uarmg && !uarms && !uarmh)
+ pline("You are not wearing any armor.");
+ else {
+ char lets[6];
+ register int ct = 0;
+
+ if(uarm) lets[ct++] = obj_to_let(uarm);
+ if(uarm2) lets[ct++] = obj_to_let(uarm2);
+ if(uarmh) lets[ct++] = obj_to_let(uarmh);
+ if(uarms) lets[ct++] = obj_to_let(uarms);
+ if(uarmg) lets[ct++] = obj_to_let(uarmg);
+ lets[ct] = 0;
+ doinv(lets);
+ }
+ return(0);
+}
+
+doprring(){
+ if(!uleft && !uright)
+ pline("You are not wearing any rings.");
+ else {
+ char lets[3];
+ register int ct = 0;
+
+ if(uleft) lets[ct++] = obj_to_let(uleft);
+ if(uright) lets[ct++] = obj_to_let(uright);
+ lets[ct] = 0;
+ doinv(lets);
+ }
+ return(0);
+}
+
+digit(c) char c; {
+ return(c >= '0' && c <= '9');
+}
diff --git a/games/hack/hack.ioctl.c b/games/hack/hack.ioctl.c
new file mode 100644
index 000000000000..6669ceaba363
--- /dev/null
+++ b/games/hack/hack.ioctl.c
@@ -0,0 +1,53 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.ioctl.c - version 1.0.2 */
+
+/* This cannot be part of hack.tty.c (as it was earlier) since on some
+ systems (e.g. MUNIX) the include files <termio.h> and <sgtty.h>
+ define the same constants, and the C preprocessor complains. */
+#include <stdio.h>
+#include "config.h"
+#ifdef BSD
+#include <sgtty.h>
+struct ltchars ltchars, ltchars0;
+#else
+#include <termio.h> /* also includes part of <sgtty.h> */
+struct termio termio;
+#endif BSD
+
+getioctls() {
+#ifdef BSD
+ (void) ioctl(fileno(stdin), (int) TIOCGLTC, (char *) &ltchars);
+ (void) ioctl(fileno(stdin), (int) TIOCSLTC, (char *) &ltchars0);
+#else
+ (void) ioctl(fileno(stdin), (int) TCGETA, &termio);
+#endif BSD
+}
+
+setioctls() {
+#ifdef BSD
+ (void) ioctl(fileno(stdin), (int) TIOCSLTC, (char *) &ltchars);
+#else
+ (void) ioctl(fileno(stdin), (int) TCSETA, &termio);
+#endif BSD
+}
+
+#ifdef SUSPEND /* implies BSD */
+dosuspend() {
+#include <signal.h>
+#ifdef SIGTSTP
+ if(signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
+ settty((char *) 0);
+ (void) signal(SIGTSTP, SIG_DFL);
+ (void) kill(0, SIGTSTP);
+ gettty();
+ setftty();
+ docrt();
+ } else {
+ pline("I don't think your shell has job control.");
+ }
+#else SIGTSTP
+ pline("Sorry, it seems we have no SIGTSTP here. Try ! or S.");
+#endif SIGTSTP
+ return(0);
+}
+#endif SUSPEND
diff --git a/games/hack/hack.lev.c b/games/hack/hack.lev.c
new file mode 100644
index 000000000000..f011f675fb8a
--- /dev/null
+++ b/games/hack/hack.lev.c
@@ -0,0 +1,285 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.lev.c - version 1.0.3 */
+
+#include "hack.h"
+#include "def.mkroom.h"
+#include <stdio.h>
+extern struct monst *restmonchn();
+extern struct obj *restobjchn();
+extern struct obj *billobjs;
+extern char *itoa();
+extern char SAVEF[];
+extern int hackpid;
+extern xchar dlevel;
+extern char nul[];
+
+#ifndef NOWORM
+#include "def.wseg.h"
+extern struct wseg *wsegs[32], *wheads[32];
+extern long wgrowtime[32];
+#endif NOWORM
+
+boolean level_exists[MAXLEVEL+1];
+
+savelev(fd,lev)
+int fd;
+xchar lev;
+{
+#ifndef NOWORM
+ register struct wseg *wtmp, *wtmp2;
+ register tmp;
+#endif NOWORM
+
+ if(fd < 0) panic("Save on bad file!"); /* impossible */
+ if(lev >= 0 && lev <= MAXLEVEL)
+ level_exists[lev] = TRUE;
+
+ bwrite(fd,(char *) &hackpid,sizeof(hackpid));
+ bwrite(fd,(char *) &lev,sizeof(lev));
+ bwrite(fd,(char *) levl,sizeof(levl));
+ bwrite(fd,(char *) &moves,sizeof(long));
+ bwrite(fd,(char *) &xupstair,sizeof(xupstair));
+ bwrite(fd,(char *) &yupstair,sizeof(yupstair));
+ bwrite(fd,(char *) &xdnstair,sizeof(xdnstair));
+ bwrite(fd,(char *) &ydnstair,sizeof(ydnstair));
+ savemonchn(fd, fmon);
+ savegoldchn(fd, fgold);
+ savetrapchn(fd, ftrap);
+ saveobjchn(fd, fobj);
+ saveobjchn(fd, billobjs);
+ billobjs = 0;
+ save_engravings(fd);
+#ifndef QUEST
+ bwrite(fd,(char *) rooms,sizeof(rooms));
+ bwrite(fd,(char *) doors,sizeof(doors));
+#endif QUEST
+ fgold = 0;
+ ftrap = 0;
+ fmon = 0;
+ fobj = 0;
+#ifndef NOWORM
+ bwrite(fd,(char *) wsegs,sizeof(wsegs));
+ for(tmp=1; tmp<32; tmp++){
+ for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2){
+ wtmp2 = wtmp->nseg;
+ bwrite(fd,(char *) wtmp,sizeof(struct wseg));
+ }
+ wsegs[tmp] = 0;
+ }
+ bwrite(fd,(char *) wgrowtime,sizeof(wgrowtime));
+#endif NOWORM
+}
+
+bwrite(fd,loc,num)
+register fd;
+register char *loc;
+register unsigned num;
+{
+/* lint wants the 3rd arg of write to be an int; lint -p an unsigned */
+ if(write(fd, loc, (int) num) != num)
+ panic("cannot write %u bytes to file #%d", num, fd);
+}
+
+saveobjchn(fd,otmp)
+register fd;
+register struct obj *otmp;
+{
+ register struct obj *otmp2;
+ unsigned xl;
+ int minusone = -1;
+
+ while(otmp) {
+ otmp2 = otmp->nobj;
+ xl = otmp->onamelth;
+ bwrite(fd, (char *) &xl, sizeof(int));
+ bwrite(fd, (char *) otmp, xl + sizeof(struct obj));
+ free((char *) otmp);
+ otmp = otmp2;
+ }
+ bwrite(fd, (char *) &minusone, sizeof(int));
+}
+
+savemonchn(fd,mtmp)
+register fd;
+register struct monst *mtmp;
+{
+ register struct monst *mtmp2;
+ unsigned xl;
+ int minusone = -1;
+ struct permonst *monbegin = &mons[0];
+
+ bwrite(fd, (char *) &monbegin, sizeof(monbegin));
+
+ while(mtmp) {
+ mtmp2 = mtmp->nmon;
+ xl = mtmp->mxlth + mtmp->mnamelth;
+ bwrite(fd, (char *) &xl, sizeof(int));
+ bwrite(fd, (char *) mtmp, xl + sizeof(struct monst));
+ if(mtmp->minvent) saveobjchn(fd,mtmp->minvent);
+ free((char *) mtmp);
+ mtmp = mtmp2;
+ }
+ bwrite(fd, (char *) &minusone, sizeof(int));
+}
+
+savegoldchn(fd,gold)
+register fd;
+register struct gold *gold;
+{
+ register struct gold *gold2;
+ while(gold) {
+ gold2 = gold->ngold;
+ bwrite(fd, (char *) gold, sizeof(struct gold));
+ free((char *) gold);
+ gold = gold2;
+ }
+ bwrite(fd, nul, sizeof(struct gold));
+}
+
+savetrapchn(fd,trap)
+register fd;
+register struct trap *trap;
+{
+ register struct trap *trap2;
+ while(trap) {
+ trap2 = trap->ntrap;
+ bwrite(fd, (char *) trap, sizeof(struct trap));
+ free((char *) trap);
+ trap = trap2;
+ }
+ bwrite(fd, nul, sizeof(struct trap));
+}
+
+getlev(fd,pid,lev)
+int fd,pid;
+xchar lev;
+{
+ register struct gold *gold;
+ register struct trap *trap;
+#ifndef NOWORM
+ register struct wseg *wtmp;
+#endif NOWORM
+ register tmp;
+ long omoves;
+ int hpid;
+ xchar dlvl;
+
+ /* First some sanity checks */
+ mread(fd, (char *) &hpid, sizeof(hpid));
+ mread(fd, (char *) &dlvl, sizeof(dlvl));
+ if((pid && pid != hpid) || (lev && dlvl != lev)) {
+ pline("Strange, this map is not as I remember it.");
+ pline("Somebody is trying some trickery here ...");
+ pline("This game is void ...");
+ done("tricked");
+ }
+
+ fgold = 0;
+ ftrap = 0;
+ mread(fd, (char *) levl, sizeof(levl));
+ mread(fd, (char *)&omoves, sizeof(omoves));
+ mread(fd, (char *)&xupstair, sizeof(xupstair));
+ mread(fd, (char *)&yupstair, sizeof(yupstair));
+ mread(fd, (char *)&xdnstair, sizeof(xdnstair));
+ mread(fd, (char *)&ydnstair, sizeof(ydnstair));
+
+ fmon = restmonchn(fd);
+
+ /* regenerate animals while on another level */
+ { long tmoves = (moves > omoves) ? moves-omoves : 0;
+ register struct monst *mtmp, *mtmp2;
+ extern char genocided[];
+
+ for(mtmp = fmon; mtmp; mtmp = mtmp2) {
+ long newhp; /* tmoves may be very large */
+
+ mtmp2 = mtmp->nmon;
+ if(index(genocided, mtmp->data->mlet)) {
+ mondead(mtmp);
+ continue;
+ }
+
+ if(mtmp->mtame && tmoves > 250) {
+ mtmp->mtame = 0;
+ mtmp->mpeaceful = 0;
+ }
+
+ newhp = mtmp->mhp +
+ (index(MREGEN, mtmp->data->mlet) ? tmoves : tmoves/20);
+ if(newhp > mtmp->mhpmax)
+ mtmp->mhp = mtmp->mhpmax;
+ else
+ mtmp->mhp = newhp;
+ }
+ }
+
+ setgd();
+ gold = newgold();
+ mread(fd, (char *)gold, sizeof(struct gold));
+ while(gold->gx) {
+ gold->ngold = fgold;
+ fgold = gold;
+ gold = newgold();
+ mread(fd, (char *)gold, sizeof(struct gold));
+ }
+ free((char *) gold);
+ trap = newtrap();
+ mread(fd, (char *)trap, sizeof(struct trap));
+ while(trap->tx) {
+ trap->ntrap = ftrap;
+ ftrap = trap;
+ trap = newtrap();
+ mread(fd, (char *)trap, sizeof(struct trap));
+ }
+ free((char *) trap);
+ fobj = restobjchn(fd);
+ billobjs = restobjchn(fd);
+ rest_engravings(fd);
+#ifndef QUEST
+ mread(fd, (char *)rooms, sizeof(rooms));
+ mread(fd, (char *)doors, sizeof(doors));
+#endif QUEST
+#ifndef NOWORM
+ mread(fd, (char *)wsegs, sizeof(wsegs));
+ for(tmp = 1; tmp < 32; tmp++) if(wsegs[tmp]){
+ wheads[tmp] = wsegs[tmp] = wtmp = newseg();
+ while(1) {
+ mread(fd, (char *)wtmp, sizeof(struct wseg));
+ if(!wtmp->nseg) break;
+ wheads[tmp]->nseg = wtmp = newseg();
+ wheads[tmp] = wtmp;
+ }
+ }
+ mread(fd, (char *)wgrowtime, sizeof(wgrowtime));
+#endif NOWORM
+}
+
+mread(fd, buf, len)
+register fd;
+register char *buf;
+register unsigned len;
+{
+ register int rlen;
+ extern boolean restoring;
+
+ rlen = read(fd, buf, (int) len);
+ if(rlen != len){
+ pline("Read %d instead of %u bytes.\n", rlen, len);
+ if(restoring) {
+ (void) unlink(SAVEF);
+ error("Error restoring old game.");
+ }
+ panic("Error reading level file.");
+ }
+}
+
+mklev()
+{
+ extern boolean in_mklev;
+
+ if(getbones()) return;
+
+ in_mklev = TRUE;
+ makelevel();
+ in_mklev = FALSE;
+}
diff --git a/games/hack/hack.main.c b/games/hack/hack.main.c
new file mode 100644
index 000000000000..d2d59a2f3356
--- /dev/null
+++ b/games/hack/hack.main.c
@@ -0,0 +1,499 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.main.c - version 1.0.3 */
+
+#include <stdio.h>
+#include <signal.h>
+#include "hack.h"
+
+#ifdef QUEST
+#define gamename "quest"
+#else
+#define gamename "hack"
+#endif
+
+extern char *getlogin(), *getenv();
+extern char plname[PL_NSIZ], pl_character[PL_CSIZ];
+extern struct permonst mons[CMNUM+2];
+extern char genocided[], fut_geno[];
+
+int (*afternmv)();
+int (*occupation)();
+char *occtxt; /* defined when occupation != NULL */
+
+void done1();
+void hangup();
+
+int hackpid; /* current pid */
+int locknum; /* max num of players */
+#ifdef DEF_PAGER
+char *catmore; /* default pager */
+#endif
+char SAVEF[PL_NSIZ + 11] = "save/"; /* save/99999player */
+char *hname; /* name of the game (argv[0] of call) */
+char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */
+
+extern char *nomovemsg;
+extern long wailmsg;
+
+#ifdef CHDIR
+static void chdirx();
+#endif
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ register int fd;
+#ifdef CHDIR
+ register char *dir;
+#endif
+
+ hname = argv[0];
+ hackpid = getpid();
+
+#ifdef CHDIR /* otherwise no chdir() */
+ /*
+ * See if we must change directory to the playground.
+ * (Perhaps hack runs suid and playground is inaccessible
+ * for the player.)
+ * The environment variable HACKDIR is overridden by a
+ * -d command line option (must be the first option given)
+ */
+
+ dir = getenv("HACKDIR");
+ if(argc > 1 && !strncmp(argv[1], "-d", 2)) {
+ argc--;
+ argv++;
+ dir = argv[0]+2;
+ if(*dir == '=' || *dir == ':') dir++;
+ if(!*dir && argc > 1) {
+ argc--;
+ argv++;
+ dir = argv[0];
+ }
+ if(!*dir)
+ error("Flag -d must be followed by a directory name.");
+ }
+#endif
+
+ /*
+ * Who am i? Algorithm: 1. Use name as specified in HACKOPTIONS
+ * 2. Use $USER or $LOGNAME (if 1. fails)
+ * 3. Use getlogin() (if 2. fails)
+ * The resulting name is overridden by command line options.
+ * If everything fails, or if the resulting name is some generic
+ * account like "games", "play", "player", "hack" then eventually
+ * we'll ask him.
+ * Note that we trust him here; it is possible to play under
+ * somebody else's name.
+ */
+ { register char *s;
+
+ initoptions();
+ if(!*plname && (s = getenv("USER")))
+ (void) strncpy(plname, s, sizeof(plname)-1);
+ if(!*plname && (s = getenv("LOGNAME")))
+ (void) strncpy(plname, s, sizeof(plname)-1);
+ if(!*plname && (s = getlogin()))
+ (void) strncpy(plname, s, sizeof(plname)-1);
+ }
+
+ /*
+ * Now we know the directory containing 'record' and
+ * may do a prscore().
+ */
+ if(argc > 1 && !strncmp(argv[1], "-s", 2)) {
+#ifdef CHDIR
+ chdirx(dir,0);
+#endif
+ prscore(argc, argv);
+ exit(0);
+ }
+
+ /*
+ * It seems he really wants to play.
+ * Remember tty modes, to be restored on exit.
+ */
+ gettty();
+ setbuf(stdout,obuf);
+ setrandom();
+ startup();
+ cls();
+ u.uhp = 1; /* prevent RIP on early quits */
+ u.ux = FAR; /* prevent nscr() */
+ (void) signal(SIGHUP, hangup);
+
+ /*
+ * Find the creation date of this game,
+ * so as to avoid restoring outdated savefiles.
+ */
+ gethdate(hname);
+
+ /*
+ * We cannot do chdir earlier, otherwise gethdate will fail.
+ */
+#ifdef CHDIR
+ chdirx(dir,1);
+#endif
+
+ /*
+ * Process options.
+ */
+ while(argc > 1 && argv[1][0] == '-'){
+ argv++;
+ argc--;
+ switch(argv[0][1]){
+#ifdef WIZARD
+ case 'D':
+/* if(!strcmp(getlogin(), WIZARD)) */
+ wizard = TRUE;
+/* else
+ printf("Sorry.\n"); */
+ break;
+#endif
+#ifdef NEWS
+ case 'n':
+ flags.nonews = TRUE;
+ break;
+#endif
+ case 'u':
+ if(argv[0][2])
+ (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
+ else if(argc > 1) {
+ argc--;
+ argv++;
+ (void) strncpy(plname, argv[0], sizeof(plname)-1);
+ } else
+ printf("Player name expected after -u\n");
+ break;
+ default:
+ /* allow -T for Tourist, etc. */
+ (void) strncpy(pl_character, argv[0]+1,
+ sizeof(pl_character)-1);
+
+ /* printf("Unknown option: %s\n", *argv); */
+ }
+ }
+
+ if(argc > 1)
+ locknum = atoi(argv[1]);
+#ifdef MAX_NR_OF_PLAYERS
+ if(!locknum || locknum > MAX_NR_OF_PLAYERS)
+ locknum = MAX_NR_OF_PLAYERS;
+#endif
+#ifdef DEF_PAGER
+ if(!(catmore = getenv("HACKPAGER")) && !(catmore = getenv("PAGER")))
+ catmore = DEF_PAGER;
+#endif
+#ifdef MAIL
+ getmailstatus();
+#endif
+#ifdef WIZARD
+ if(wizard) (void) strcpy(plname, "wizard"); else
+#endif
+ if(!*plname || !strncmp(plname, "player", 4)
+ || !strncmp(plname, "games", 4))
+ askname();
+ plnamesuffix(); /* strip suffix from name; calls askname() */
+ /* again if suffix was whole name */
+ /* accepts any suffix */
+#ifdef WIZARD
+ if(!wizard) {
+#endif
+ /*
+ * check for multiple games under the same name
+ * (if !locknum) or check max nr of players (otherwise)
+ */
+ (void) signal(SIGQUIT,SIG_IGN);
+ (void) signal(SIGINT,SIG_IGN);
+ if(!locknum)
+ (void) strcpy(lock,plname);
+ getlock(); /* sets lock if locknum != 0 */
+#ifdef WIZARD
+ } else {
+ register char *sfoo;
+ (void) strcpy(lock,plname);
+ if(sfoo = getenv("MAGIC"))
+ while(*sfoo) {
+ switch(*sfoo++) {
+ case 'n': (void) srandom(*sfoo++);
+ break;
+ }
+ }
+ if(sfoo = getenv("GENOCIDED")){
+ if(*sfoo == '!'){
+ register struct permonst *pm = mons;
+ register char *gp = genocided;
+
+ while(pm < mons+CMNUM+2){
+ if(!index(sfoo, pm->mlet))
+ *gp++ = pm->mlet;
+ pm++;
+ }
+ *gp = 0;
+ } else
+ (void) strcpy(genocided, sfoo);
+ (void) strcpy(fut_geno, genocided);
+ }
+ }
+#endif
+ setftty();
+ (void) sprintf(SAVEF, "save/%d%s", getuid(), plname);
+ regularize(SAVEF+5); /* avoid . or / in name */
+ if((fd = open(SAVEF,0)) >= 0 &&
+ (uptodate(fd) || unlink(SAVEF) == 666)) {
+ (void) signal(SIGINT,done1);
+ pline("Restoring old save file...");
+ (void) fflush(stdout);
+ if(!dorecover(fd))
+ goto not_recovered;
+ pline("Hello %s, welcome to %s!", plname, gamename);
+ flags.move = 0;
+ } else {
+not_recovered:
+ fobj = fcobj = invent = 0;
+ fmon = fallen_down = 0;
+ ftrap = 0;
+ fgold = 0;
+ flags.ident = 1;
+ init_objects();
+ u_init();
+
+ (void) signal(SIGINT,done1);
+ mklev();
+ u.ux = xupstair;
+ u.uy = yupstair;
+ (void) inshop();
+ setsee();
+ flags.botlx = 1;
+ makedog();
+ { register struct monst *mtmp;
+ if(mtmp = m_at(u.ux, u.uy)) mnexto(mtmp); /* riv05!a3 */
+ }
+ seemons();
+#ifdef NEWS
+ if(flags.nonews || !readnews())
+ /* after reading news we did docrt() already */
+#endif
+ docrt();
+
+ /* give welcome message before pickup messages */
+ pline("Hello %s, welcome to %s!", plname, gamename);
+
+ pickup(1);
+ read_engr_at(u.ux,u.uy);
+ flags.move = 1;
+ }
+
+ flags.moonphase = phase_of_the_moon();
+ if(flags.moonphase == FULL_MOON) {
+ pline("You are lucky! Full moon tonight.");
+ u.uluck++;
+ } else if(flags.moonphase == NEW_MOON) {
+ pline("Be careful! New moon tonight.");
+ }
+
+ initrack();
+
+ for(;;) {
+ if(flags.move) { /* actual time passed */
+
+ settrack();
+
+ if(moves%2 == 0 ||
+ (!(Fast & ~INTRINSIC) && (!Fast || rn2(3)))) {
+ extern struct monst *makemon();
+ movemon();
+ if(!rn2(70))
+ (void) makemon((struct permonst *)0, 0, 0);
+ }
+ if(Glib) glibr();
+ timeout();
+ ++moves;
+ if(flags.time) flags.botl = 1;
+ if(u.uhp < 1) {
+ pline("You die...");
+ done("died");
+ }
+ if(u.uhp*10 < u.uhpmax && moves-wailmsg > 50){
+ wailmsg = moves;
+ if(u.uhp == 1)
+ pline("You hear the wailing of the Banshee...");
+ else
+ pline("You hear the howling of the CwnAnnwn...");
+ }
+ if(u.uhp < u.uhpmax) {
+ if(u.ulevel > 9) {
+ if(Regeneration || !(moves%3)) {
+ flags.botl = 1;
+ u.uhp += rnd((int) u.ulevel-9);
+ if(u.uhp > u.uhpmax)
+ u.uhp = u.uhpmax;
+ }
+ } else if(Regeneration ||
+ (!(moves%(22-u.ulevel*2)))) {
+ flags.botl = 1;
+ u.uhp++;
+ }
+ }
+ if(Teleportation && !rn2(85)) tele();
+ if(Searching && multi >= 0) (void) dosearch();
+ gethungry();
+ invault();
+ amulet();
+ }
+ if(multi < 0) {
+ if(!++multi){
+ pline(nomovemsg ? nomovemsg :
+ "You can move again.");
+ nomovemsg = 0;
+ if(afternmv) (*afternmv)();
+ afternmv = 0;
+ }
+ }
+
+ find_ac();
+#ifndef QUEST
+ if(!flags.mv || Blind)
+#endif
+ {
+ seeobjs();
+ seemons();
+ nscr();
+ }
+ if(flags.botl || flags.botlx) bot();
+
+ flags.move = 1;
+
+ if(multi >= 0 && occupation) {
+ if(monster_nearby())
+ stop_occupation();
+ else if ((*occupation)() == 0)
+ occupation = 0;
+ continue;
+ }
+
+ if(multi > 0) {
+#ifdef QUEST
+ if(flags.run >= 4) finddir();
+#endif
+ lookaround();
+ if(!multi) { /* lookaround may clear multi */
+ flags.move = 0;
+ continue;
+ }
+ if(flags.mv) {
+ if(multi < COLNO && !--multi)
+ flags.mv = flags.run = 0;
+ domove();
+ } else {
+ --multi;
+ rhack(save_cm);
+ }
+ } else if(multi == 0) {
+#ifdef MAIL
+ ckmailstatus();
+#endif
+ rhack((char *) 0);
+ }
+ if(multi && multi%7 == 0)
+ (void) fflush(stdout);
+ }
+}
+
+glo(foo)
+register foo;
+{
+ /* construct the string xlock.n */
+ register char *tf;
+
+ tf = lock;
+ while(*tf && *tf != '.') tf++;
+ (void) sprintf(tf, ".%d", foo);
+}
+
+/*
+ * plname is filled either by an option (-u Player or -uPlayer) or
+ * explicitly (-w implies wizard) or by askname.
+ * It may still contain a suffix denoting pl_character.
+ */
+askname(){
+register int c,ct;
+ printf("\nWho are you? ");
+ (void) fflush(stdout);
+ ct = 0;
+ while((c = getchar()) != '\n'){
+ if(c == EOF) error("End of input\n");
+ /* some people get confused when their erase char is not ^H */
+ if(c == '\010') {
+ if(ct) ct--;
+ continue;
+ }
+ if(c != '-')
+ if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_';
+ if(ct < sizeof(plname)-1) plname[ct++] = c;
+ }
+ plname[ct] = 0;
+ if(ct == 0) askname();
+}
+
+/*VARARGS1*/
+impossible(s,x1,x2)
+register char *s;
+{
+ pline(s,x1,x2);
+ pline("Program in disorder - perhaps you'd better Quit.");
+}
+
+#ifdef CHDIR
+static void
+chdirx(dir, wr)
+char *dir;
+boolean wr;
+{
+
+#ifdef SECURE
+ if(dir /* User specified directory? */
+#ifdef HACKDIR
+ && strcmp(dir, HACKDIR) /* and not the default? */
+#endif
+ ) {
+ (void) setuid(getuid()); /* Ron Wessels */
+ (void) setgid(getgid());
+ }
+#endif
+
+#ifdef HACKDIR
+ if(dir == NULL)
+ dir = HACKDIR;
+#endif
+
+ if(dir && chdir(dir) < 0) {
+ perror(dir);
+ error("Cannot chdir to %s.", dir);
+ }
+
+ /* warn the player if he cannot write the record file */
+ /* perhaps we should also test whether . is writable */
+ /* unfortunately the access systemcall is worthless */
+ if(wr) {
+ register fd;
+
+ if(dir == NULL)
+ dir = ".";
+ if((fd = open(RECORD, 2)) < 0) {
+ printf("Warning: cannot write %s/%s", dir, RECORD);
+ getret();
+ } else
+ (void) close(fd);
+ }
+}
+#endif
+
+stop_occupation()
+{
+ if(occupation) {
+ pline("You stop %s.", occtxt);
+ occupation = 0;
+ }
+}
diff --git a/games/hack/hack.makemon.c b/games/hack/hack.makemon.c
new file mode 100644
index 000000000000..bcf23b621d74
--- /dev/null
+++ b/games/hack/hack.makemon.c
@@ -0,0 +1,198 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.makemon.c - version 1.0.2 */
+
+#include "hack.h"
+extern char fut_geno[];
+extern char *index();
+extern struct obj *mkobj_at();
+struct monst zeromonst;
+
+/*
+ * called with [x,y] = coordinates;
+ * [0,0] means anyplace
+ * [u.ux,u.uy] means: call mnexto (if !in_mklev)
+ *
+ * In case we make an Orc or killer bee, we make an entire horde (swarm);
+ * note that in this case we return only one of them (the one at [x,y]).
+ */
+struct monst *
+makemon(ptr,x,y)
+register struct permonst *ptr;
+{
+ register struct monst *mtmp;
+ register tmp, ct;
+ boolean anything = (!ptr);
+ extern boolean in_mklev;
+
+ if(x != 0 || y != 0) if(m_at(x,y)) return((struct monst *) 0);
+ if(ptr){
+ if(index(fut_geno, ptr->mlet)) return((struct monst *) 0);
+ } else {
+ ct = CMNUM - strlen(fut_geno);
+ if(index(fut_geno, 'm')) ct++; /* make only 1 minotaur */
+ if(index(fut_geno, '@')) ct++;
+ if(ct <= 0) return(0); /* no more monsters! */
+ tmp = rn2(ct*dlevel/24 + 7);
+ if(tmp < dlevel - 4) tmp = rn2(ct*dlevel/24 + 12);
+ if(tmp >= ct) tmp = rn1(ct - ct/2, ct/2);
+ for(ct = 0; ct < CMNUM; ct++){
+ ptr = &mons[ct];
+ if(index(fut_geno, ptr->mlet))
+ continue;
+ if(!tmp--) goto gotmon;
+ }
+ panic("makemon?");
+ }
+gotmon:
+ mtmp = newmonst(ptr->pxlth);
+ *mtmp = zeromonst; /* clear all entries in structure */
+ for(ct = 0; ct < ptr->pxlth; ct++)
+ ((char *) &(mtmp->mextra[0]))[ct] = 0;
+ mtmp->nmon = fmon;
+ fmon = mtmp;
+ mtmp->m_id = flags.ident++;
+ mtmp->data = ptr;
+ mtmp->mxlth = ptr->pxlth;
+ if(ptr->mlet == 'D') mtmp->mhpmax = mtmp->mhp = 80;
+ else if(!ptr->mlevel) mtmp->mhpmax = mtmp->mhp = rnd(4);
+ else mtmp->mhpmax = mtmp->mhp = d(ptr->mlevel, 8);
+ mtmp->mx = x;
+ mtmp->my = y;
+ mtmp->mcansee = 1;
+ if(ptr->mlet == 'M'){
+ mtmp->mimic = 1;
+ mtmp->mappearance = ']';
+ }
+ if(!in_mklev) {
+ if(x == u.ux && y == u.uy && ptr->mlet != ' ')
+ mnexto(mtmp);
+ if(x == 0 && y == 0)
+ rloc(mtmp);
+ }
+ if(ptr->mlet == 's' || ptr->mlet == 'S') {
+ mtmp->mhide = mtmp->mundetected = 1;
+ if(in_mklev)
+ if(mtmp->mx && mtmp->my)
+ (void) mkobj_at(0, mtmp->mx, mtmp->my);
+ }
+ if(ptr->mlet == ':') {
+ mtmp->cham = 1;
+ (void) newcham(mtmp, &mons[dlevel+14+rn2(CMNUM-14-dlevel)]);
+ }
+ if(ptr->mlet == 'I' || ptr->mlet == ';')
+ mtmp->minvis = 1;
+ if(ptr->mlet == 'L' || ptr->mlet == 'N'
+ || (in_mklev && index("&w;", ptr->mlet) && rn2(5))
+ ) mtmp->msleep = 1;
+
+#ifndef NOWORM
+ if(ptr->mlet == 'w' && getwn(mtmp))
+ initworm(mtmp);
+#endif NOWORM
+
+ if(anything) if(ptr->mlet == 'O' || ptr->mlet == 'k') {
+ coord enexto();
+ coord mm;
+ register int cnt = rnd(10);
+ mm.x = x;
+ mm.y = y;
+ while(cnt--) {
+ mm = enexto(mm.x, mm.y);
+ (void) makemon(ptr, mm.x, mm.y);
+ }
+ }
+
+ return(mtmp);
+}
+
+coord
+enexto(xx,yy)
+register xchar xx,yy;
+{
+ register xchar x,y;
+ coord foo[15], *tfoo;
+ int range;
+
+ tfoo = foo;
+ range = 1;
+ do { /* full kludge action. */
+ for(x = xx-range; x <= xx+range; x++)
+ if(goodpos(x, yy-range)) {
+ tfoo->x = x;
+ tfoo++->y = yy-range;
+ if(tfoo == &foo[15]) goto foofull;
+ }
+ for(x = xx-range; x <= xx+range; x++)
+ if(goodpos(x,yy+range)) {
+ tfoo->x = x;
+ tfoo++->y = yy+range;
+ if(tfoo == &foo[15]) goto foofull;
+ }
+ for(y = yy+1-range; y < yy+range; y++)
+ if(goodpos(xx-range,y)) {
+ tfoo->x = xx-range;
+ tfoo++->y = y;
+ if(tfoo == &foo[15]) goto foofull;
+ }
+ for(y = yy+1-range; y < yy+range; y++)
+ if(goodpos(xx+range,y)) {
+ tfoo->x = xx+range;
+ tfoo++->y = y;
+ if(tfoo == &foo[15]) goto foofull;
+ }
+ range++;
+ } while(tfoo == foo);
+foofull:
+ return( foo[rn2(tfoo-foo)] );
+}
+
+goodpos(x,y) /* used only in mnexto and rloc */
+{
+ return(
+ ! (x < 1 || x > COLNO-2 || y < 1 || y > ROWNO-2 ||
+ m_at(x,y) || !ACCESSIBLE(levl[x][y].typ)
+ || (x == u.ux && y == u.uy)
+ || sobj_at(ENORMOUS_ROCK, x, y)
+ ));
+}
+
+rloc(mtmp)
+struct monst *mtmp;
+{
+ register tx,ty;
+ register char ch = mtmp->data->mlet;
+
+#ifndef NOWORM
+ if(ch == 'w' && mtmp->mx) return; /* do not relocate worms */
+#endif NOWORM
+ do {
+ tx = rn1(COLNO-3,2);
+ ty = rn2(ROWNO);
+ } while(!goodpos(tx,ty));
+ mtmp->mx = tx;
+ mtmp->my = ty;
+ if(u.ustuck == mtmp){
+ if(u.uswallow) {
+ u.ux = tx;
+ u.uy = ty;
+ docrt();
+ } else u.ustuck = 0;
+ }
+ pmon(mtmp);
+}
+
+struct monst *
+mkmon_at(let,x,y)
+char let;
+register int x,y;
+{
+ register int ct;
+ register struct permonst *ptr;
+
+ for(ct = 0; ct < CMNUM; ct++) {
+ ptr = &mons[ct];
+ if(ptr->mlet == let)
+ return(makemon(ptr,x,y));
+ }
+ return(0);
+}
diff --git a/games/hack/hack.mfndpos.h b/games/hack/hack.mfndpos.h
new file mode 100644
index 000000000000..f4da529fc7fc
--- /dev/null
+++ b/games/hack/hack.mfndpos.h
@@ -0,0 +1,12 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mfndpos.h - version 1.0.2 */
+
+#define ALLOW_TRAPS 0777
+#define ALLOW_U 01000
+#define ALLOW_M 02000
+#define ALLOW_TM 04000
+#define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS)
+#define ALLOW_SSM 010000
+#define ALLOW_ROCK 020000
+#define NOTONL 040000
+#define NOGARLIC 0100000
diff --git a/games/hack/hack.mhitu.c b/games/hack/hack.mhitu.c
new file mode 100644
index 000000000000..ae7d204b17e7
--- /dev/null
+++ b/games/hack/hack.mhitu.c
@@ -0,0 +1,363 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mhitu.c - version 1.0.3 */
+
+#include "hack.h"
+extern struct monst *makemon();
+
+/*
+ * mhitu: monster hits you
+ * returns 1 if monster dies (e.g. 'y', 'F'), 0 otherwise
+ */
+mhitu(mtmp)
+register struct monst *mtmp;
+{
+ register struct permonst *mdat = mtmp->data;
+ register int tmp, ctmp;
+
+ nomul(0);
+
+ /* If swallowed, can only be affected by hissers and by u.ustuck */
+ if(u.uswallow) {
+ if(mtmp != u.ustuck) {
+ if(mdat->mlet == 'c' && !rn2(13)) {
+ pline("Outside, you hear %s's hissing!",
+ monnam(mtmp));
+ pline("%s gets turned to stone!",
+ Monnam(u.ustuck));
+ pline("And the same fate befalls you.");
+ done_in_by(mtmp);
+ /* "notreached": not return(1); */
+ }
+ return(0);
+ }
+ switch(mdat->mlet) { /* now mtmp == u.ustuck */
+ case ',':
+ youswld(mtmp, (u.uac > 0) ? u.uac+4 : 4,
+ 5, "The trapper");
+ break;
+ case '\'':
+ youswld(mtmp,rnd(6),7,"The lurker above");
+ break;
+ case 'P':
+ youswld(mtmp,d(2,4),12,"The purple worm");
+ break;
+ default:
+ /* This is not impossible! */
+ pline("The mysterious monster totally digests you.");
+ u.uhp = 0;
+ }
+ if(u.uhp < 1) done_in_by(mtmp);
+ return(0);
+ }
+
+ if(mdat->mlet == 'c' && Stoned)
+ return(0);
+
+ /* make eels visible the moment they hit/miss us */
+ if(mdat->mlet == ';' && mtmp->minvis && cansee(mtmp->mx,mtmp->my)){
+ mtmp->minvis = 0;
+ pmon(mtmp);
+ }
+ if(!index("1&DuxynNF",mdat->mlet))
+ tmp = hitu(mtmp,d(mdat->damn,mdat->damd));
+ else
+ tmp = 0;
+ if(index(UNDEAD, mdat->mlet) && midnight())
+ tmp += hitu(mtmp,d(mdat->damn,mdat->damd));
+
+ ctmp = tmp && !mtmp->mcan &&
+ (!uarm || objects[uarm->otyp].a_can < rnd(3) || !rn2(50));
+ switch(mdat->mlet) {
+ case '1':
+ if(wiz_hit(mtmp)) return(1); /* he disappeared */
+ break;
+ case '&':
+ if(!mtmp->cham && !mtmp->mcan && !rn2(13)) {
+ (void) makemon(PM_DEMON,u.ux,u.uy);
+ } else {
+ (void) hitu(mtmp,d(2,6));
+ (void) hitu(mtmp,d(2,6));
+ (void) hitu(mtmp,rnd(3));
+ (void) hitu(mtmp,rnd(3));
+ (void) hitu(mtmp,rn1(4,2));
+ }
+ break;
+ case ',':
+ if(tmp) justswld(mtmp,"The trapper");
+ break;
+ case '\'':
+ if(tmp) justswld(mtmp, "The lurker above");
+ break;
+ case ';':
+ if(ctmp) {
+ if(!u.ustuck && !rn2(10)) {
+ pline("%s swings itself around you!",
+ Monnam(mtmp));
+ u.ustuck = mtmp;
+ } else if(u.ustuck == mtmp &&
+ levl[mtmp->mx][mtmp->my].typ == POOL) {
+ pline("%s drowns you ...", Monnam(mtmp));
+ done("drowned");
+ }
+ }
+ break;
+ case 'A':
+ if(ctmp && rn2(2)) {
+ if(Poison_resistance)
+ pline("The sting doesn't seem to affect you.");
+ else {
+ pline("You feel weaker!");
+ losestr(1);
+ }
+ }
+ break;
+ case 'C':
+ (void) hitu(mtmp,rnd(6));
+ break;
+ case 'c':
+ if(!rn2(5)) {
+ pline("You hear %s's hissing!", monnam(mtmp));
+ if(ctmp || !rn2(20) || (flags.moonphase == NEW_MOON
+ && !carrying(DEAD_LIZARD))) {
+ Stoned = 5;
+ /* pline("You get turned to stone!"); */
+ /* done_in_by(mtmp); */
+ }
+ }
+ break;
+ case 'D':
+ if(rn2(6) || mtmp->mcan) {
+ (void) hitu(mtmp,d(3,10));
+ (void) hitu(mtmp,rnd(8));
+ (void) hitu(mtmp,rnd(8));
+ break;
+ }
+ kludge("%s breathes fire!","The dragon");
+ buzz(-1,mtmp->mx,mtmp->my,u.ux-mtmp->mx,u.uy-mtmp->my);
+ break;
+ case 'd':
+ (void) hitu(mtmp,d(2, (flags.moonphase == FULL_MOON) ? 3 : 4));
+ break;
+ case 'e':
+ (void) hitu(mtmp,d(3,6));
+ break;
+ case 'F':
+ if(mtmp->mcan) break;
+ kludge("%s explodes!","The freezing sphere");
+ if(Cold_resistance) pline("You don't seem affected by it.");
+ else {
+ xchar dn;
+ if(17-(u.ulevel/2) > rnd(20)) {
+ pline("You get blasted!");
+ dn = 6;
+ } else {
+ pline("You duck the blast...");
+ dn = 3;
+ }
+ losehp_m(d(dn,6), mtmp);
+ }
+ mondead(mtmp);
+ return(1);
+ case 'g':
+ if(ctmp && multi >= 0 && !rn2(3)) {
+ kludge("You are frozen by %ss juices","the cube'");
+ nomul(-rnd(10));
+ }
+ break;
+ case 'h':
+ if(ctmp && multi >= 0 && !rn2(5)) {
+ nomul(-rnd(10));
+ kludge("You are put to sleep by %ss bite!",
+ "the homunculus'");
+ }
+ break;
+ case 'j':
+ tmp = hitu(mtmp,rnd(3));
+ tmp &= hitu(mtmp,rnd(3));
+ if(tmp){
+ (void) hitu(mtmp,rnd(4));
+ (void) hitu(mtmp,rnd(4));
+ }
+ break;
+ case 'k':
+ if((hitu(mtmp,rnd(4)) || !rn2(3)) && ctmp){
+ poisoned("bee's sting",mdat->mname);
+ }
+ break;
+ case 'L':
+ if(tmp) stealgold(mtmp);
+ break;
+ case 'N':
+ if(mtmp->mcan && !Blind) {
+ pline("%s tries to seduce you, but you seem not interested.",
+ Amonnam(mtmp, "plain"));
+ if(rn2(3)) rloc(mtmp);
+ } else if(steal(mtmp)) {
+ rloc(mtmp);
+ mtmp->mflee = 1;
+ }
+ break;
+ case 'n':
+ if(!uwep && !uarm && !uarmh && !uarms && !uarmg) {
+ pline("%s hits! (I hope you don't mind)",
+ Monnam(mtmp));
+ u.uhp += rnd(7);
+ if(!rn2(7)) u.uhpmax++;
+ if(u.uhp > u.uhpmax) u.uhp = u.uhpmax;
+ flags.botl = 1;
+ if(!rn2(50)) rloc(mtmp);
+ } else {
+ (void) hitu(mtmp,d(2,6));
+ (void) hitu(mtmp,d(2,6));
+ }
+ break;
+ case 'o':
+ tmp = hitu(mtmp,rnd(6));
+ if(hitu(mtmp,rnd(6)) && tmp && /* hits with both paws */
+ !u.ustuck && rn2(2)) {
+ u.ustuck = mtmp;
+ kludge("%s has grabbed you!","The owlbear");
+ u.uhp -= d(2,8);
+ } else if(u.ustuck == mtmp) {
+ u.uhp -= d(2,8);
+ pline("You are being crushed.");
+ }
+ break;
+ case 'P':
+ if(ctmp && !rn2(4))
+ justswld(mtmp,"The purple worm");
+ else
+ (void) hitu(mtmp,d(2,4));
+ break;
+ case 'Q':
+ (void) hitu(mtmp,rnd(2));
+ (void) hitu(mtmp,rnd(2));
+ break;
+ case 'R':
+ if(tmp && uarmh && !uarmh->rustfree &&
+ (int) uarmh->spe >= -1) {
+ pline("Your helmet rusts!");
+ uarmh->spe--;
+ } else
+ if(ctmp && uarm && !uarm->rustfree && /* Mike Newton */
+ uarm->otyp < STUDDED_LEATHER_ARMOR &&
+ (int) uarm->spe >= -1) {
+ pline("Your armor rusts!");
+ uarm->spe--;
+ }
+ break;
+ case 'S':
+ if(ctmp && !rn2(8)) {
+ poisoned("snake's bite",mdat->mname);
+ }
+ break;
+ case 's':
+ if(tmp && !rn2(8)) {
+ poisoned("scorpion's sting",mdat->mname);
+ }
+ (void) hitu(mtmp,rnd(8));
+ (void) hitu(mtmp,rnd(8));
+ break;
+ case 'T':
+ (void) hitu(mtmp,rnd(6));
+ (void) hitu(mtmp,rnd(6));
+ break;
+ case 't':
+ if(!rn2(5)) rloc(mtmp);
+ break;
+ case 'u':
+ mtmp->mflee = 1;
+ break;
+ case 'U':
+ (void) hitu(mtmp,d(3,4));
+ (void) hitu(mtmp,d(3,4));
+ break;
+ case 'v':
+ if(ctmp && !u.ustuck) u.ustuck = mtmp;
+ break;
+ case 'V':
+ if(tmp) u.uhp -= 4;
+ if(ctmp) losexp();
+ break;
+ case 'W':
+ if(ctmp) losexp();
+ break;
+#ifndef NOWORM
+ case 'w':
+ if(tmp) wormhit(mtmp);
+#endif NOWORM
+ break;
+ case 'X':
+ (void) hitu(mtmp,rnd(5));
+ (void) hitu(mtmp,rnd(5));
+ (void) hitu(mtmp,rnd(5));
+ break;
+ case 'x':
+ { register long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE;
+ pline("%s pricks in your %s leg!",
+ Monnam(mtmp), (side == RIGHT_SIDE) ? "right" : "left");
+ set_wounded_legs(side, rnd(50));
+ losehp_m(2, mtmp);
+ break;
+ }
+ case 'y':
+ if(mtmp->mcan) break;
+ mondead(mtmp);
+ if(!Blind) {
+ pline("You are blinded by a blast of light!");
+ Blind = d(4,12);
+ seeoff(0);
+ }
+ return(1);
+ case 'Y':
+ (void) hitu(mtmp,rnd(6));
+ break;
+ }
+ if(u.uhp < 1) done_in_by(mtmp);
+ return(0);
+}
+
+hitu(mtmp,dam)
+register struct monst *mtmp;
+register dam;
+{
+ register tmp, res;
+
+ nomul(0);
+ if(u.uswallow) return(0);
+
+ if(mtmp->mhide && mtmp->mundetected) {
+ mtmp->mundetected = 0;
+ if(!Blind) {
+ register struct obj *obj;
+ extern char * Xmonnam();
+ if(obj = o_at(mtmp->mx,mtmp->my))
+ pline("%s was hidden under %s!",
+ Xmonnam(mtmp), doname(obj));
+ }
+ }
+
+ tmp = u.uac;
+ /* give people with Ac = -10 at least some vulnerability */
+ if(tmp < 0) {
+ dam += tmp; /* decrease damage */
+ if(dam <= 0) dam = 1;
+ tmp = -rn2(-tmp);
+ }
+ tmp += mtmp->data->mlevel;
+ if(multi < 0) tmp += 4;
+ if((Invis && mtmp->data->mlet != 'I') || !mtmp->mcansee) tmp -= 2;
+ if(mtmp->mtrapped) tmp -= 2;
+ if(tmp <= rnd(20)) {
+ if(Blind) pline("It misses.");
+ else pline("%s misses.",Monnam(mtmp));
+ res = 0;
+ } else {
+ if(Blind) pline("It hits!");
+ else pline("%s hits!",Monnam(mtmp));
+ losehp_m(dam, mtmp);
+ res = 1;
+ }
+ stop_occupation();
+ return(res);
+}
diff --git a/games/hack/hack.mklev.c b/games/hack/hack.mklev.c
new file mode 100644
index 000000000000..89040213744d
--- /dev/null
+++ b/games/hack/hack.mklev.c
@@ -0,0 +1,741 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mklev.c - version 1.0.3 */
+
+#include "hack.h"
+
+extern char *getlogin(), *getenv();
+extern struct monst *makemon();
+extern struct obj *mkobj_at();
+extern struct trap *maketrap();
+
+#define somex() ((random()%(croom->hx-croom->lx+1))+croom->lx)
+#define somey() ((random()%(croom->hy-croom->ly+1))+croom->ly)
+
+#include "def.mkroom.h"
+#define XLIM 4 /* define minimum required space around a room */
+#define YLIM 3
+boolean secret; /* TRUE while making a vault: increase [XY]LIM */
+struct mkroom rooms[MAXNROFROOMS+1];
+int smeq[MAXNROFROOMS+1];
+coord doors[DOORMAX];
+int doorindex;
+struct rm zerorm;
+int comp();
+schar nxcor;
+boolean goldseen;
+int nroom;
+xchar xdnstair,xupstair,ydnstair,yupstair;
+
+/* Definitions used by makerooms() and addrs() */
+#define MAXRS 50 /* max lth of temp rectangle table - arbitrary */
+struct rectangle {
+ xchar rlx,rly,rhx,rhy;
+} rs[MAXRS+1];
+int rscnt,rsmax; /* 0..rscnt-1: currently under consideration */
+ /* rscnt..rsmax: discarded */
+
+makelevel()
+{
+ register struct mkroom *croom, *troom;
+ register unsigned tryct;
+ register x,y;
+
+ nroom = 0;
+ doorindex = 0;
+ rooms[0].hx = -1; /* in case we are in a maze */
+
+ for(x=0; x<COLNO; x++) for(y=0; y<ROWNO; y++)
+ levl[x][y] = zerorm;
+
+ oinit(); /* assign level dependent obj probabilities */
+
+ if(dlevel >= rn1(3, 26)) { /* there might be several mazes */
+ makemaz();
+ return;
+ }
+
+ /* construct the rooms */
+ nroom = 0;
+ secret = FALSE;
+ (void) makerooms();
+
+ /* construct stairs (up and down in different rooms if possible) */
+ croom = &rooms[rn2(nroom)];
+ xdnstair = somex();
+ ydnstair = somey();
+ levl[xdnstair][ydnstair].scrsym ='>';
+ levl[xdnstair][ydnstair].typ = STAIRS;
+ if(nroom > 1) {
+ troom = croom;
+ croom = &rooms[rn2(nroom-1)];
+ if(croom >= troom) croom++;
+ }
+ xupstair = somex(); /* %% < and > might be in the same place */
+ yupstair = somey();
+ levl[xupstair][yupstair].scrsym ='<';
+ levl[xupstair][yupstair].typ = STAIRS;
+
+ /* for each room: put things inside */
+ for(croom = rooms; croom->hx > 0; croom++) {
+
+ /* put a sleeping monster inside */
+ /* Note: monster may be on the stairs. This cannot be
+ avoided: maybe the player fell through a trapdoor
+ while a monster was on the stairs. Conclusion:
+ we have to check for monsters on the stairs anyway. */
+ if(!rn2(3)) (void)
+ makemon((struct permonst *) 0, somex(), somey());
+
+ /* put traps and mimics inside */
+ goldseen = FALSE;
+ while(!rn2(8-(dlevel/6))) mktrap(0,0,croom);
+ if(!goldseen && !rn2(3)) mkgold(0L,somex(),somey());
+ if(!rn2(3)) {
+ (void) mkobj_at(0, somex(), somey());
+ tryct = 0;
+ while(!rn2(5)) {
+ if(++tryct > 100){
+ printf("tryct overflow4\n");
+ break;
+ }
+ (void) mkobj_at(0, somex(), somey());
+ }
+ }
+ }
+
+ qsort((char *) rooms, nroom, sizeof(struct mkroom), comp);
+ makecorridors();
+ make_niches();
+
+ /* make a secret treasure vault, not connected to the rest */
+ if(nroom <= (2*MAXNROFROOMS/3)) if(rn2(3)) {
+ troom = &rooms[nroom];
+ secret = TRUE;
+ if(makerooms()) {
+ troom->rtype = VAULT; /* treasure vault */
+ for(x = troom->lx; x <= troom->hx; x++)
+ for(y = troom->ly; y <= troom->hy; y++)
+ mkgold((long)(rnd(dlevel*100) + 50), x, y);
+ if(!rn2(3))
+ makevtele();
+ }
+ }
+
+#ifndef QUEST
+#ifdef WIZARD
+ if(wizard && getenv("SHOPTYPE")) mkshop(); else
+#endif WIZARD
+ if(dlevel > 1 && dlevel < 20 && rn2(dlevel) < 3) mkshop();
+ else
+ if(dlevel > 6 && !rn2(7)) mkzoo(ZOO);
+ else
+ if(dlevel > 9 && !rn2(5)) mkzoo(BEEHIVE);
+ else
+ if(dlevel > 11 && !rn2(6)) mkzoo(MORGUE);
+ else
+ if(dlevel > 18 && !rn2(6)) mkswamp();
+#endif QUEST
+}
+
+makerooms() {
+register struct rectangle *rsp;
+register int lx, ly, hx, hy, lowx, lowy, hix, hiy, dx, dy;
+int tryct = 0, xlim, ylim;
+
+ /* init */
+ xlim = XLIM + secret;
+ ylim = YLIM + secret;
+ if(nroom == 0) {
+ rsp = rs;
+ rsp->rlx = rsp->rly = 0;
+ rsp->rhx = COLNO-1;
+ rsp->rhy = ROWNO-1;
+ rsmax = 1;
+ }
+ rscnt = rsmax;
+
+ /* make rooms until satisfied */
+ while(rscnt > 0 && nroom < MAXNROFROOMS-1) {
+ if(!secret && nroom > (MAXNROFROOMS/3) &&
+ !rn2((MAXNROFROOMS-nroom)*(MAXNROFROOMS-nroom)))
+ return(0);
+
+ /* pick a rectangle */
+ rsp = &rs[rn2(rscnt)];
+ hx = rsp->rhx;
+ hy = rsp->rhy;
+ lx = rsp->rlx;
+ ly = rsp->rly;
+
+ /* find size of room */
+ if(secret)
+ dx = dy = 1;
+ else {
+ dx = 2 + rn2((hx-lx-8 > 20) ? 12 : 8);
+ dy = 2 + rn2(4);
+ if(dx*dy > 50)
+ dy = 50/dx;
+ }
+
+ /* look whether our room will fit */
+ if(hx-lx < dx + dx/2 + 2*xlim || hy-ly < dy + dy/3 + 2*ylim) {
+ /* no, too small */
+ /* maybe we throw this area out */
+ if(secret || !rn2(MAXNROFROOMS+1-nroom-tryct)) {
+ rscnt--;
+ rs[rsmax] = *rsp;
+ *rsp = rs[rscnt];
+ rs[rscnt] = rs[rsmax];
+ tryct = 0;
+ } else
+ tryct++;
+ continue;
+ }
+
+ lowx = lx + xlim + rn2(hx - lx - dx - 2*xlim + 1);
+ lowy = ly + ylim + rn2(hy - ly - dy - 2*ylim + 1);
+ hix = lowx + dx;
+ hiy = lowy + dy;
+
+ if(maker(lowx, dx, lowy, dy)) {
+ if(secret)
+ return(1);
+ addrs(lowx-1, lowy-1, hix+1, hiy+1);
+ tryct = 0;
+ } else
+ if(tryct++ > 100)
+ break;
+ }
+ return(0); /* failed to make vault - very strange */
+}
+
+addrs(lowx,lowy,hix,hiy)
+register int lowx,lowy,hix,hiy;
+{
+ register struct rectangle *rsp;
+ register int lx,ly,hx,hy,xlim,ylim;
+ boolean discarded;
+
+ xlim = XLIM + secret;
+ ylim = YLIM + secret;
+
+ /* walk down since rscnt and rsmax change */
+ for(rsp = &rs[rsmax-1]; rsp >= rs; rsp--) {
+
+ if((lx = rsp->rlx) > hix || (ly = rsp->rly) > hiy ||
+ (hx = rsp->rhx) < lowx || (hy = rsp->rhy) < lowy)
+ continue;
+ if((discarded = (rsp >= &rs[rscnt]))) {
+ *rsp = rs[--rsmax];
+ } else {
+ rsmax--;
+ rscnt--;
+ *rsp = rs[rscnt];
+ if(rscnt != rsmax)
+ rs[rscnt] = rs[rsmax];
+ }
+ if(lowy - ly > 2*ylim + 4)
+ addrsx(lx,ly,hx,lowy-2,discarded);
+ if(lowx - lx > 2*xlim + 4)
+ addrsx(lx,ly,lowx-2,hy,discarded);
+ if(hy - hiy > 2*ylim + 4)
+ addrsx(lx,hiy+2,hx,hy,discarded);
+ if(hx - hix > 2*xlim + 4)
+ addrsx(hix+2,ly,hx,hy,discarded);
+ }
+}
+
+addrsx(lx,ly,hx,hy,discarded)
+register int lx,ly,hx,hy;
+boolean discarded; /* piece of a discarded area */
+{
+ register struct rectangle *rsp;
+
+ /* check inclusions */
+ for(rsp = rs; rsp < &rs[rsmax]; rsp++) {
+ if(lx >= rsp->rlx && hx <= rsp->rhx &&
+ ly >= rsp->rly && hy <= rsp->rhy)
+ return;
+ }
+
+ /* make a new entry */
+ if(rsmax >= MAXRS) {
+#ifdef WIZARD
+ if(wizard) pline("MAXRS may be too small.");
+#endif WIZARD
+ return;
+ }
+ rsmax++;
+ if(!discarded) {
+ *rsp = rs[rscnt];
+ rsp = &rs[rscnt];
+ rscnt++;
+ }
+ rsp->rlx = lx;
+ rsp->rly = ly;
+ rsp->rhx = hx;
+ rsp->rhy = hy;
+}
+
+comp(x,y)
+register struct mkroom *x,*y;
+{
+ if(x->lx < y->lx) return(-1);
+ return(x->lx > y->lx);
+}
+
+coord
+finddpos(xl,yl,xh,yh) {
+ coord ff;
+ register x,y;
+
+ x = (xl == xh) ? xl : (xl + rn2(xh-xl+1));
+ y = (yl == yh) ? yl : (yl + rn2(yh-yl+1));
+ if(okdoor(x, y))
+ goto gotit;
+
+ for(x = xl; x <= xh; x++) for(y = yl; y <= yh; y++)
+ if(okdoor(x, y))
+ goto gotit;
+
+ for(x = xl; x <= xh; x++) for(y = yl; y <= yh; y++)
+ if(levl[x][y].typ == DOOR || levl[x][y].typ == SDOOR)
+ goto gotit;
+ /* cannot find something reasonable -- strange */
+ x = xl;
+ y = yh;
+gotit:
+ ff.x = x;
+ ff.y = y;
+ return(ff);
+}
+
+/* see whether it is allowable to create a door at [x,y] */
+okdoor(x,y)
+register x,y;
+{
+ if(levl[x-1][y].typ == DOOR || levl[x+1][y].typ == DOOR ||
+ levl[x][y+1].typ == DOOR || levl[x][y-1].typ == DOOR ||
+ levl[x-1][y].typ == SDOOR || levl[x+1][y].typ == SDOOR ||
+ levl[x][y-1].typ == SDOOR || levl[x][y+1].typ == SDOOR ||
+ (levl[x][y].typ != HWALL && levl[x][y].typ != VWALL) ||
+ doorindex >= DOORMAX)
+ return(0);
+ return(1);
+}
+
+dodoor(x,y,aroom)
+register x,y;
+register struct mkroom *aroom;
+{
+ if(doorindex >= DOORMAX) {
+ impossible("DOORMAX exceeded?");
+ return;
+ }
+ if(!okdoor(x,y) && nxcor)
+ return;
+ dosdoor(x,y,aroom,rn2(8) ? DOOR : SDOOR);
+}
+
+dosdoor(x,y,aroom,type)
+register x,y;
+register struct mkroom *aroom;
+register type;
+{
+ register struct mkroom *broom;
+ register tmp;
+
+ if(!IS_WALL(levl[x][y].typ)) /* avoid SDOORs with '+' as scrsym */
+ type = DOOR;
+ levl[x][y].typ = type;
+ if(type == DOOR)
+ levl[x][y].scrsym = '+';
+ aroom->doorct++;
+ broom = aroom+1;
+ if(broom->hx < 0) tmp = doorindex; else
+ for(tmp = doorindex; tmp > broom->fdoor; tmp--)
+ doors[tmp] = doors[tmp-1];
+ doorindex++;
+ doors[tmp].x = x;
+ doors[tmp].y = y;
+ for( ; broom->hx >= 0; broom++) broom->fdoor++;
+}
+
+/* Only called from makerooms() */
+maker(lowx,ddx,lowy,ddy)
+schar lowx,ddx,lowy,ddy;
+{
+ register struct mkroom *croom;
+ register x, y, hix = lowx+ddx, hiy = lowy+ddy;
+ register xlim = XLIM + secret, ylim = YLIM + secret;
+
+ if(nroom >= MAXNROFROOMS) return(0);
+ if(lowx < XLIM) lowx = XLIM;
+ if(lowy < YLIM) lowy = YLIM;
+ if(hix > COLNO-XLIM-1) hix = COLNO-XLIM-1;
+ if(hiy > ROWNO-YLIM-1) hiy = ROWNO-YLIM-1;
+chk:
+ if(hix <= lowx || hiy <= lowy) return(0);
+
+ /* check area around room (and make room smaller if necessary) */
+ for(x = lowx - xlim; x <= hix + xlim; x++) {
+ for(y = lowy - ylim; y <= hiy + ylim; y++) {
+ if(levl[x][y].typ) {
+#ifdef WIZARD
+ if(wizard && !secret)
+ pline("Strange area [%d,%d] in maker().",x,y);
+#endif WIZARD
+ if(!rn2(3)) return(0);
+ if(x < lowx)
+ lowx = x+xlim+1;
+ else
+ hix = x-xlim-1;
+ if(y < lowy)
+ lowy = y+ylim+1;
+ else
+ hiy = y-ylim-1;
+ goto chk;
+ }
+ }
+ }
+
+ croom = &rooms[nroom];
+
+ /* on low levels the room is lit (usually) */
+ /* secret vaults are always lit */
+ if((rnd(dlevel) < 10 && rn2(77)) || (ddx == 1 && ddy == 1)) {
+ for(x = lowx-1; x <= hix+1; x++)
+ for(y = lowy-1; y <= hiy+1; y++)
+ levl[x][y].lit = 1;
+ croom->rlit = 1;
+ } else
+ croom->rlit = 0;
+ croom->lx = lowx;
+ croom->hx = hix;
+ croom->ly = lowy;
+ croom->hy = hiy;
+ croom->rtype = croom->doorct = croom->fdoor = 0;
+
+ for(x = lowx-1; x <= hix+1; x++)
+ for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) {
+ levl[x][y].scrsym = '-';
+ levl[x][y].typ = HWALL;
+ }
+ for(x = lowx-1; x <= hix+1; x += (hix-lowx+2))
+ for(y = lowy; y <= hiy; y++) {
+ levl[x][y].scrsym = '|';
+ levl[x][y].typ = VWALL;
+ }
+ for(x = lowx; x <= hix; x++)
+ for(y = lowy; y <= hiy; y++) {
+ levl[x][y].scrsym = '.';
+ levl[x][y].typ = ROOM;
+ }
+
+ smeq[nroom] = nroom;
+ croom++;
+ croom->hx = -1;
+ nroom++;
+ return(1);
+}
+
+makecorridors() {
+ register a,b;
+
+ nxcor = 0;
+ for(a = 0; a < nroom-1; a++)
+ join(a, a+1);
+ for(a = 0; a < nroom-2; a++)
+ if(smeq[a] != smeq[a+2])
+ join(a, a+2);
+ for(a = 0; a < nroom; a++)
+ for(b = 0; b < nroom; b++)
+ if(smeq[a] != smeq[b])
+ join(a, b);
+ if(nroom > 2)
+ for(nxcor = rn2(nroom) + 4; nxcor; nxcor--) {
+ a = rn2(nroom);
+ b = rn2(nroom-2);
+ if(b >= a) b += 2;
+ join(a, b);
+ }
+}
+
+join(a,b)
+register a,b;
+{
+ coord cc,tt;
+ register tx, ty, xx, yy;
+ register struct rm *crm;
+ register struct mkroom *croom, *troom;
+ register dx, dy, dix, diy, cct;
+
+ croom = &rooms[a];
+ troom = &rooms[b];
+
+ /* find positions cc and tt for doors in croom and troom
+ and direction for a corridor between them */
+
+ if(troom->hx < 0 || croom->hx < 0 || doorindex >= DOORMAX) return;
+ if(troom->lx > croom->hx) {
+ dx = 1;
+ dy = 0;
+ xx = croom->hx+1;
+ tx = troom->lx-1;
+ cc = finddpos(xx,croom->ly,xx,croom->hy);
+ tt = finddpos(tx,troom->ly,tx,troom->hy);
+ } else if(troom->hy < croom->ly) {
+ dy = -1;
+ dx = 0;
+ yy = croom->ly-1;
+ cc = finddpos(croom->lx,yy,croom->hx,yy);
+ ty = troom->hy+1;
+ tt = finddpos(troom->lx,ty,troom->hx,ty);
+ } else if(troom->hx < croom->lx) {
+ dx = -1;
+ dy = 0;
+ xx = croom->lx-1;
+ tx = troom->hx+1;
+ cc = finddpos(xx,croom->ly,xx,croom->hy);
+ tt = finddpos(tx,troom->ly,tx,troom->hy);
+ } else {
+ dy = 1;
+ dx = 0;
+ yy = croom->hy+1;
+ ty = troom->ly-1;
+ cc = finddpos(croom->lx,yy,croom->hx,yy);
+ tt = finddpos(troom->lx,ty,troom->hx,ty);
+ }
+ xx = cc.x;
+ yy = cc.y;
+ tx = tt.x - dx;
+ ty = tt.y - dy;
+ if(nxcor && levl[xx+dx][yy+dy].typ)
+ return;
+ dodoor(xx,yy,croom);
+
+ cct = 0;
+ while(xx != tx || yy != ty) {
+ xx += dx;
+ yy += dy;
+
+ /* loop: dig corridor at [xx,yy] and find new [xx,yy] */
+ if(cct++ > 500 || (nxcor && !rn2(35)))
+ return;
+
+ if(xx == COLNO-1 || xx == 0 || yy == 0 || yy == ROWNO-1)
+ return; /* impossible */
+
+ crm = &levl[xx][yy];
+ if(!(crm->typ)) {
+ if(rn2(100)) {
+ crm->typ = CORR;
+ crm->scrsym = CORR_SYM;
+ if(nxcor && !rn2(50))
+ (void) mkobj_at(ROCK_SYM, xx, yy);
+ } else {
+ crm->typ = SCORR;
+ crm->scrsym = ' ';
+ }
+ } else
+ if(crm->typ != CORR && crm->typ != SCORR) {
+ /* strange ... */
+ return;
+ }
+
+ /* find next corridor position */
+ dix = abs(xx-tx);
+ diy = abs(yy-ty);
+
+ /* do we have to change direction ? */
+ if(dy && dix > diy) {
+ register ddx = (xx > tx) ? -1 : 1;
+
+ crm = &levl[xx+ddx][yy];
+ if(!crm->typ || crm->typ == CORR || crm->typ == SCORR) {
+ dx = ddx;
+ dy = 0;
+ continue;
+ }
+ } else if(dx && diy > dix) {
+ register ddy = (yy > ty) ? -1 : 1;
+
+ crm = &levl[xx][yy+ddy];
+ if(!crm->typ || crm->typ == CORR || crm->typ == SCORR) {
+ dy = ddy;
+ dx = 0;
+ continue;
+ }
+ }
+
+ /* continue straight on? */
+ crm = &levl[xx+dx][yy+dy];
+ if(!crm->typ || crm->typ == CORR || crm->typ == SCORR)
+ continue;
+
+ /* no, what must we do now?? */
+ if(dx) {
+ dx = 0;
+ dy = (ty < yy) ? -1 : 1;
+ crm = &levl[xx+dx][yy+dy];
+ if(!crm->typ || crm->typ == CORR || crm->typ == SCORR)
+ continue;
+ dy = -dy;
+ continue;
+ } else {
+ dy = 0;
+ dx = (tx < xx) ? -1 : 1;
+ crm = &levl[xx+dx][yy+dy];
+ if(!crm->typ || crm->typ == CORR || crm->typ == SCORR)
+ continue;
+ dx = -dx;
+ continue;
+ }
+ }
+
+ /* we succeeded in digging the corridor */
+ dodoor(tt.x, tt.y, troom);
+
+ if(smeq[a] < smeq[b])
+ smeq[b] = smeq[a];
+ else
+ smeq[a] = smeq[b];
+}
+
+make_niches()
+{
+ register int ct = rnd(nroom/2 + 1);
+ while(ct--) makeniche(FALSE);
+}
+
+makevtele()
+{
+ makeniche(TRUE);
+}
+
+makeniche(with_trap)
+boolean with_trap;
+{
+ register struct mkroom *aroom;
+ register struct rm *rm;
+ register int vct = 8;
+ coord dd;
+ register dy,xx,yy;
+ register struct trap *ttmp;
+
+ if(doorindex < DOORMAX)
+ while(vct--) {
+ aroom = &rooms[rn2(nroom-1)];
+ if(aroom->rtype != 0) continue; /* not an ordinary room */
+ if(aroom->doorct == 1 && rn2(5)) continue;
+ if(rn2(2)) {
+ dy = 1;
+ dd = finddpos(aroom->lx,aroom->hy+1,aroom->hx,aroom->hy+1);
+ } else {
+ dy = -1;
+ dd = finddpos(aroom->lx,aroom->ly-1,aroom->hx,aroom->ly-1);
+ }
+ xx = dd.x;
+ yy = dd.y;
+ if((rm = &levl[xx][yy+dy])->typ) continue;
+ if(with_trap || !rn2(4)) {
+ rm->typ = SCORR;
+ rm->scrsym = ' ';
+ if(with_trap) {
+ ttmp = maketrap(xx, yy+dy, TELEP_TRAP);
+ ttmp->once = 1;
+ make_engr_at(xx, yy-dy, "ad ae?ar um");
+ }
+ dosdoor(xx, yy, aroom, SDOOR);
+ } else {
+ rm->typ = CORR;
+ rm->scrsym = CORR_SYM;
+ if(rn2(7))
+ dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR);
+ else {
+ mksobj_at(SCR_TELEPORTATION, xx, yy+dy);
+ if(!rn2(3)) (void) mkobj_at(0, xx, yy+dy);
+ }
+ }
+ return;
+ }
+}
+
+/* make a trap somewhere (in croom if mazeflag = 0) */
+mktrap(num,mazeflag,croom)
+register num,mazeflag;
+register struct mkroom *croom;
+{
+ register struct trap *ttmp;
+ register int kind,nopierc,nomimic,fakedoor,fakegold,tryct = 0;
+ register xchar mx,my;
+ extern char fut_geno[];
+
+ if(!num || num >= TRAPNUM) {
+ nopierc = (dlevel < 4) ? 1 : 0;
+ nomimic = (dlevel < 9 || goldseen ) ? 1 : 0;
+ if(index(fut_geno, 'M')) nomimic = 1;
+ kind = rn2(TRAPNUM - nopierc - nomimic);
+ /* note: PIERC = 7, MIMIC = 8, TRAPNUM = 9 */
+ } else kind = num;
+
+ if(kind == MIMIC) {
+ register struct monst *mtmp;
+
+ fakedoor = (!rn2(3) && !mazeflag);
+ fakegold = (!fakedoor && !rn2(2));
+ if(fakegold) goldseen = TRUE;
+ do {
+ if(++tryct > 200) return;
+ if(fakedoor) {
+ /* note: fakedoor maybe on actual door */
+ if(rn2(2)){
+ if(rn2(2))
+ mx = croom->hx+1;
+ else mx = croom->lx-1;
+ my = somey();
+ } else {
+ if(rn2(2))
+ my = croom->hy+1;
+ else my = croom->ly-1;
+ mx = somex();
+ }
+ } else if(mazeflag) {
+ extern coord mazexy();
+ coord mm;
+ mm = mazexy();
+ mx = mm.x;
+ my = mm.y;
+ } else {
+ mx = somex();
+ my = somey();
+ }
+ } while(m_at(mx,my) || levl[mx][my].typ == STAIRS);
+ if(mtmp = makemon(PM_MIMIC,mx,my)) {
+ mtmp->mimic = 1;
+ mtmp->mappearance =
+ fakegold ? '$' : fakedoor ? '+' :
+ (mazeflag && rn2(2)) ? AMULET_SYM :
+ "=/)%?![<>" [ rn2(9) ];
+ }
+ return;
+ }
+
+ do {
+ if(++tryct > 200)
+ return;
+ if(mazeflag){
+ extern coord mazexy();
+ coord mm;
+ mm = mazexy();
+ mx = mm.x;
+ my = mm.y;
+ } else {
+ mx = somex();
+ my = somey();
+ }
+ } while(t_at(mx, my) || levl[mx][my].typ == STAIRS);
+ ttmp = maketrap(mx, my, kind);
+ if(mazeflag && !rn2(10) && ttmp->ttyp < PIERC)
+ ttmp->tseen = 1;
+}
diff --git a/games/hack/hack.mkmaze.c b/games/hack/hack.mkmaze.c
new file mode 100644
index 000000000000..bee24f954c30
--- /dev/null
+++ b/games/hack/hack.mkmaze.c
@@ -0,0 +1,136 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mkmaze.c - version 1.0.2 */
+
+#include "hack.h"
+#include "def.mkroom.h" /* not really used */
+extern struct monst *makemon();
+extern struct permonst pm_wizard;
+extern struct obj *mkobj_at();
+extern coord mazexy();
+struct permonst hell_hound =
+ { "hell hound", 'd', 12, 14, 2, 3, 6, 0 };
+
+makemaz()
+{
+ int x,y;
+ register zx,zy;
+ coord mm;
+ boolean al = (dlevel >= 30 && !flags.made_amulet);
+
+ for(x = 2; x < COLNO-1; x++)
+ for(y = 2; y < ROWNO-1; y++)
+ levl[x][y].typ = (x%2 && y%2) ? 0 : HWALL;
+ if(al) {
+ register struct monst *mtmp;
+
+ zx = 2*(COLNO/4) - 1;
+ zy = 2*(ROWNO/4) - 1;
+ for(x = zx-2; x < zx+4; x++) for(y = zy-2; y <= zy+2; y++) {
+ levl[x][y].typ =
+ (y == zy-2 || y == zy+2 || x == zx-2 || x == zx+3) ? POOL :
+ (y == zy-1 || y == zy+1 || x == zx-1 || x == zx+2) ? HWALL:
+ ROOM;
+ }
+ (void) mkobj_at(AMULET_SYM, zx, zy);
+ flags.made_amulet = 1;
+ walkfrom(zx+4, zy);
+ if(mtmp = makemon(&hell_hound, zx, zy))
+ mtmp->msleep = 1;
+ if(mtmp = makemon(PM_WIZARD, zx+1, zy)) {
+ mtmp->msleep = 1;
+ flags.no_of_wizards = 1;
+ }
+ } else {
+ mm = mazexy();
+ zx = mm.x;
+ zy = mm.y;
+ walkfrom(zx,zy);
+ (void) mksobj_at(WAN_WISHING, zx, zy);
+ (void) mkobj_at(ROCK_SYM, zx, zy); /* put a rock on top of it */
+ }
+
+ for(x = 2; x < COLNO-1; x++)
+ for(y = 2; y < ROWNO-1; y++) {
+ switch(levl[x][y].typ) {
+ case HWALL:
+ levl[x][y].scrsym = '-';
+ break;
+ case ROOM:
+ levl[x][y].scrsym = '.';
+ break;
+ }
+ }
+ for(x = rn1(8,11); x; x--) {
+ mm = mazexy();
+ (void) mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y);
+ }
+ for(x = rn1(10,2); x; x--) {
+ mm = mazexy();
+ (void) mkobj_at(ROCK_SYM, mm.x, mm.y);
+ }
+ mm = mazexy();
+ (void) makemon(PM_MINOTAUR, mm.x, mm.y);
+ for(x = rn1(5,7); x; x--) {
+ mm = mazexy();
+ (void) makemon((struct permonst *) 0, mm.x, mm.y);
+ }
+ for(x = rn1(6,7); x; x--) {
+ mm = mazexy();
+ mkgold(0L,mm.x,mm.y);
+ }
+ for(x = rn1(6,7); x; x--)
+ mktrap(0,1,(struct mkroom *) 0);
+ mm = mazexy();
+ levl[(xupstair = mm.x)][(yupstair = mm.y)].scrsym = '<';
+ levl[xupstair][yupstair].typ = STAIRS;
+ xdnstair = ydnstair = 0;
+}
+
+walkfrom(x,y) int x,y; {
+register int q,a,dir;
+int dirs[4];
+ levl[x][y].typ = ROOM;
+ while(1) {
+ q = 0;
+ for(a = 0; a < 4; a++)
+ if(okay(x,y,a)) dirs[q++]= a;
+ if(!q) return;
+ dir = dirs[rn2(q)];
+ move(&x,&y,dir);
+ levl[x][y].typ = ROOM;
+ move(&x,&y,dir);
+ walkfrom(x,y);
+ }
+}
+
+move(x,y,dir)
+register int *x, *y;
+register int dir;
+{
+ switch(dir){
+ case 0: --(*y); break;
+ case 1: (*x)++; break;
+ case 2: (*y)++; break;
+ case 3: --(*x); break;
+ }
+}
+
+okay(x,y,dir)
+int x,y;
+register int dir;
+{
+ move(&x,&y,dir);
+ move(&x,&y,dir);
+ if(x<3 || y<3 || x>COLNO-3 || y>ROWNO-3 || levl[x][y].typ != 0)
+ return(0);
+ else
+ return(1);
+}
+
+coord
+mazexy(){
+ coord mm;
+ mm.x = 3 + 2*rn2(COLNO/2 - 2);
+ mm.y = 3 + 2*rn2(ROWNO/2 - 2);
+ return mm;
+}
diff --git a/games/hack/hack.mkobj.c b/games/hack/hack.mkobj.c
new file mode 100644
index 000000000000..18a69146d045
--- /dev/null
+++ b/games/hack/hack.mkobj.c
@@ -0,0 +1,148 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mkobj.c - version 1.0.3 */
+
+#include "hack.h"
+
+char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%";
+struct obj *mkobj(), *mksobj();
+
+struct obj *
+mkobj_at(let,x,y)
+register let,x,y;
+{
+ register struct obj *otmp = mkobj(let);
+ otmp->ox = x;
+ otmp->oy = y;
+ otmp->nobj = fobj;
+ fobj = otmp;
+ return(otmp);
+}
+
+mksobj_at(otyp,x,y)
+register otyp,x,y;
+{
+ register struct obj *otmp = mksobj(otyp);
+ otmp->ox = x;
+ otmp->oy = y;
+ otmp->nobj = fobj;
+ fobj = otmp;
+}
+
+struct obj *
+mkobj(let) {
+ if(!let)
+ let = mkobjstr[rn2(sizeof(mkobjstr) - 1)];
+ return(
+ mksobj(
+ letter(let) ?
+ CORPSE + ((let > 'Z') ? (let-'a'+'Z'-'@'+1) : (let-'@'))
+ : probtype(let)
+ )
+ );
+}
+
+
+struct obj zeroobj;
+
+struct obj *
+mksobj(otyp)
+register otyp;
+{
+ register struct obj *otmp;
+ char let = objects[otyp].oc_olet;
+
+ otmp = newobj(0);
+ *otmp = zeroobj;
+ otmp->age = moves;
+ otmp->o_id = flags.ident++;
+ otmp->quan = 1;
+ otmp->olet = let;
+ otmp->otyp = otyp;
+ otmp->dknown = index("/=!?*", let) ? 0 : 1;
+ switch(let) {
+ case WEAPON_SYM:
+ otmp->quan = (otmp->otyp <= ROCK) ? rn1(6,6) : 1;
+ if(!rn2(11)) otmp->spe = rnd(3);
+ else if(!rn2(10)) {
+ otmp->cursed = 1;
+ otmp->spe = -rnd(3);
+ }
+ break;
+ case FOOD_SYM:
+ if(otmp->otyp >= CORPSE) break;
+#ifdef NOT_YET_IMPLEMENTED
+ /* if tins are to be identified, need to adapt doname() etc */
+ if(otmp->otyp == TIN)
+ otmp->spe = rnd(...);
+#endif NOT_YET_IMPLEMENTED
+ /* fall into next case */
+ case GEM_SYM:
+ otmp->quan = rn2(6) ? 1 : 2;
+ case TOOL_SYM:
+ case CHAIN_SYM:
+ case BALL_SYM:
+ case ROCK_SYM:
+ case POTION_SYM:
+ case SCROLL_SYM:
+ case AMULET_SYM:
+ break;
+ case ARMOR_SYM:
+ if(!rn2(8)) otmp->cursed = 1;
+ if(!rn2(10)) otmp->spe = rnd(3);
+ else if(!rn2(9)) {
+ otmp->spe = -rnd(3);
+ otmp->cursed = 1;
+ }
+ break;
+ case WAND_SYM:
+ if(otmp->otyp == WAN_WISHING) otmp->spe = 3; else
+ otmp->spe = rn1(5,
+ (objects[otmp->otyp].bits & NODIR) ? 11 : 4);
+ break;
+ case RING_SYM:
+ if(objects[otmp->otyp].bits & SPEC) {
+ if(!rn2(3)) {
+ otmp->cursed = 1;
+ otmp->spe = -rnd(2);
+ } else otmp->spe = rnd(2);
+ } else if(otmp->otyp == RIN_TELEPORTATION ||
+ otmp->otyp == RIN_AGGRAVATE_MONSTER ||
+ otmp->otyp == RIN_HUNGER || !rn2(9))
+ otmp->cursed = 1;
+ break;
+ default:
+ panic("impossible mkobj");
+ }
+ otmp->owt = weight(otmp);
+ return(otmp);
+}
+
+letter(c) {
+ return(('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
+}
+
+weight(obj)
+register struct obj *obj;
+{
+register int wt = objects[obj->otyp].oc_weight;
+ return(wt ? wt*obj->quan : (obj->quan + 1)/2);
+}
+
+mkgold(num,x,y)
+register long num;
+{
+ register struct gold *gold;
+ register long amount = (num ? num : 1 + (rnd(dlevel+2) * rnd(30)));
+
+ if(gold = g_at(x,y))
+ gold->amount += amount;
+ else {
+ gold = newgold();
+ gold->ngold = fgold;
+ gold->gx = x;
+ gold->gy = y;
+ gold->amount = amount;
+ fgold = gold;
+ /* do sth with display? */
+ }
+}
diff --git a/games/hack/hack.mkshop.c b/games/hack/hack.mkshop.c
new file mode 100644
index 000000000000..9d99a22d2cb2
--- /dev/null
+++ b/games/hack/hack.mkshop.c
@@ -0,0 +1,274 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mkshop.c - version 1.0.3 */
+
+#ifndef QUEST
+#include "hack.h"
+#include "def.mkroom.h"
+#include "def.eshk.h"
+#define ESHK ((struct eshk *)(&(shk->mextra[0])))
+extern struct monst *makemon();
+extern struct obj *mkobj_at();
+extern int nroom;
+extern char shtypes[]; /* = "=/)%?!["; 8 types: 7 specialized, 1 mixed */
+schar shprobs[] = { 3,3,5,5,10,10,14,50 }; /* their probabilities */
+
+mkshop(){
+register struct mkroom *sroom;
+register int sh,sx,sy,i = -1;
+register char let;
+int roomno;
+register struct monst *shk;
+#ifdef WIZARD
+ /* first determine shoptype */
+ if(wizard){
+ extern char *getenv();
+ register char *ep = getenv("SHOPTYPE");
+ if(ep){
+ if(*ep == 'z' || *ep == 'Z'){
+ mkzoo(ZOO);
+ return;
+ }
+ if(*ep == 'm' || *ep == 'M'){
+ mkzoo(MORGUE);
+ return;
+ }
+ if(*ep == 'b' || *ep == 'B'){
+ mkzoo(BEEHIVE);
+ return;
+ }
+ if(*ep == 's' || *ep == 'S'){
+ mkswamp();
+ return;
+ }
+ for(i=0; shtypes[i]; i++)
+ if(*ep == shtypes[i]) break;
+ goto gottype;
+ }
+ }
+gottype:
+#endif WIZARD
+ for(sroom = &rooms[0], roomno = 0; ; sroom++, roomno++){
+ if(sroom->hx < 0) return;
+ if(sroom - rooms >= nroom) {
+ pline("rooms not closed by -1?");
+ return;
+ }
+ if(sroom->rtype) continue;
+ if(!sroom->rlit || has_dnstairs(sroom) || has_upstairs(sroom))
+ continue;
+ if(
+#ifdef WIZARD
+ (wizard && getenv("SHOPTYPE") && sroom->doorct != 0) ||
+#endif WIZARD
+ sroom->doorct <= 2 && sroom->doorct > 0) break;
+ }
+
+ if(i < 0) { /* shoptype not yet determined */
+ register int j;
+
+ for(j = rn2(100), i = 0; (j -= shprobs[i])>= 0; i++)
+ if(!shtypes[i]) break; /* superfluous */
+ if(isbig(sroom) && i + SHOPBASE == WANDSHOP)
+ i = GENERAL-SHOPBASE;
+ }
+ sroom->rtype = i + SHOPBASE;
+ let = shtypes[i];
+ sh = sroom->fdoor;
+ sx = doors[sh].x;
+ sy = doors[sh].y;
+ if(sx == sroom->lx-1) sx++; else
+ if(sx == sroom->hx+1) sx--; else
+ if(sy == sroom->ly-1) sy++; else
+ if(sy == sroom->hy+1) sy--; else {
+#ifdef WIZARD
+ /* This is said to happen sometimes, but I've never seen it. */
+ if(wizard) {
+ register int j = sroom->doorct;
+ extern int doorindex;
+
+ pline("Where is shopdoor?");
+ pline("Room at (%d,%d),(%d,%d).", sroom->lx, sroom->ly,
+ sroom->hx, sroom->hy);
+ pline("doormax=%d doorct=%d fdoor=%d",
+ doorindex, sroom->doorct, sh);
+ while(j--) {
+ pline("door [%d,%d]", doors[sh].x, doors[sh].y);
+ sh++;
+ }
+ more();
+ }
+#endif WIZARD
+ return;
+ }
+ if(!(shk = makemon(PM_SHK,sx,sy))) return;
+ shk->isshk = shk->mpeaceful = 1;
+ shk->msleep = 0;
+ shk->mtrapseen = ~0; /* we know all the traps already */
+ ESHK->shoproom = roomno;
+ ESHK->shoplevel = dlevel;
+ ESHK->shd = doors[sh];
+ ESHK->shk.x = sx;
+ ESHK->shk.y = sy;
+ ESHK->robbed = 0;
+ ESHK->visitct = 0;
+ ESHK->following = 0;
+ shk->mgold = 1000 + 30*rnd(100); /* initial capital */
+ ESHK->billct = 0;
+ findname(ESHK->shknam, let);
+ for(sx = sroom->lx; sx <= sroom->hx; sx++)
+ for(sy = sroom->ly; sy <= sroom->hy; sy++){
+ register struct monst *mtmp;
+ if((sx == sroom->lx && doors[sh].x == sx-1) ||
+ (sx == sroom->hx && doors[sh].x == sx+1) ||
+ (sy == sroom->ly && doors[sh].y == sy-1) ||
+ (sy == sroom->hy && doors[sh].y == sy+1)) continue;
+ if(rn2(100) < dlevel && !m_at(sx,sy) &&
+ (mtmp = makemon(PM_MIMIC, sx, sy))){
+ mtmp->mimic = 1;
+ mtmp->mappearance =
+ (let && rn2(10) < dlevel) ? let : ']';
+ continue;
+ }
+ (void) mkobj_at(let, sx, sy);
+ }
+}
+
+mkzoo(type)
+int type;
+{
+ register struct mkroom *sroom;
+ register struct monst *mon;
+ register int sh,sx,sy,i;
+ int goldlim = 500 * dlevel;
+ int moct = 0;
+ struct permonst *morguemon();
+
+ i = nroom;
+ for(sroom = &rooms[rn2(nroom)]; ; sroom++) {
+ if(sroom == &rooms[nroom])
+ sroom = &rooms[0];
+ if(!i-- || sroom->hx < 0)
+ return;
+ if(sroom->rtype)
+ continue;
+ if(type == MORGUE && sroom->rlit)
+ continue;
+ if(has_upstairs(sroom) || (has_dnstairs(sroom) && rn2(3)))
+ continue;
+ if(sroom->doorct == 1 || !rn2(5))
+ break;
+ }
+ sroom->rtype = type;
+ sh = sroom->fdoor;
+ for(sx = sroom->lx; sx <= sroom->hx; sx++)
+ for(sy = sroom->ly; sy <= sroom->hy; sy++){
+ if((sx == sroom->lx && doors[sh].x == sx-1) ||
+ (sx == sroom->hx && doors[sh].x == sx+1) ||
+ (sy == sroom->ly && doors[sh].y == sy-1) ||
+ (sy == sroom->hy && doors[sh].y == sy+1)) continue;
+ mon = makemon(
+ (type == MORGUE) ? morguemon() :
+ (type == BEEHIVE) ? PM_KILLER_BEE : (struct permonst *) 0,
+ sx, sy);
+ if(mon) mon->msleep = 1;
+ switch(type) {
+ case ZOO:
+ i = sq(dist2(sx,sy,doors[sh].x,doors[sh].y));
+ if(i >= goldlim) i = 5*dlevel;
+ goldlim -= i;
+ mkgold((long)(10 + rn2(i)), sx, sy);
+ break;
+ case MORGUE:
+ /* Usually there is one dead body in the morgue */
+ if(!moct && rn2(3)) {
+ mksobj_at(CORPSE, sx, sy);
+ moct++;
+ }
+ break;
+ case BEEHIVE:
+ if(!rn2(3)) mksobj_at(LUMP_OF_ROYAL_JELLY, sx, sy);
+ break;
+ }
+ }
+}
+
+struct permonst *
+morguemon()
+{
+ extern struct permonst pm_ghost;
+ register int i = rn2(100), hd = rn2(dlevel);
+
+ if(hd > 10 && i < 10) return(PM_DEMON);
+ if(hd > 8 && i > 85) return(PM_VAMPIRE);
+ return((i < 40) ? PM_GHOST : (i < 60) ? PM_WRAITH : PM_ZOMBIE);
+}
+
+mkswamp() /* Michiel Huisjes & Fred de Wilde */
+{
+ register struct mkroom *sroom;
+ register int sx,sy,i,eelct = 0;
+ extern struct permonst pm_eel;
+
+ for(i=0; i<5; i++) { /* 5 tries */
+ sroom = &rooms[rn2(nroom)];
+ if(sroom->hx < 0 || sroom->rtype ||
+ has_upstairs(sroom) || has_dnstairs(sroom))
+ continue;
+
+ /* satisfied; make a swamp */
+ sroom->rtype = SWAMP;
+ for(sx = sroom->lx; sx <= sroom->hx; sx++)
+ for(sy = sroom->ly; sy <= sroom->hy; sy++)
+ if((sx+sy)%2 && !o_at(sx,sy) && !t_at(sx,sy)
+ && !m_at(sx,sy) && !nexttodoor(sx,sy)){
+ levl[sx][sy].typ = POOL;
+ levl[sx][sy].scrsym = POOL_SYM;
+ if(!eelct || !rn2(4)) {
+ (void) makemon(PM_EEL, sx, sy);
+ eelct++;
+ }
+ }
+ }
+}
+
+nexttodoor(sx,sy)
+register sx,sy;
+{
+ register dx,dy;
+ register struct rm *lev;
+ for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
+ if((lev = &levl[sx+dx][sy+dy])->typ == DOOR ||
+ lev->typ == SDOOR || lev->typ == LDOOR)
+ return(1);
+ return(0);
+}
+
+has_dnstairs(sroom)
+register struct mkroom *sroom;
+{
+ return(sroom->lx <= xdnstair && xdnstair <= sroom->hx &&
+ sroom->ly <= ydnstair && ydnstair <= sroom->hy);
+}
+
+has_upstairs(sroom)
+register struct mkroom *sroom;
+{
+ return(sroom->lx <= xupstair && xupstair <= sroom->hx &&
+ sroom->ly <= yupstair && yupstair <= sroom->hy);
+}
+
+isbig(sroom)
+register struct mkroom *sroom;
+{
+ register int area = (sroom->hx - sroom->lx) * (sroom->hy - sroom->ly);
+ return( area > 20 );
+}
+
+dist2(x0,y0,x1,y1){
+ return((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1));
+}
+
+sq(a) int a; {
+ return(a*a);
+}
+#endif QUEST
diff --git a/games/hack/hack.mon.c b/games/hack/hack.mon.c
new file mode 100644
index 000000000000..b31c15922c9a
--- /dev/null
+++ b/games/hack/hack.mon.c
@@ -0,0 +1,853 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mon.c - version 1.0.3 */
+
+#include "hack.h"
+#include "hack.mfndpos.h"
+
+#ifndef NULL
+#define NULL (char *) 0
+#endif
+
+extern struct monst *makemon();
+extern struct obj *mkobj_at();
+
+int warnlevel; /* used by movemon and dochugw */
+long lastwarntime;
+int lastwarnlev;
+char *warnings[] = {
+ "white", "pink", "red", "ruby", "purple", "black"
+};
+
+movemon()
+{
+ register struct monst *mtmp;
+ register int fr;
+
+ warnlevel = 0;
+
+ while(1) {
+ /* find a monster that we haven't treated yet */
+ /* note that mtmp or mtmp->nmon might get killed
+ while mtmp moves, so we cannot just walk down the
+ chain (even new monsters might get created!) */
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->mlstmv < moves) goto next_mon;
+ /* treated all monsters */
+ break;
+
+ next_mon:
+ mtmp->mlstmv = moves;
+
+ /* most monsters drown in pools */
+ { boolean inpool, iseel;
+
+ inpool = (levl[mtmp->mx][mtmp->my].typ == POOL);
+ iseel = (mtmp->data->mlet == ';');
+ if(inpool && !iseel) {
+ if(cansee(mtmp->mx,mtmp->my))
+ pline("%s drowns.", Monnam(mtmp));
+ mondead(mtmp);
+ continue;
+ }
+ /* but eels have a difficult time outside */
+ if(iseel && !inpool) {
+ if(mtmp->mhp > 1) mtmp->mhp--;
+ mtmp->mflee = 1;
+ mtmp->mfleetim += 2;
+ }
+ }
+ if(mtmp->mblinded && !--mtmp->mblinded)
+ mtmp->mcansee = 1;
+ if(mtmp->mfleetim && !--mtmp->mfleetim)
+ mtmp->mflee = 0;
+ if(mtmp->mimic) continue;
+ if(mtmp->mspeed != MSLOW || !(moves%2)){
+ /* continue if the monster died fighting */
+ fr = -1;
+ if(Conflict && cansee(mtmp->mx,mtmp->my)
+ && (fr = fightm(mtmp)) == 2)
+ continue;
+ if(fr<0 && dochugw(mtmp))
+ continue;
+ }
+ if(mtmp->mspeed == MFAST && dochugw(mtmp))
+ continue;
+ }
+
+ warnlevel -= u.ulevel;
+ if(warnlevel >= SIZE(warnings))
+ warnlevel = SIZE(warnings)-1;
+ if(warnlevel >= 0)
+ if(warnlevel > lastwarnlev || moves > lastwarntime + 5){
+ register char *rr;
+ switch(Warning & (LEFT_RING | RIGHT_RING)){
+ case LEFT_RING:
+ rr = "Your left ring glows";
+ break;
+ case RIGHT_RING:
+ rr = "Your right ring glows";
+ break;
+ case LEFT_RING | RIGHT_RING:
+ rr = "Both your rings glow";
+ break;
+ default:
+ rr = "Your fingertips glow";
+ break;
+ }
+ pline("%s %s!", rr, warnings[warnlevel]);
+ lastwarntime = moves;
+ lastwarnlev = warnlevel;
+ }
+
+ dmonsfree(); /* remove all dead monsters */
+}
+
+justswld(mtmp,name)
+register struct monst *mtmp;
+char *name;
+{
+
+ mtmp->mx = u.ux;
+ mtmp->my = u.uy;
+ u.ustuck = mtmp;
+ pmon(mtmp);
+ kludge("%s swallows you!",name);
+ more();
+ seeoff(1);
+ u.uswallow = 1;
+ u.uswldtim = 0;
+ swallowed();
+}
+
+youswld(mtmp,dam,die,name)
+register struct monst *mtmp;
+register dam,die;
+char *name;
+{
+ if(mtmp != u.ustuck) return;
+ kludge("%s digests you!",name);
+ u.uhp -= dam;
+ if(u.uswldtim++ >= die){ /* a3 */
+ pline("It totally digests you!");
+ u.uhp = -1;
+ }
+ if(u.uhp < 1) done_in_by(mtmp);
+ /* flags.botlx = 1; /* should we show status line ? */
+}
+
+dochugw(mtmp) register struct monst *mtmp; {
+register x = mtmp->mx;
+register y = mtmp->my;
+register d = dochug(mtmp);
+register dd;
+ if(!d) /* monster still alive */
+ if(Warning)
+ if(!mtmp->mpeaceful)
+ if(mtmp->data->mlevel > warnlevel)
+ if((dd = dist(mtmp->mx,mtmp->my)) < dist(x,y))
+ if(dd < 100)
+ if(!canseemon(mtmp))
+ warnlevel = mtmp->data->mlevel;
+ return(d);
+}
+
+/* returns 1 if monster died moving, 0 otherwise */
+dochug(mtmp)
+register struct monst *mtmp;
+{
+ register struct permonst *mdat;
+ register tmp, nearby, scared;
+
+ if(mtmp->cham && !rn2(6))
+ (void) newcham(mtmp, &mons[dlevel+14+rn2(CMNUM-14-dlevel)]);
+ mdat = mtmp->data;
+ if(mdat->mlevel < 0)
+ panic("bad monster %c (%d)",mdat->mlet,mdat->mlevel);
+
+ /* regenerate monsters */
+ if((!(moves%20) || index(MREGEN, mdat->mlet)) &&
+ mtmp->mhp < mtmp->mhpmax)
+ mtmp->mhp++;
+
+ if(mtmp->mfroz) return(0); /* frozen monsters don't do anything */
+
+ if(mtmp->msleep) {
+ /* wake up, or get out of here. */
+ /* ettins are hard to surprise */
+ /* Nymphs and Leprechauns do not easily wake up */
+ if(cansee(mtmp->mx,mtmp->my) &&
+ (!Stealth || (mdat->mlet == 'e' && rn2(10))) &&
+ (!index("NL",mdat->mlet) || !rn2(50)) &&
+ (Aggravate_monster || index("d1", mdat->mlet)
+ || (!rn2(7) && !mtmp->mimic)))
+ mtmp->msleep = 0;
+ else return(0);
+ }
+
+ /* not frozen or sleeping: wipe out texts written in the dust */
+ wipe_engr_at(mtmp->mx, mtmp->my, 1);
+
+ /* confused monsters get unconfused with small probability */
+ if(mtmp->mconf && !rn2(50)) mtmp->mconf = 0;
+
+ /* some monsters teleport */
+ if(mtmp->mflee && index("tNL", mdat->mlet) && !rn2(40)){
+ rloc(mtmp);
+ return(0);
+ }
+ if(mdat->mmove < rnd(6)) return(0);
+
+ /* fleeing monsters might regain courage */
+ if(mtmp->mflee && !mtmp->mfleetim
+ && mtmp->mhp == mtmp->mhpmax && !rn2(25))
+ mtmp->mflee = 0;
+
+ nearby = (dist(mtmp->mx, mtmp->my) < 3);
+ scared = (nearby && (sengr_at("Elbereth", u.ux, u.uy) ||
+ sobj_at(SCR_SCARE_MONSTER, u.ux, u.uy)));
+ if(scared && !mtmp->mflee) {
+ mtmp->mflee = 1;
+ mtmp->mfleetim = (rn2(7) ? rnd(10) : rnd(100));
+ }
+
+ if(!nearby ||
+ mtmp->mflee ||
+ mtmp->mconf ||
+ (mtmp->minvis && !rn2(3)) ||
+ (index("BIuy", mdat->mlet) && !rn2(4)) ||
+ (mdat->mlet == 'L' && !u.ugold && (mtmp->mgold || rn2(2))) ||
+ (!mtmp->mcansee && !rn2(4)) ||
+ mtmp->mpeaceful
+ ) {
+ tmp = m_move(mtmp,0); /* 2: monster died moving */
+ if(tmp == 2 || (tmp && mdat->mmove <= 12))
+ return(tmp == 2);
+ }
+
+ if(!index("Ea", mdat->mlet) && nearby &&
+ !mtmp->mpeaceful && u.uhp > 0 && !scared) {
+ if(mhitu(mtmp))
+ return(1); /* monster died (e.g. 'y' or 'F') */
+ }
+ /* extra movement for fast monsters */
+ if(mdat->mmove-12 > rnd(12)) tmp = m_move(mtmp,1);
+ return(tmp == 2);
+}
+
+m_move(mtmp,after)
+register struct monst *mtmp;
+{
+ register struct monst *mtmp2;
+ register nx,ny,omx,omy,appr,nearer,cnt,i,j;
+ xchar gx,gy,nix,niy,chcnt;
+ schar chi;
+ boolean likegold, likegems, likeobjs;
+ char msym = mtmp->data->mlet;
+ schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */
+ coord poss[9];
+ int info[9];
+
+ if(mtmp->mfroz || mtmp->msleep)
+ return(0);
+ if(mtmp->mtrapped) {
+ i = mintrap(mtmp);
+ if(i == 2) return(2); /* he died */
+ if(i == 1) return(0); /* still in trap, so didnt move */
+ }
+ if(mtmp->mhide && o_at(mtmp->mx,mtmp->my) && rn2(10))
+ return(0); /* do not leave hiding place */
+
+#ifndef NOWORM
+ if(mtmp->wormno)
+ goto not_special;
+#endif NOWORM
+
+ /* my dog gets a special treatment */
+ if(mtmp->mtame) {
+ return( dog_move(mtmp, after) );
+ }
+
+ /* likewise for shopkeeper */
+ if(mtmp->isshk) {
+ mmoved = shk_move(mtmp);
+ if(mmoved >= 0)
+ goto postmov;
+ mmoved = 0; /* follow player outside shop */
+ }
+
+ /* and for the guard */
+ if(mtmp->isgd) {
+ mmoved = gd_move();
+ goto postmov;
+ }
+
+/* teleport if that lies in our nature ('t') or when badly wounded ('1') */
+ if((msym == 't' && !rn2(5))
+ || (msym == '1' && (mtmp->mhp < 7 || (!xdnstair && !rn2(5))
+ || levl[u.ux][u.uy].typ == STAIRS))) {
+ if(mtmp->mhp < 7 || (msym == 't' && rn2(2)))
+ rloc(mtmp);
+ else
+ mnexto(mtmp);
+ mmoved = 1;
+ goto postmov;
+ }
+
+ /* spit fire ('D') or use a wand ('1') when appropriate */
+ if(index("D1", msym))
+ inrange(mtmp);
+
+ if(msym == 'U' && !mtmp->mcan && canseemon(mtmp) &&
+ mtmp->mcansee && rn2(5)) {
+ if(!Confusion)
+ pline("%s's gaze has confused you!", Monnam(mtmp));
+ else
+ pline("You are getting more and more confused.");
+ if(rn2(3)) mtmp->mcan = 1;
+ Confusion += d(3,4); /* timeout */
+ }
+not_special:
+ if(!mtmp->mflee && u.uswallow && u.ustuck != mtmp) return(1);
+ appr = 1;
+ if(mtmp->mflee) appr = -1;
+ if(mtmp->mconf || Invis || !mtmp->mcansee ||
+ (index("BIy", msym) && !rn2(3)))
+ appr = 0;
+ omx = mtmp->mx;
+ omy = mtmp->my;
+ gx = u.ux;
+ gy = u.uy;
+ if(msym == 'L' && appr == 1 && mtmp->mgold > u.ugold)
+ appr = -1;
+
+ /* random criterion for 'smell' or track finding ability
+ should use mtmp->msmell or sth
+ */
+ if(msym == '@' ||
+ ('a' <= msym && msym <= 'z')) {
+ extern coord *gettrack();
+ register coord *cp;
+ schar mroom;
+ mroom = inroom(omx,omy);
+ if(mroom < 0 || mroom != inroom(u.ux,u.uy)){
+ cp = gettrack(omx,omy);
+ if(cp){
+ gx = cp->x;
+ gy = cp->y;
+ }
+ }
+ }
+
+ /* look for gold or jewels nearby */
+ likegold = (index("LOD", msym) != NULL);
+ likegems = (index("ODu", msym) != NULL);
+ likeobjs = mtmp->mhide;
+#define SRCHRADIUS 25
+ { xchar mind = SRCHRADIUS; /* not too far away */
+ register int dd;
+ if(likegold){
+ register struct gold *gold;
+ for(gold = fgold; gold; gold = gold->ngold)
+ if((dd = DIST(omx,omy,gold->gx,gold->gy)) < mind){
+ mind = dd;
+ gx = gold->gx;
+ gy = gold->gy;
+ }
+ }
+ if(likegems || likeobjs){
+ register struct obj *otmp;
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(likeobjs || otmp->olet == GEM_SYM)
+ if(msym != 'u' ||
+ objects[otmp->otyp].g_val != 0)
+ if((dd = DIST(omx,omy,otmp->ox,otmp->oy)) < mind){
+ mind = dd;
+ gx = otmp->ox;
+ gy = otmp->oy;
+ }
+ }
+ if(mind < SRCHRADIUS && appr == -1) {
+ if(dist(omx,omy) < 10) {
+ gx = u.ux;
+ gy = u.uy;
+ } else
+ appr = 1;
+ }
+ }
+ nix = omx;
+ niy = omy;
+ cnt = mfndpos(mtmp,poss,info,
+ msym == 'u' ? NOTONL :
+ (msym == '@' || msym == '1') ? (ALLOW_SSM | ALLOW_TRAPS) :
+ index(UNDEAD, msym) ? NOGARLIC : ALLOW_TRAPS);
+ /* ALLOW_ROCK for some monsters ? */
+ chcnt = 0;
+ chi = -1;
+ for(i=0; i<cnt; i++) {
+ nx = poss[i].x;
+ ny = poss[i].y;
+ for(j=0; j<MTSZ && j<cnt-1; j++)
+ if(nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
+ if(rn2(4*(cnt-j))) goto nxti;
+#ifdef STUPID
+ /* some stupid compilers think that this is too complicated */
+ { int d1 = DIST(nx,ny,gx,gy);
+ int d2 = DIST(nix,niy,gx,gy);
+ nearer = (d1 < d2);
+ }
+#else
+ nearer = (DIST(nx,ny,gx,gy) < DIST(nix,niy,gx,gy));
+#endif STUPID
+ if((appr == 1 && nearer) || (appr == -1 && !nearer) ||
+ !mmoved ||
+ (!appr && !rn2(++chcnt))){
+ nix = nx;
+ niy = ny;
+ chi = i;
+ mmoved = 1;
+ }
+ nxti: ;
+ }
+ if(mmoved){
+ if(info[chi] & ALLOW_M){
+ mtmp2 = m_at(nix,niy);
+ if(hitmm(mtmp,mtmp2) == 1 && rn2(4) &&
+ hitmm(mtmp2,mtmp) == 2) return(2);
+ return(0);
+ }
+ if(info[chi] & ALLOW_U){
+ (void) hitu(mtmp, d(mtmp->data->damn, mtmp->data->damd)+1);
+ return(0);
+ }
+ mtmp->mx = nix;
+ mtmp->my = niy;
+ for(j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1];
+ mtmp->mtrack[0].x = omx;
+ mtmp->mtrack[0].y = omy;
+#ifndef NOWORM
+ if(mtmp->wormno) worm_move(mtmp);
+#endif NOWORM
+ } else {
+ if(msym == 'u' && rn2(2)){
+ rloc(mtmp);
+ return(0);
+ }
+#ifndef NOWORM
+ if(mtmp->wormno) worm_nomove(mtmp);
+#endif NOWORM
+ }
+postmov:
+ if(mmoved == 1) {
+ if(mintrap(mtmp) == 2) /* he died */
+ return(2);
+ if(likegold) mpickgold(mtmp);
+ if(likegems) mpickgems(mtmp);
+ if(mtmp->mhide) mtmp->mundetected = 1;
+ }
+ pmon(mtmp);
+ return(mmoved);
+}
+
+mpickgold(mtmp) register struct monst *mtmp; {
+register struct gold *gold;
+ while(gold = g_at(mtmp->mx, mtmp->my)){
+ mtmp->mgold += gold->amount;
+ freegold(gold);
+ if(levl[mtmp->mx][mtmp->my].scrsym == '$')
+ newsym(mtmp->mx, mtmp->my);
+ }
+}
+
+mpickgems(mtmp) register struct monst *mtmp; {
+register struct obj *otmp;
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(otmp->olet == GEM_SYM)
+ if(otmp->ox == mtmp->mx && otmp->oy == mtmp->my)
+ if(mtmp->data->mlet != 'u' || objects[otmp->otyp].g_val != 0){
+ freeobj(otmp);
+ mpickobj(mtmp, otmp);
+ if(levl[mtmp->mx][mtmp->my].scrsym == GEM_SYM)
+ newsym(mtmp->mx, mtmp->my); /* %% */
+ return; /* pick only one object */
+ }
+}
+
+/* return number of acceptable neighbour positions */
+mfndpos(mon,poss,info,flag)
+register struct monst *mon;
+coord poss[9];
+int info[9], flag;
+{
+ register int x,y,nx,ny,cnt = 0,ntyp;
+ register struct monst *mtmp;
+ int nowtyp;
+ boolean pool;
+
+ x = mon->mx;
+ y = mon->my;
+ nowtyp = levl[x][y].typ;
+
+ pool = (mon->data->mlet == ';');
+nexttry: /* eels prefer the water, but if there is no water nearby,
+ they will crawl over land */
+ if(mon->mconf) {
+ flag |= ALLOW_ALL;
+ flag &= ~NOTONL;
+ }
+ for(nx = x-1; nx <= x+1; nx++) for(ny = y-1; ny <= y+1; ny++)
+ if(nx != x || ny != y) if(isok(nx,ny))
+ if(!IS_ROCK(ntyp = levl[nx][ny].typ))
+ if(!(nx != x && ny != y && (nowtyp == DOOR || ntyp == DOOR)))
+ if((ntyp == POOL) == pool) {
+ info[cnt] = 0;
+ if(nx == u.ux && ny == u.uy){
+ if(!(flag & ALLOW_U)) continue;
+ info[cnt] = ALLOW_U;
+ } else if(mtmp = m_at(nx,ny)){
+ if(!(flag & ALLOW_M)) continue;
+ info[cnt] = ALLOW_M;
+ if(mtmp->mtame){
+ if(!(flag & ALLOW_TM)) continue;
+ info[cnt] |= ALLOW_TM;
+ }
+ }
+ if(sobj_at(CLOVE_OF_GARLIC, nx, ny)) {
+ if(flag & NOGARLIC) continue;
+ info[cnt] |= NOGARLIC;
+ }
+ if(sobj_at(SCR_SCARE_MONSTER, nx, ny) ||
+ (!mon->mpeaceful && sengr_at("Elbereth", nx, ny))) {
+ if(!(flag & ALLOW_SSM)) continue;
+ info[cnt] |= ALLOW_SSM;
+ }
+ if(sobj_at(ENORMOUS_ROCK, nx, ny)) {
+ if(!(flag & ALLOW_ROCK)) continue;
+ info[cnt] |= ALLOW_ROCK;
+ }
+ if(!Invis && online(nx,ny)){
+ if(flag & NOTONL) continue;
+ info[cnt] |= NOTONL;
+ }
+ /* we cannot avoid traps of an unknown kind */
+ { register struct trap *ttmp = t_at(nx, ny);
+ register int tt;
+ if(ttmp) {
+ tt = 1 << ttmp->ttyp;
+ if(mon->mtrapseen & tt){
+ if(!(flag & tt)) continue;
+ info[cnt] |= tt;
+ }
+ }
+ }
+ poss[cnt].x = nx;
+ poss[cnt].y = ny;
+ cnt++;
+ }
+ if(!cnt && pool && nowtyp != POOL) {
+ pool = FALSE;
+ goto nexttry;
+ }
+ return(cnt);
+}
+
+dist(x,y) int x,y; {
+ return((x-u.ux)*(x-u.ux) + (y-u.uy)*(y-u.uy));
+}
+
+poisoned(string, pname)
+register char *string, *pname;
+{
+ register int i;
+
+ if(Blind) pline("It was poisoned.");
+ else pline("The %s was poisoned!",string);
+ if(Poison_resistance) {
+ pline("The poison doesn't seem to affect you.");
+ return;
+ }
+ i = rn2(10);
+ if(i == 0) {
+ u.uhp = -1;
+ pline("I am afraid the poison was deadly ...");
+ } else if(i <= 5) {
+ losestr(rn1(3,3));
+ } else {
+ losehp(rn1(10,6), pname);
+ }
+ if(u.uhp < 1) {
+ killer = pname;
+ done("died");
+ }
+}
+
+mondead(mtmp)
+register struct monst *mtmp;
+{
+ relobj(mtmp,1);
+ unpmon(mtmp);
+ relmon(mtmp);
+ unstuck(mtmp);
+ if(mtmp->isshk) shkdead(mtmp);
+ if(mtmp->isgd) gddead();
+#ifndef NOWORM
+ if(mtmp->wormno) wormdead(mtmp);
+#endif NOWORM
+ monfree(mtmp);
+}
+
+/* called when monster is moved to larger structure */
+replmon(mtmp,mtmp2)
+register struct monst *mtmp, *mtmp2;
+{
+ relmon(mtmp);
+ monfree(mtmp);
+ mtmp2->nmon = fmon;
+ fmon = mtmp2;
+ if(u.ustuck == mtmp) u.ustuck = mtmp2;
+ if(mtmp2->isshk) replshk(mtmp,mtmp2);
+ if(mtmp2->isgd) replgd(mtmp,mtmp2);
+}
+
+relmon(mon)
+register struct monst *mon;
+{
+ register struct monst *mtmp;
+
+ if(mon == fmon) fmon = fmon->nmon;
+ else {
+ for(mtmp = fmon; mtmp->nmon != mon; mtmp = mtmp->nmon) ;
+ mtmp->nmon = mon->nmon;
+ }
+}
+
+/* we do not free monsters immediately, in order to have their name
+ available shortly after their demise */
+struct monst *fdmon; /* chain of dead monsters, need not to be saved */
+
+monfree(mtmp) register struct monst *mtmp; {
+ mtmp->nmon = fdmon;
+ fdmon = mtmp;
+}
+
+dmonsfree(){
+register struct monst *mtmp;
+ while(mtmp = fdmon){
+ fdmon = mtmp->nmon;
+ free((char *) mtmp);
+ }
+}
+
+unstuck(mtmp)
+register struct monst *mtmp;
+{
+ if(u.ustuck == mtmp) {
+ if(u.uswallow){
+ u.ux = mtmp->mx;
+ u.uy = mtmp->my;
+ u.uswallow = 0;
+ setsee();
+ docrt();
+ }
+ u.ustuck = 0;
+ }
+}
+
+killed(mtmp)
+register struct monst *mtmp;
+{
+#ifdef lint
+#define NEW_SCORING
+#endif lint
+ register int tmp,tmp2,nk,x,y;
+ register struct permonst *mdat;
+ extern long newuexp();
+
+ if(mtmp->cham) mtmp->data = PM_CHAMELEON;
+ mdat = mtmp->data;
+ if(Blind) pline("You destroy it!");
+ else {
+ pline("You destroy %s!",
+ mtmp->mtame ? amonnam(mtmp, "poor") : monnam(mtmp));
+ }
+ if(u.umconf) {
+ if(!Blind) pline("Your hands stop glowing blue.");
+ u.umconf = 0;
+ }
+
+ /* count killed monsters */
+#define MAXMONNO 100
+ nk = 1; /* in case we cannot find it in mons */
+ tmp = mdat - mons; /* index in mons array (if not 'd', '@', ...) */
+ if(tmp >= 0 && tmp < CMNUM+2) {
+ extern char fut_geno[];
+ u.nr_killed[tmp]++;
+ if((nk = u.nr_killed[tmp]) > MAXMONNO &&
+ !index(fut_geno, mdat->mlet))
+ charcat(fut_geno, mdat->mlet);
+ }
+
+ /* punish bad behaviour */
+ if(mdat->mlet == '@') Telepat = 0, u.uluck -= 2;
+ if(mtmp->mpeaceful || mtmp->mtame) u.uluck--;
+ if(mdat->mlet == 'u') u.uluck -= 5;
+ if((int)u.uluck < LUCKMIN) u.uluck = LUCKMIN;
+
+ /* give experience points */
+ tmp = 1 + mdat->mlevel * mdat->mlevel;
+ if(mdat->ac < 3) tmp += 2*(7 - mdat->ac);
+ if(index("AcsSDXaeRTVWU&In:P", mdat->mlet))
+ tmp += 2*mdat->mlevel;
+ if(index("DeV&P",mdat->mlet)) tmp += (7*mdat->mlevel);
+ if(mdat->mlevel > 6) tmp += 50;
+ if(mdat->mlet == ';') tmp += 1000;
+
+#ifdef NEW_SCORING
+ /* ------- recent addition: make nr of points decrease
+ when this is not the first of this kind */
+ { int ul = u.ulevel;
+ int ml = mdat->mlevel;
+
+ if(ul < 14) /* points are given based on present and future level */
+ for(tmp2 = 0; !tmp2 || ul + tmp2 <= ml; tmp2++)
+ if(u.uexp + 1 + (tmp + ((tmp2 <= 0) ? 0 : 4<<(tmp2-1)))/nk
+ >= 10*pow((unsigned)(ul-1)))
+ if(++ul == 14) break;
+
+ tmp2 = ml - ul -1;
+ tmp = (tmp + ((tmp2 < 0) ? 0 : 4<<tmp2))/nk;
+ if(!tmp) tmp = 1;
+ }
+ /* note: ul is not necessarily the future value of u.ulevel */
+ /* ------- end of recent valuation change ------- */
+#endif NEW_SCORING
+
+ more_experienced(tmp,0);
+ flags.botl = 1;
+ while(u.ulevel < 14 && u.uexp >= newuexp()){
+ pline("Welcome to experience level %u.", ++u.ulevel);
+ tmp = rnd(10);
+ if(tmp < 3) tmp = rnd(10);
+ u.uhpmax += tmp;
+ u.uhp += tmp;
+ flags.botl = 1;
+ }
+
+ /* dispose of monster and make cadaver */
+ x = mtmp->mx; y = mtmp->my;
+ mondead(mtmp);
+ tmp = mdat->mlet;
+ if(tmp == 'm') { /* he killed a minotaur, give him a wand of digging */
+ /* note: the dead minotaur will be on top of it! */
+ mksobj_at(WAN_DIGGING, x, y);
+ /* if(cansee(x,y)) atl(x,y,fobj->olet); */
+ stackobj(fobj);
+ } else
+#ifndef NOWORM
+ if(tmp == 'w') {
+ mksobj_at(WORM_TOOTH, x, y);
+ stackobj(fobj);
+ } else
+#endif NOWORM
+ if(!letter(tmp) || (!index("mw", tmp) && !rn2(3))) tmp = 0;
+
+ if(ACCESSIBLE(levl[x][y].typ)) /* might be mimic in wall or dead eel*/
+ if(x != u.ux || y != u.uy) /* might be here after swallowed */
+ if(index("NTVm&",mdat->mlet) || rn2(5)) {
+ register struct obj *obj2 = mkobj_at(tmp,x,y);
+ if(cansee(x,y))
+ atl(x,y,obj2->olet);
+ stackobj(obj2);
+ }
+}
+
+kludge(str,arg)
+register char *str,*arg;
+{
+ if(Blind) {
+ if(*str == '%') pline(str,"It");
+ else pline(str,"it");
+ } else pline(str,arg);
+}
+
+rescham() /* force all chameleons to become normal */
+{
+ register struct monst *mtmp;
+
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->cham) {
+ mtmp->cham = 0;
+ (void) newcham(mtmp, PM_CHAMELEON);
+ }
+}
+
+newcham(mtmp,mdat) /* make a chameleon look like a new monster */
+ /* returns 1 if the monster actually changed */
+register struct monst *mtmp;
+register struct permonst *mdat;
+{
+ register mhp, hpn, hpd;
+
+ if(mdat == mtmp->data) return(0); /* still the same monster */
+#ifndef NOWORM
+ if(mtmp->wormno) wormdead(mtmp); /* throw tail away */
+#endif NOWORM
+ if (u.ustuck == mtmp) {
+ if (u.uswallow) {
+ u.uswallow = 0;
+ u.uswldtim = 0;
+ mnexto (mtmp);
+ docrt ();
+ prme ();
+ }
+ u.ustuck = 0;
+ }
+ hpn = mtmp->mhp;
+ hpd = (mtmp->data->mlevel)*8;
+ if(!hpd) hpd = 4;
+ mtmp->data = mdat;
+ mhp = (mdat->mlevel)*8;
+ /* new hp: same fraction of max as before */
+ mtmp->mhp = 2 + (hpn*mhp)/hpd;
+ hpn = mtmp->mhpmax;
+ mtmp->mhpmax = 2 + (hpn*mhp)/hpd;
+ mtmp->minvis = (mdat->mlet == 'I') ? 1 : 0;
+#ifndef NOWORM
+ if(mdat->mlet == 'w' && getwn(mtmp)) initworm(mtmp);
+ /* perhaps we should clear mtmp->mtame here? */
+#endif NOWORM
+ unpmon(mtmp); /* necessary for 'I' and to force pmon */
+ pmon(mtmp);
+ return(1);
+}
+
+mnexto(mtmp) /* Make monster mtmp next to you (if possible) */
+struct monst *mtmp;
+{
+ extern coord enexto();
+ coord mm;
+ mm = enexto(u.ux, u.uy);
+ mtmp->mx = mm.x;
+ mtmp->my = mm.y;
+ pmon(mtmp);
+}
+
+ishuman(mtmp) register struct monst *mtmp; {
+ return(mtmp->data->mlet == '@');
+}
+
+setmangry(mtmp) register struct monst *mtmp; {
+ if(!mtmp->mpeaceful) return;
+ if(mtmp->mtame) return;
+ mtmp->mpeaceful = 0;
+ if(ishuman(mtmp)) pline("%s gets angry!", Monnam(mtmp));
+}
+
+/* not one hundred procent correct: now a snake may hide under an
+ invisible object */
+canseemon(mtmp)
+register struct monst *mtmp;
+{
+ return((!mtmp->minvis || See_invisible)
+ && (!mtmp->mhide || !o_at(mtmp->mx,mtmp->my))
+ && cansee(mtmp->mx, mtmp->my));
+}
diff --git a/games/hack/hack.monst.c b/games/hack/hack.monst.c
new file mode 100644
index 000000000000..b682b59de230
--- /dev/null
+++ b/games/hack/hack.monst.c
@@ -0,0 +1,79 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.monst.c - version 1.0.2 */
+
+#include "hack.h"
+#include "def.eshk.h"
+extern char plname[PL_NSIZ];
+
+struct permonst mons[CMNUM+2] = {
+ { "bat", 'B',1,22,8,1,4,0 },
+ { "gnome", 'G',1,6,5,1,6,0 },
+ { "hobgoblin", 'H',1,9,5,1,8,0 },
+ { "jackal", 'J',0,12,7,1,2,0 },
+ { "kobold", 'K',1,6,7,1,4,0 },
+ { "leprechaun", 'L',5,15,8,1,2,0 },
+ { "giant rat", 'r',0,12,7,1,3,0 },
+ { "acid blob", 'a',2,3,8,0,0,0 },
+ { "floating eye", 'E',2,1,9,0,0,0 },
+ { "homunculus", 'h',2,6,6,1,3,0 },
+ { "imp", 'i',2,6,2,1,4,0 },
+ { "orc", 'O',2,9,6,1,8,0 },
+ { "yellow light", 'y',3,15,0,0,0,0 },
+ { "zombie", 'Z',2,6,8,1,8,0 },
+ { "giant ant", 'A',3,18,3,1,6,0 },
+ { "fog cloud", 'f',3,1,0,1,6,0 },
+ { "nymph", 'N',6,12,9,1,2,0 },
+ { "piercer", 'p',3,1,3,2,6,0 },
+ { "quasit", 'Q',3,15,3,1,4,0 },
+ { "quivering blob", 'q',3,1,8,1,8,0 },
+ { "violet fungi", 'v',3,1,7,1,4,0 },
+ { "giant beetle", 'b',4,6,4,3,4,0 },
+ { "centaur", 'C',4,18,4,1,6,0 },
+ { "cockatrice", 'c',4,6,6,1,3,0 },
+ { "gelatinous cube", 'g',4,6,8,2,4,0 },
+ { "jaguar", 'j',4,15,6,1,8,0 },
+ { "killer bee", 'k',4,14,4,2,4,0 },
+ { "snake", 'S',4,15,3,1,6,0 },
+ { "freezing sphere", 'F',2,13,4,0,0,0 },
+ { "owlbear", 'o',5,12,5,2,6,0 },
+ { "rust monster", 'R',10,18,3,0,0,0 },
+ { "scorpion", 's',5,15,3,1,4,0 },
+ { "tengu", 't',5,13,5,1,7,0 },
+ { "wraith", 'W',5,12,5,1,6,0 },
+#ifdef NOWORM
+ { "wumpus", 'w',8,3,2,3,6,0 },
+#else
+ { "long worm", 'w',8,3,5,1,4,0 },
+#endif NOWORM
+ { "large dog", 'd',6,15,4,2,4,0 },
+ { "leocrotta", 'l',6,18,4,3,6,0 },
+ { "mimic", 'M',7,3,7,3,4,0 },
+ { "troll", 'T',7,12,4,2,7,0 },
+ { "unicorn", 'u',8,24,5,1,10,0 },
+ { "yeti", 'Y',5,15,6,1,6,0 },
+ { "stalker", 'I',8,12,3,4,4,0 },
+ { "umber hulk", 'U',9,6,2,2,10,0 },
+ { "vampire", 'V',8,12,1,1,6,0 },
+ { "xorn", 'X',8,9,-2,4,6,0 },
+ { "xan", 'x',7,18,-2,2,4,0 },
+ { "zruty", 'z',9,8,3,3,6,0 },
+ { "chameleon", ':',6,5,6,4,2,0 },
+ { "dragon", 'D',10,9,-1,3,8,0 },
+ { "ettin", 'e',10,12,3,2,8,0 },
+ { "lurker above", '\'',10,3,3,0,0,0 },
+ { "nurse", 'n',11,6,0,1,3,0 },
+ { "trapper", ',',12,3,3,0,0,0 },
+ { "purple worm", 'P',15,9,6,2,8,0 },
+ { "demon", '&',10,12,-4,1,4,0 },
+ { "minotaur", 'm',15,15,6,4,10,0 },
+ { "shopkeeper", '@', 12, 18, 0, 4, 8, sizeof(struct eshk) }
+};
+
+struct permonst pm_ghost = { "ghost", ' ', 10, 3, -5, 1, 1, sizeof(plname) };
+struct permonst pm_wizard = {
+ "wizard of Yendor", '1', 15, 12, -2, 1, 12, 0
+};
+#ifdef MAIL
+struct permonst pm_mail_daemon = { "mail daemon", '2', 100, 1, 10, 0, 0, 0 };
+#endif MAIL
+struct permonst pm_eel = { "giant eel", ';', 15, 6, -3, 3, 6, 0 };
diff --git a/games/hack/hack.o_init.c b/games/hack/hack.o_init.c
new file mode 100644
index 000000000000..37b33aefb081
--- /dev/null
+++ b/games/hack/hack.o_init.c
@@ -0,0 +1,160 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.o_init.c - version 1.0.3 */
+
+#include "config.h" /* for typedefs */
+#include "def.objects.h"
+#include "hack.onames.h" /* for LAST_GEM */
+extern char *index();
+
+int
+letindex(let) register char let; {
+register int i = 0;
+register char ch;
+ while((ch = obj_symbols[i++]) != 0)
+ if(ch == let) return(i);
+ return(0);
+}
+
+init_objects(){
+register int i, j, first, last, sum, end;
+register char let, *tmp;
+ /* init base; if probs given check that they add up to 100,
+ otherwise compute probs; shuffle descriptions */
+ end = SIZE(objects);
+ first = 0;
+ while( first < end ) {
+ let = objects[first].oc_olet;
+ last = first+1;
+ while(last < end && objects[last].oc_olet == let
+ && objects[last].oc_name != NULL)
+ last++;
+ i = letindex(let);
+ if((!i && let != ILLOBJ_SYM) || bases[i] != 0)
+ error("initialization error");
+ bases[i] = first;
+
+ if(let == GEM_SYM)
+ setgemprobs();
+ check:
+ sum = 0;
+ for(j = first; j < last; j++) sum += objects[j].oc_prob;
+ if(sum == 0) {
+ for(j = first; j < last; j++)
+ objects[j].oc_prob = (100+j-first)/(last-first);
+ goto check;
+ }
+ if(sum != 100)
+ error("init-prob error for %c", let);
+
+ if(objects[first].oc_descr != NULL && let != TOOL_SYM){
+ /* shuffle, also some additional descriptions */
+ while(last < end && objects[last].oc_olet == let)
+ last++;
+ j = last;
+ while(--j > first) {
+ i = first + rn2(j+1-first);
+ tmp = objects[j].oc_descr;
+ objects[j].oc_descr = objects[i].oc_descr;
+ objects[i].oc_descr = tmp;
+ }
+ }
+ first = last;
+ }
+}
+
+probtype(let) register char let; {
+register int i = bases[letindex(let)];
+register int prob = rn2(100);
+ while((prob -= objects[i].oc_prob) >= 0) i++;
+ if(objects[i].oc_olet != let || !objects[i].oc_name)
+ panic("probtype(%c) error, i=%d", let, i);
+ return(i);
+}
+
+setgemprobs()
+{
+ register int j,first;
+ extern xchar dlevel;
+
+ first = bases[letindex(GEM_SYM)];
+
+ for(j = 0; j < 9-dlevel/3; j++)
+ objects[first+j].oc_prob = 0;
+ first += j;
+ if(first >= LAST_GEM || first >= SIZE(objects) ||
+ objects[first].oc_olet != GEM_SYM ||
+ objects[first].oc_name == NULL)
+ printf("Not enough gems? - first=%d j=%d LAST_GEM=%d\n",
+ first, j, LAST_GEM);
+ for(j = first; j < LAST_GEM; j++)
+ objects[j].oc_prob = (20+j-first)/(LAST_GEM-first);
+}
+
+oinit() /* level dependent initialization */
+{
+ setgemprobs();
+}
+
+extern long *alloc();
+
+savenames(fd) register fd; {
+register int i;
+unsigned len;
+ bwrite(fd, (char *) bases, sizeof bases);
+ bwrite(fd, (char *) objects, sizeof objects);
+ /* as long as we use only one version of Hack/Quest we
+ need not save oc_name and oc_descr, but we must save
+ oc_uname for all objects */
+ for(i=0; i < SIZE(objects); i++) {
+ if(objects[i].oc_uname) {
+ len = strlen(objects[i].oc_uname)+1;
+ bwrite(fd, (char *) &len, sizeof len);
+ bwrite(fd, objects[i].oc_uname, len);
+ }
+ }
+}
+
+restnames(fd) register fd; {
+register int i;
+unsigned len;
+ mread(fd, (char *) bases, sizeof bases);
+ mread(fd, (char *) objects, sizeof objects);
+ for(i=0; i < SIZE(objects); i++) if(objects[i].oc_uname) {
+ mread(fd, (char *) &len, sizeof len);
+ objects[i].oc_uname = (char *) alloc(len);
+ mread(fd, objects[i].oc_uname, len);
+ }
+}
+
+dodiscovered() /* free after Robert Viduya */
+{
+ extern char *typename();
+ register int i, end;
+ int ct = 0;
+
+ cornline(0, "Discoveries");
+
+ end = SIZE(objects);
+ for (i = 0; i < end; i++) {
+ if (interesting_to_discover (i)) {
+ ct++;
+ cornline(1, typename(i));
+ }
+ }
+ if (ct == 0) {
+ pline ("You haven't discovered anything yet...");
+ cornline(3, (char *) 0);
+ } else
+ cornline(2, (char *) 0);
+
+ return(0);
+}
+
+interesting_to_discover(i)
+register int i;
+{
+ return(
+ objects[i].oc_uname != NULL ||
+ (objects[i].oc_name_known && objects[i].oc_descr != NULL)
+ );
+}
diff --git a/games/hack/hack.objnam.c b/games/hack/hack.objnam.c
new file mode 100644
index 000000000000..a1c966cd912e
--- /dev/null
+++ b/games/hack/hack.objnam.c
@@ -0,0 +1,547 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.objnam.c - version 1.0.2 */
+
+#include "hack.h"
+#define Sprintf (void) sprintf
+#define Strcat (void) strcat
+#define Strcpy (void) strcpy
+#define PREFIX 15
+extern char *eos();
+extern int bases[];
+
+char *
+strprepend(s,pref) register char *s, *pref; {
+register int i = strlen(pref);
+ if(i > PREFIX) {
+ pline("WARNING: prefix too short.");
+ return(s);
+ }
+ s -= i;
+ (void) strncpy(s, pref, i); /* do not copy trailing 0 */
+ return(s);
+}
+
+char *
+sitoa(a) int a; {
+static char buf[13];
+ Sprintf(buf, (a < 0) ? "%d" : "+%d", a);
+ return(buf);
+}
+
+char *
+typename(otyp)
+register int otyp;
+{
+static char buf[BUFSZ];
+register struct objclass *ocl = &objects[otyp];
+register char *an = ocl->oc_name;
+register char *dn = ocl->oc_descr;
+register char *un = ocl->oc_uname;
+register int nn = ocl->oc_name_known;
+ switch(ocl->oc_olet) {
+ case POTION_SYM:
+ Strcpy(buf, "potion");
+ break;
+ case SCROLL_SYM:
+ Strcpy(buf, "scroll");
+ break;
+ case WAND_SYM:
+ Strcpy(buf, "wand");
+ break;
+ case RING_SYM:
+ Strcpy(buf, "ring");
+ break;
+ default:
+ if(nn) {
+ Strcpy(buf, an);
+ if(otyp >= TURQUOISE && otyp <= JADE)
+ Strcat(buf, " stone");
+ if(un)
+ Sprintf(eos(buf), " called %s", un);
+ if(dn)
+ Sprintf(eos(buf), " (%s)", dn);
+ } else {
+ Strcpy(buf, dn ? dn : an);
+ if(ocl->oc_olet == GEM_SYM)
+ Strcat(buf, " gem");
+ if(un)
+ Sprintf(eos(buf), " called %s", un);
+ }
+ return(buf);
+ }
+ /* here for ring/scroll/potion/wand */
+ if(nn)
+ Sprintf(eos(buf), " of %s", an);
+ if(un)
+ Sprintf(eos(buf), " called %s", un);
+ if(dn)
+ Sprintf(eos(buf), " (%s)", dn);
+ return(buf);
+}
+
+char *
+xname(obj)
+register struct obj *obj;
+{
+static char bufr[BUFSZ];
+register char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */
+register int nn = objects[obj->otyp].oc_name_known;
+register char *an = objects[obj->otyp].oc_name;
+register char *dn = objects[obj->otyp].oc_descr;
+register char *un = objects[obj->otyp].oc_uname;
+register int pl = (obj->quan != 1);
+ if(!obj->dknown && !Blind) obj->dknown = 1; /* %% doesnt belong here */
+ switch(obj->olet) {
+ case AMULET_SYM:
+ Strcpy(buf, (obj->spe < 0 && obj->known)
+ ? "cheap plastic imitation of the " : "");
+ Strcat(buf,"Amulet of Yendor");
+ break;
+ case TOOL_SYM:
+ if(!nn) {
+ Strcpy(buf, dn);
+ break;
+ }
+ Strcpy(buf,an);
+ break;
+ case FOOD_SYM:
+ if(obj->otyp == DEAD_HOMUNCULUS && pl) {
+ pl = 0;
+ Strcpy(buf, "dead homunculi");
+ break;
+ }
+ /* fungis ? */
+ /* fall into next case */
+ case WEAPON_SYM:
+ if(obj->otyp == WORM_TOOTH && pl) {
+ pl = 0;
+ Strcpy(buf, "worm teeth");
+ break;
+ }
+ if(obj->otyp == CRYSKNIFE && pl) {
+ pl = 0;
+ Strcpy(buf, "crysknives");
+ break;
+ }
+ /* fall into next case */
+ case ARMOR_SYM:
+ case CHAIN_SYM:
+ case ROCK_SYM:
+ Strcpy(buf,an);
+ break;
+ case BALL_SYM:
+ Sprintf(buf, "%sheavy iron ball",
+ (obj->owt > objects[obj->otyp].oc_weight) ? "very " : "");
+ break;
+ case POTION_SYM:
+ if(nn || un || !obj->dknown) {
+ Strcpy(buf, "potion");
+ if(pl) {
+ pl = 0;
+ Strcat(buf, "s");
+ }
+ if(!obj->dknown) break;
+ if(un) {
+ Strcat(buf, " called ");
+ Strcat(buf, un);
+ } else {
+ Strcat(buf, " of ");
+ Strcat(buf, an);
+ }
+ } else {
+ Strcpy(buf, dn);
+ Strcat(buf, " potion");
+ }
+ break;
+ case SCROLL_SYM:
+ Strcpy(buf, "scroll");
+ if(pl) {
+ pl = 0;
+ Strcat(buf, "s");
+ }
+ if(!obj->dknown) break;
+ if(nn) {
+ Strcat(buf, " of ");
+ Strcat(buf, an);
+ } else if(un) {
+ Strcat(buf, " called ");
+ Strcat(buf, un);
+ } else {
+ Strcat(buf, " labeled ");
+ Strcat(buf, dn);
+ }
+ break;
+ case WAND_SYM:
+ if(!obj->dknown)
+ Sprintf(buf, "wand");
+ else if(nn)
+ Sprintf(buf, "wand of %s", an);
+ else if(un)
+ Sprintf(buf, "wand called %s", un);
+ else
+ Sprintf(buf, "%s wand", dn);
+ break;
+ case RING_SYM:
+ if(!obj->dknown)
+ Sprintf(buf, "ring");
+ else if(nn)
+ Sprintf(buf, "ring of %s", an);
+ else if(un)
+ Sprintf(buf, "ring called %s", un);
+ else
+ Sprintf(buf, "%s ring", dn);
+ break;
+ case GEM_SYM:
+ if(!obj->dknown) {
+ Strcpy(buf, "gem");
+ break;
+ }
+ if(!nn) {
+ Sprintf(buf, "%s gem", dn);
+ break;
+ }
+ Strcpy(buf, an);
+ if(obj->otyp >= TURQUOISE && obj->otyp <= JADE)
+ Strcat(buf, " stone");
+ break;
+ default:
+ Sprintf(buf,"glorkum %c (0%o) %u %d",
+ obj->olet,obj->olet,obj->otyp,obj->spe);
+ }
+ if(pl) {
+ register char *p;
+
+ for(p = buf; *p; p++) {
+ if(!strncmp(" of ", p, 4)) {
+ /* pieces of, cloves of, lumps of */
+ register int c1, c2 = 's';
+
+ do {
+ c1 = c2; c2 = *p; *p++ = c1;
+ } while(c1);
+ goto nopl;
+ }
+ }
+ p = eos(buf)-1;
+ if(*p == 's' || *p == 'z' || *p == 'x' ||
+ (*p == 'h' && p[-1] == 's'))
+ Strcat(buf, "es"); /* boxes */
+ else if(*p == 'y' && !index(vowels, p[-1]))
+ Strcpy(p, "ies"); /* rubies, zruties */
+ else
+ Strcat(buf, "s");
+ }
+nopl:
+ if(obj->onamelth) {
+ Strcat(buf, " named ");
+ Strcat(buf, ONAME(obj));
+ }
+ return(buf);
+}
+
+char *
+doname(obj)
+register struct obj *obj;
+{
+char prefix[PREFIX];
+register char *bp = xname(obj);
+ if(obj->quan != 1)
+ Sprintf(prefix, "%u ", obj->quan);
+ else
+ Strcpy(prefix, "a ");
+ switch(obj->olet) {
+ case AMULET_SYM:
+ if(strncmp(bp, "cheap ", 6))
+ Strcpy(prefix, "the ");
+ break;
+ case ARMOR_SYM:
+ if(obj->owornmask & W_ARMOR)
+ Strcat(bp, " (being worn)");
+ /* fall into next case */
+ case WEAPON_SYM:
+ if(obj->known) {
+ Strcat(prefix, sitoa(obj->spe));
+ Strcat(prefix, " ");
+ }
+ break;
+ case WAND_SYM:
+ if(obj->known)
+ Sprintf(eos(bp), " (%d)", obj->spe);
+ break;
+ case RING_SYM:
+ if(obj->owornmask & W_RINGR) Strcat(bp, " (on right hand)");
+ if(obj->owornmask & W_RINGL) Strcat(bp, " (on left hand)");
+ if(obj->known && (objects[obj->otyp].bits & SPEC)) {
+ Strcat(prefix, sitoa(obj->spe));
+ Strcat(prefix, " ");
+ }
+ break;
+ }
+ if(obj->owornmask & W_WEP)
+ Strcat(bp, " (weapon in hand)");
+ if(obj->unpaid)
+ Strcat(bp, " (unpaid)");
+ if(!strcmp(prefix, "a ") && index(vowels, *bp))
+ Strcpy(prefix, "an ");
+ bp = strprepend(bp, prefix);
+ return(bp);
+}
+
+/* used only in hack.fight.c (thitu) */
+setan(str,buf)
+register char *str,*buf;
+{
+ if(index(vowels,*str))
+ Sprintf(buf, "an %s", str);
+ else
+ Sprintf(buf, "a %s", str);
+}
+
+char *
+aobjnam(otmp,verb) register struct obj *otmp; register char *verb; {
+register char *bp = xname(otmp);
+char prefix[PREFIX];
+ if(otmp->quan != 1) {
+ Sprintf(prefix, "%u ", otmp->quan);
+ bp = strprepend(bp, prefix);
+ }
+
+ if(verb) {
+ /* verb is given in plural (i.e., without trailing s) */
+ Strcat(bp, " ");
+ if(otmp->quan != 1)
+ Strcat(bp, verb);
+ else if(!strcmp(verb, "are"))
+ Strcat(bp, "is");
+ else {
+ Strcat(bp, verb);
+ Strcat(bp, "s");
+ }
+ }
+ return(bp);
+}
+
+char *
+Doname(obj)
+register struct obj *obj;
+{
+ register char *s = doname(obj);
+
+ if('a' <= *s && *s <= 'z') *s -= ('a' - 'A');
+ return(s);
+}
+
+char *wrp[] = { "wand", "ring", "potion", "scroll", "gem" };
+char wrpsym[] = { WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM };
+
+struct obj *
+readobjnam(bp) register char *bp; {
+register char *p;
+register int i;
+int cnt, spe, spesgn, typ, heavy;
+char let;
+char *un, *dn, *an;
+/* int the = 0; char *oname = 0; */
+ cnt = spe = spesgn = typ = heavy = 0;
+ let = 0;
+ an = dn = un = 0;
+ for(p = bp; *p; p++)
+ if('A' <= *p && *p <= 'Z') *p += 'a'-'A';
+ if(!strncmp(bp, "the ", 4)){
+/* the = 1; */
+ bp += 4;
+ } else if(!strncmp(bp, "an ", 3)){
+ cnt = 1;
+ bp += 3;
+ } else if(!strncmp(bp, "a ", 2)){
+ cnt = 1;
+ bp += 2;
+ }
+ if(!cnt && digit(*bp)){
+ cnt = atoi(bp);
+ while(digit(*bp)) bp++;
+ while(*bp == ' ') bp++;
+ }
+ if(!cnt) cnt = 1; /* %% what with "gems" etc. ? */
+
+ if(*bp == '+' || *bp == '-'){
+ spesgn = (*bp++ == '+') ? 1 : -1;
+ spe = atoi(bp);
+ while(digit(*bp)) bp++;
+ while(*bp == ' ') bp++;
+ } else {
+ p = rindex(bp, '(');
+ if(p) {
+ if(p > bp && p[-1] == ' ') p[-1] = 0;
+ else *p = 0;
+ p++;
+ spe = atoi(p);
+ while(digit(*p)) p++;
+ if(strcmp(p, ")")) spe = 0;
+ else spesgn = 1;
+ }
+ }
+ /* now we have the actual name, as delivered by xname, say
+ green potions called whisky
+ scrolls labeled "QWERTY"
+ egg
+ dead zruties
+ fortune cookies
+ very heavy iron ball named hoei
+ wand of wishing
+ elven cloak
+ */
+ for(p = bp; *p; p++) if(!strncmp(p, " named ", 7)) {
+ *p = 0;
+/* oname = p+7; */
+ }
+ for(p = bp; *p; p++) if(!strncmp(p, " called ", 8)) {
+ *p = 0;
+ un = p+8;
+ }
+ for(p = bp; *p; p++) if(!strncmp(p, " labeled ", 9)) {
+ *p = 0;
+ dn = p+9;
+ }
+
+ /* first change to singular if necessary */
+ if(cnt != 1) {
+ /* find "cloves of garlic", "worthless pieces of blue glass" */
+ for(p = bp; *p; p++) if(!strncmp(p, "s of ", 5)){
+ while(*p = p[1]) p++;
+ goto sing;
+ }
+ /* remove -s or -es (boxes) or -ies (rubies, zruties) */
+ p = eos(bp);
+ if(p[-1] == 's') {
+ if(p[-2] == 'e') {
+ if(p[-3] == 'i') {
+ if(!strcmp(p-7, "cookies"))
+ goto mins;
+ Strcpy(p-3, "y");
+ goto sing;
+ }
+
+ /* note: cloves / knives from clove / knife */
+ if(!strcmp(p-6, "knives")) {
+ Strcpy(p-3, "fe");
+ goto sing;
+ }
+
+ /* note: nurses, axes but boxes */
+ if(!strcmp(p-5, "boxes")) {
+ p[-2] = 0;
+ goto sing;
+ }
+ }
+ mins:
+ p[-1] = 0;
+ } else {
+ if(!strcmp(p-9, "homunculi")) {
+ Strcpy(p-1, "us"); /* !! makes string longer */
+ goto sing;
+ }
+ if(!strcmp(p-5, "teeth")) {
+ Strcpy(p-5, "tooth");
+ goto sing;
+ }
+ /* here we cannot find the plural suffix */
+ }
+ }
+sing:
+ if(!strcmp(bp, "amulet of yendor")) {
+ typ = AMULET_OF_YENDOR;
+ goto typfnd;
+ }
+ p = eos(bp);
+ if(!strcmp(p-5, " mail")){ /* Note: ring mail is not a ring ! */
+ let = ARMOR_SYM;
+ an = bp;
+ goto srch;
+ }
+ for(i = 0; i < sizeof(wrpsym); i++) {
+ register int j = strlen(wrp[i]);
+ if(!strncmp(bp, wrp[i], j)){
+ let = wrpsym[i];
+ bp += j;
+ if(!strncmp(bp, " of ", 4)) an = bp+4;
+ /* else if(*bp) ?? */
+ goto srch;
+ }
+ if(!strcmp(p-j, wrp[i])){
+ let = wrpsym[i];
+ p -= j;
+ *p = 0;
+ if(p[-1] == ' ') p[-1] = 0;
+ dn = bp;
+ goto srch;
+ }
+ }
+ if(!strcmp(p-6, " stone")){
+ p[-6] = 0;
+ let = GEM_SYM;
+ an = bp;
+ goto srch;
+ }
+ if(!strcmp(bp, "very heavy iron ball")){
+ heavy = 1;
+ typ = HEAVY_IRON_BALL;
+ goto typfnd;
+ }
+ an = bp;
+srch:
+ if(!an && !dn && !un)
+ goto any;
+ i = 1;
+ if(let) i = bases[letindex(let)];
+ while(i <= NROFOBJECTS && (!let || objects[i].oc_olet == let)){
+ register char *zn = objects[i].oc_name;
+
+ if(!zn) goto nxti;
+ if(an && strcmp(an, zn))
+ goto nxti;
+ if(dn && (!(zn = objects[i].oc_descr) || strcmp(dn, zn)))
+ goto nxti;
+ if(un && (!(zn = objects[i].oc_uname) || strcmp(un, zn)))
+ goto nxti;
+ typ = i;
+ goto typfnd;
+ nxti:
+ i++;
+ }
+any:
+ if(!let) let = wrpsym[rn2(sizeof(wrpsym))];
+ typ = probtype(let);
+typfnd:
+ { register struct obj *otmp;
+ extern struct obj *mksobj();
+ let = objects[typ].oc_olet;
+ otmp = mksobj(typ);
+ if(heavy)
+ otmp->owt += 15;
+ if(cnt > 0 && index("%?!*)", let) &&
+ (cnt < 4 || (let == WEAPON_SYM && typ <= ROCK && cnt < 20)))
+ otmp->quan = cnt;
+
+ if(spe > 3 && spe > otmp->spe)
+ spe = 0;
+ else if(let == WAND_SYM)
+ spe = otmp->spe;
+ if(spe == 3 && u.uluck < 0)
+ spesgn = -1;
+ if(let != WAND_SYM && spesgn == -1)
+ spe = -spe;
+ if(let == BALL_SYM)
+ spe = 0;
+ else if(let == AMULET_SYM)
+ spe = -1;
+ else if(typ == WAN_WISHING && rn2(10))
+ spe = (rn2(10) ? -1 : 0);
+ otmp->spe = spe;
+
+ if(spesgn == -1)
+ otmp->cursed = 1;
+
+ return(otmp);
+ }
+}
diff --git a/games/hack/hack.options.c b/games/hack/hack.options.c
new file mode 100644
index 000000000000..ed95de310ead
--- /dev/null
+++ b/games/hack/hack.options.c
@@ -0,0 +1,203 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.options.c - version 1.0.3 */
+
+#include "config.h"
+#include "hack.h"
+extern char *eos();
+
+initoptions()
+{
+ register char *opts;
+ extern char *getenv();
+
+ flags.time = flags.nonews = flags.notombstone = flags.end_own =
+ flags.standout = flags.nonull = FALSE;
+ flags.no_rest_on_space = TRUE;
+ flags.invlet_constant = TRUE;
+ flags.end_top = 5;
+ flags.end_around = 4;
+ flags.female = FALSE; /* players are usually male */
+
+ if(opts = getenv("HACKOPTIONS"))
+ parseoptions(opts,TRUE);
+}
+
+parseoptions(opts, from_env)
+register char *opts;
+boolean from_env;
+{
+ register char *op,*op2;
+ unsigned num;
+ boolean negated;
+
+ if(op = index(opts, ',')) {
+ *op++ = 0;
+ parseoptions(op, from_env);
+ }
+ if(op = index(opts, ' ')) {
+ op2 = op;
+ while(*op++)
+ if(*op != ' ') *op2++ = *op;
+ }
+ if(!*opts) return;
+ negated = FALSE;
+ while((*opts == '!') || !strncmp(opts, "no", 2)) {
+ if(*opts == '!') opts++; else opts += 2;
+ negated = !negated;
+ }
+
+ if(!strncmp(opts,"standout",8)) {
+ flags.standout = !negated;
+ return;
+ }
+
+ if(!strncmp(opts,"null",3)) {
+ flags.nonull = negated;
+ return;
+ }
+
+ if(!strncmp(opts,"tombstone",4)) {
+ flags.notombstone = negated;
+ return;
+ }
+
+ if(!strncmp(opts,"news",4)) {
+ flags.nonews = negated;
+ return;
+ }
+
+ if(!strncmp(opts,"time",4)) {
+ flags.time = !negated;
+ flags.botl = 1;
+ return;
+ }
+
+ if(!strncmp(opts,"restonspace",4)) {
+ flags.no_rest_on_space = negated;
+ return;
+ }
+
+ if(!strncmp(opts,"fixinv",4)) {
+ if(from_env)
+ flags.invlet_constant = !negated;
+ else
+ pline("The fixinvlet option must be in HACKOPTIONS.");
+ return;
+ }
+
+ if(!strncmp(opts,"male",4)) {
+ flags.female = negated;
+ return;
+ }
+ if(!strncmp(opts,"female",6)) {
+ flags.female = !negated;
+ return;
+ }
+
+ /* name:string */
+ if(!strncmp(opts,"name",4)) {
+ extern char plname[PL_NSIZ];
+ if(!from_env) {
+ pline("The playername can be set only from HACKOPTIONS.");
+ return;
+ }
+ op = index(opts,':');
+ if(!op) goto bad;
+ (void) strncpy(plname, op+1, sizeof(plname)-1);
+ return;
+ }
+
+ /* endgame:5t[op] 5a[round] o[wn] */
+ if(!strncmp(opts,"endgame",3)) {
+ op = index(opts,':');
+ if(!op) goto bad;
+ op++;
+ while(*op) {
+ num = 1;
+ if(digit(*op)) {
+ num = atoi(op);
+ while(digit(*op)) op++;
+ } else
+ if(*op == '!') {
+ negated = !negated;
+ op++;
+ }
+ switch(*op) {
+ case 't':
+ flags.end_top = num;
+ break;
+ case 'a':
+ flags.end_around = num;
+ break;
+ case 'o':
+ flags.end_own = !negated;
+ break;
+ default:
+ goto bad;
+ }
+ while(letter(*++op)) ;
+ if(*op == '/') op++;
+ }
+ return;
+ }
+bad:
+ if(!from_env) {
+ if(!strncmp(opts, "help", 4)) {
+ pline("%s%s%s",
+"To set options use `HACKOPTIONS=\"<options>\"' in your environment, or ",
+"give the command 'o' followed by the line `<options>' while playing. ",
+"Here <options> is a list of <option>s separated by commas." );
+ pline("%s%s%s",
+"Simple (boolean) options are rest_on_space, news, time, ",
+"null, tombstone, (fe)male. ",
+"These can be negated by prefixing them with '!' or \"no\"." );
+ pline("%s",
+"A string option is name, as in HACKOPTIONS=\"name:Merlin-W\"." );
+ pline("%s%s%s",
+"A compound option is endgame; it is followed by a description of what ",
+"parts of the scorelist you want to see. You might for example say: ",
+"`endgame:own scores/5 top scores/4 around my score'." );
+ return;
+ }
+ pline("Bad option: %s.", opts);
+ pline("Type `o help<cr>' for help.");
+ return;
+ }
+ puts("Bad syntax in HACKOPTIONS.");
+ puts("Use for example:");
+ puts(
+"HACKOPTIONS=\"!restonspace,notombstone,endgame:own/5 topscorers/4 around me\""
+ );
+ getret();
+}
+
+doset()
+{
+ char buf[BUFSZ];
+
+ pline("What options do you want to set? ");
+ getlin(buf);
+ if(!buf[0] || buf[0] == '\033') {
+ (void) strcpy(buf,"HACKOPTIONS=");
+ (void) strcat(buf, flags.female ? "female," : "male,");
+ if(flags.standout) (void) strcat(buf,"standout,");
+ if(flags.nonull) (void) strcat(buf,"nonull,");
+ if(flags.nonews) (void) strcat(buf,"nonews,");
+ if(flags.time) (void) strcat(buf,"time,");
+ if(flags.notombstone) (void) strcat(buf,"notombstone,");
+ if(flags.no_rest_on_space)
+ (void) strcat(buf,"!rest_on_space,");
+ if(flags.end_top != 5 || flags.end_around != 4 || flags.end_own){
+ (void) sprintf(eos(buf), "endgame: %u topscores/%u around me",
+ flags.end_top, flags.end_around);
+ if(flags.end_own) (void) strcat(buf, "/own scores");
+ } else {
+ register char *eop = eos(buf);
+ if(*--eop == ',') *eop = 0;
+ }
+ pline(buf);
+ } else
+ parseoptions(buf, FALSE);
+
+ return(0);
+}
diff --git a/games/hack/hack.pager.c b/games/hack/hack.pager.c
new file mode 100644
index 000000000000..b1edb9164e8c
--- /dev/null
+++ b/games/hack/hack.pager.c
@@ -0,0 +1,406 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.pager.c - version 1.0.3 */
+
+/* This file contains the command routine dowhatis() and a pager. */
+/* Also readmail() and doshell(), and generally the things that
+ contact the outside world. */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <stdio.h>
+#include "hack.h"
+extern int CO, LI; /* usually COLNO and ROWNO+2 */
+extern char *CD;
+extern char quitchars[];
+extern char *getenv(), *getlogin();
+void done1();
+
+dowhatis()
+{
+ FILE *fp;
+ char bufr[BUFSZ+6];
+ register char *buf = &bufr[6], *ep, q;
+ extern char readchar();
+
+ if(!(fp = fopen(DATAFILE, "r")))
+ pline("Cannot open data file!");
+ else {
+ pline("Specify what? ");
+ q = readchar();
+ if(q != '\t')
+ while(fgets(buf,BUFSZ,fp))
+ if(*buf == q) {
+ ep = index(buf, '\n');
+ if(ep) *ep = 0;
+ /* else: bad data file */
+ /* Expand tab 'by hand' */
+ if(buf[1] == '\t'){
+ buf = bufr;
+ buf[0] = q;
+ (void) strncpy(buf+1, " ", 7);
+ }
+ pline(buf);
+ if(ep[-1] == ';') {
+ pline("More info? ");
+ if(readchar() == 'y') {
+ page_more(fp,1); /* does fclose() */
+ return(0);
+ }
+ }
+ (void) fclose(fp); /* kopper@psuvax1 */
+ return(0);
+ }
+ pline("I've never heard of such things.");
+ (void) fclose(fp);
+ }
+ return(0);
+}
+
+/* make the paging of a file interruptible */
+static int got_intrup;
+
+void
+intruph(){
+ got_intrup++;
+}
+
+/* simple pager, also used from dohelp() */
+page_more(fp,strip)
+FILE *fp;
+int strip; /* nr of chars to be stripped from each line (0 or 1) */
+{
+ register char *bufr, *ep;
+ sig_t prevsig = signal(SIGINT, intruph);
+
+ set_pager(0);
+ bufr = (char *) alloc((unsigned) CO);
+ bufr[CO-1] = 0;
+ while(fgets(bufr,CO-1,fp) && (!strip || *bufr == '\t') && !got_intrup){
+ ep = index(bufr, '\n');
+ if(ep)
+ *ep = 0;
+ if(page_line(bufr+strip)) {
+ set_pager(2);
+ goto ret;
+ }
+ }
+ set_pager(1);
+ret:
+ free(bufr);
+ (void) fclose(fp);
+ (void) signal(SIGINT, prevsig);
+ got_intrup = 0;
+}
+
+static boolean whole_screen = TRUE;
+#define PAGMIN 12 /* minimum # of lines for page below level map */
+
+set_whole_screen() { /* called in termcap as soon as LI is known */
+ whole_screen = (LI-ROWNO-2 <= PAGMIN || !CD);
+}
+
+#ifdef NEWS
+readnews() {
+ register int ret;
+
+ whole_screen = TRUE; /* force a docrt(), our first */
+ ret = page_file(NEWS, TRUE);
+ set_whole_screen();
+ return(ret); /* report whether we did docrt() */
+}
+#endif NEWS
+
+set_pager(mode)
+register int mode; /* 0: open 1: wait+close 2: close */
+{
+ static boolean so;
+ if(mode == 0) {
+ if(!whole_screen) {
+ /* clear topline */
+ clrlin();
+ /* use part of screen below level map */
+ curs(1, ROWNO+4);
+ } else {
+ cls();
+ }
+ so = flags.standout;
+ flags.standout = 1;
+ } else {
+ if(mode == 1) {
+ curs(1, LI);
+ more();
+ }
+ flags.standout = so;
+ if(whole_screen)
+ docrt();
+ else {
+ curs(1, ROWNO+4);
+ cl_eos();
+ }
+ }
+}
+
+page_line(s) /* returns 1 if we should quit */
+register char *s;
+{
+ extern char morc;
+
+ if(cury == LI-1) {
+ if(!*s)
+ return(0); /* suppress blank lines at top */
+ putchar('\n');
+ cury++;
+ cmore("q\033");
+ if(morc) {
+ morc = 0;
+ return(1);
+ }
+ if(whole_screen)
+ cls();
+ else {
+ curs(1, ROWNO+4);
+ cl_eos();
+ }
+ }
+ puts(s);
+ cury++;
+ return(0);
+}
+
+/*
+ * Flexible pager: feed it with a number of lines and it will decide
+ * whether these should be fed to the pager above, or displayed in a
+ * corner.
+ * Call:
+ * cornline(0, title or 0) : initialize
+ * cornline(1, text) : add text to the chain of texts
+ * cornline(2, morcs) : output everything and cleanup
+ * cornline(3, 0) : cleanup
+ */
+
+cornline(mode, text)
+int mode;
+char *text;
+{
+ static struct line {
+ struct line *next_line;
+ char *line_text;
+ } *texthead, *texttail;
+ static int maxlen;
+ static int linect;
+ register struct line *tl;
+
+ if(mode == 0) {
+ texthead = 0;
+ maxlen = 0;
+ linect = 0;
+ if(text) {
+ cornline(1, text); /* title */
+ cornline(1, ""); /* blank line */
+ }
+ return;
+ }
+
+ if(mode == 1) {
+ register int len;
+
+ if(!text) return; /* superfluous, just to be sure */
+ linect++;
+ len = strlen(text);
+ if(len > maxlen)
+ maxlen = len;
+ tl = (struct line *)
+ alloc((unsigned)(len + sizeof(struct line) + 1));
+ tl->next_line = 0;
+ tl->line_text = (char *)(tl + 1);
+ (void) strcpy(tl->line_text, text);
+ if(!texthead)
+ texthead = tl;
+ else
+ texttail->next_line = tl;
+ texttail = tl;
+ return;
+ }
+
+ /* --- now we really do it --- */
+ if(mode == 2 && linect == 1) /* topline only */
+ pline(texthead->line_text);
+ else
+ if(mode == 2) {
+ register int curline, lth;
+
+ if(flags.toplin == 1) more(); /* ab@unido */
+ remember_topl();
+
+ lth = CO - maxlen - 2; /* Use full screen width */
+ if (linect < LI && lth >= 10) { /* in a corner */
+ home ();
+ cl_end ();
+ flags.toplin = 0;
+ curline = 1;
+ for (tl = texthead; tl; tl = tl->next_line) {
+ curs (lth, curline);
+ if(curline > 1)
+ cl_end ();
+ putsym(' ');
+ putstr (tl->line_text);
+ curline++;
+ }
+ curs (lth, curline);
+ cl_end ();
+ cmore (text);
+ home ();
+ cl_end ();
+ docorner (lth, curline-1);
+ } else { /* feed to pager */
+ set_pager(0);
+ for (tl = texthead; tl; tl = tl->next_line) {
+ if (page_line (tl->line_text)) {
+ set_pager(2);
+ goto cleanup;
+ }
+ }
+ if(text) {
+ cgetret(text);
+ set_pager(2);
+ } else
+ set_pager(1);
+ }
+ }
+
+cleanup:
+ while(tl = texthead) {
+ texthead = tl->next_line;
+ free((char *) tl);
+ }
+}
+
+dohelp()
+{
+ char c;
+
+ pline ("Long or short help? ");
+ while (((c = readchar ()) != 'l') && (c != 's') && !index(quitchars,c))
+ bell ();
+ if (!index(quitchars, c))
+ (void) page_file((c == 'l') ? HELP : SHELP, FALSE);
+ return(0);
+}
+
+page_file(fnam, silent) /* return: 0 - cannot open fnam; 1 - otherwise */
+register char *fnam;
+boolean silent;
+{
+#ifdef DEF_PAGER /* this implies that UNIX is defined */
+ {
+ /* use external pager; this may give security problems */
+
+ register int fd = open(fnam, 0);
+
+ if(fd < 0) {
+ if(!silent) pline("Cannot open %s.", fnam);
+ return(0);
+ }
+ if(child(1)){
+ extern char *catmore;
+
+ /* Now that child() does a setuid(getuid()) and a chdir(),
+ we may not be able to open file fnam anymore, so make
+ it stdin. */
+ (void) close(0);
+ if(dup(fd)) {
+ if(!silent) printf("Cannot open %s as stdin.\n", fnam);
+ } else {
+ execl(catmore, "page", (char *) 0);
+ if(!silent) printf("Cannot exec %s.\n", catmore);
+ }
+ exit(1);
+ }
+ (void) close(fd);
+ }
+#else DEF_PAGER
+ {
+ FILE *f; /* free after Robert Viduya */
+
+ if ((f = fopen (fnam, "r")) == (FILE *) 0) {
+ if(!silent) {
+ home(); perror (fnam); flags.toplin = 1;
+ pline ("Cannot open %s.", fnam);
+ }
+ return(0);
+ }
+ page_more(f, 0);
+ }
+#endif DEF_PAGER
+
+ return(1);
+}
+
+#ifdef UNIX
+#ifdef SHELL
+dosh(){
+register char *str;
+ if(child(0)) {
+ if(str = getenv("SHELL"))
+ execl(str, str, (char *) 0);
+ else
+ execl("/bin/sh", "sh", (char *) 0);
+ pline("sh: cannot execute.");
+ exit(1);
+ }
+ return(0);
+}
+#endif SHELL
+
+#ifdef NOWAITINCLUDE
+union wait { /* used only for the cast (union wait *) 0 */
+ int w_status;
+ struct {
+ unsigned short w_Termsig:7;
+ unsigned short w_Coredump:1;
+ unsigned short w_Retcode:8;
+ } w_T;
+};
+
+#else
+
+#ifdef BSD
+#include <sys/wait.h>
+#else
+#include <wait.h>
+#endif BSD
+#endif NOWAITINCLUDE
+
+child(wt) {
+ int status;
+ register int f;
+
+ f = fork();
+ if(f == 0){ /* child */
+ settty((char *) 0); /* also calls end_screen() */
+ (void) setuid(getuid());
+ (void) setgid(getgid());
+#ifdef CHDIR
+ (void) chdir(getenv("HOME"));
+#endif CHDIR
+ return(1);
+ }
+ if(f == -1) { /* cannot fork */
+ pline("Fork failed. Try again.");
+ return(0);
+ }
+ /* fork succeeded; wait for child to exit */
+ (void) signal(SIGINT,SIG_IGN);
+ (void) signal(SIGQUIT,SIG_IGN);
+ (void) wait(&status);
+ gettty();
+ setftty();
+ (void) signal(SIGINT,done1);
+#ifdef WIZARD
+ if(wizard) (void) signal(SIGQUIT,SIG_DFL);
+#endif WIZARD
+ if(wt) getret();
+ docrt();
+ return(0);
+}
+#endif UNIX
diff --git a/games/hack/hack.potion.c b/games/hack/hack.potion.c
new file mode 100644
index 000000000000..c860299eb5bb
--- /dev/null
+++ b/games/hack/hack.potion.c
@@ -0,0 +1,386 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.potion.c - version 1.0.3 */
+
+#include "hack.h"
+extern int float_down();
+extern char *nomovemsg;
+extern struct monst youmonst;
+extern struct monst *makemon();
+
+dodrink() {
+ register struct obj *otmp,*objs;
+ register struct monst *mtmp;
+ register int unkn = 0, nothing = 0;
+
+ otmp = getobj("!", "drink");
+ if(!otmp) return(0);
+ if(!strcmp(objects[otmp->otyp].oc_descr, "smoky") && !rn2(13)) {
+ ghost_from_bottle();
+ goto use_it;
+ }
+ switch(otmp->otyp){
+ case POT_RESTORE_STRENGTH:
+ unkn++;
+ pline("Wow! This makes you feel great!");
+ if(u.ustr < u.ustrmax) {
+ u.ustr = u.ustrmax;
+ flags.botl = 1;
+ }
+ break;
+ case POT_BOOZE:
+ unkn++;
+ pline("Ooph! This tastes like liquid fire!");
+ Confusion += d(3,8);
+ /* the whiskey makes us feel better */
+ if(u.uhp < u.uhpmax) losehp(-1, "bottle of whiskey");
+ if(!rn2(4)) {
+ pline("You pass out.");
+ multi = -rnd(15);
+ nomovemsg = "You awake with a headache.";
+ }
+ break;
+ case POT_INVISIBILITY:
+ if(Invis || See_invisible)
+ nothing++;
+ else {
+ if(!Blind)
+ pline("Gee! All of a sudden, you can't see yourself.");
+ else
+ pline("You feel rather airy."), unkn++;
+ newsym(u.ux,u.uy);
+ }
+ Invis += rn1(15,31);
+ break;
+ case POT_FRUIT_JUICE:
+ pline("This tastes like fruit juice.");
+ lesshungry(20);
+ break;
+ case POT_HEALING:
+ pline("You begin to feel better.");
+ flags.botl = 1;
+ u.uhp += rnd(10);
+ if(u.uhp > u.uhpmax)
+ u.uhp = ++u.uhpmax;
+ if(Blind) Blind = 1; /* see on next move */
+ if(Sick) Sick = 0;
+ break;
+ case POT_PARALYSIS:
+ if(Levitation)
+ pline("You are motionlessly suspended.");
+ else
+ pline("Your feet are frozen to the floor!");
+ nomul(-(rn1(10,25)));
+ break;
+ case POT_MONSTER_DETECTION:
+ if(!fmon) {
+ strange_feeling(otmp, "You feel threatened.");
+ return(1);
+ } else {
+ cls();
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->mx > 0)
+ at(mtmp->mx,mtmp->my,mtmp->data->mlet);
+ prme();
+ pline("You sense the presence of monsters.");
+ more();
+ docrt();
+ }
+ break;
+ case POT_OBJECT_DETECTION:
+ if(!fobj) {
+ strange_feeling(otmp, "You feel a pull downward.");
+ return(1);
+ } else {
+ for(objs = fobj; objs; objs = objs->nobj)
+ if(objs->ox != u.ux || objs->oy != u.uy)
+ goto outobjmap;
+ pline("You sense the presence of objects close nearby.");
+ break;
+ outobjmap:
+ cls();
+ for(objs = fobj; objs; objs = objs->nobj)
+ at(objs->ox,objs->oy,objs->olet);
+ prme();
+ pline("You sense the presence of objects.");
+ more();
+ docrt();
+ }
+ break;
+ case POT_SICKNESS:
+ pline("Yech! This stuff tastes like poison.");
+ if(Poison_resistance)
+ pline("(But in fact it was biologically contaminated orange juice.)");
+ losestr(rn1(4,3));
+ losehp(rnd(10), "contaminated potion");
+ break;
+ case POT_CONFUSION:
+ if(!Confusion)
+ pline("Huh, What? Where am I?");
+ else
+ nothing++;
+ Confusion += rn1(7,16);
+ break;
+ case POT_GAIN_STRENGTH:
+ pline("Wow do you feel strong!");
+ if(u.ustr >= 118) break; /* > 118 is impossible */
+ if(u.ustr > 17) u.ustr += rnd(118-u.ustr);
+ else u.ustr++;
+ if(u.ustr > u.ustrmax) u.ustrmax = u.ustr;
+ flags.botl = 1;
+ break;
+ case POT_SPEED:
+ if(Wounded_legs) {
+ heal_legs();
+ unkn++;
+ break;
+ }
+ if(!(Fast & ~INTRINSIC))
+ pline("You are suddenly moving much faster.");
+ else
+ pline("Your legs get new energy."), unkn++;
+ Fast += rn1(10,100);
+ break;
+ case POT_BLINDNESS:
+ if(!Blind)
+ pline("A cloud of darkness falls upon you.");
+ else
+ nothing++;
+ Blind += rn1(100,250);
+ seeoff(0);
+ break;
+ case POT_GAIN_LEVEL:
+ pluslvl();
+ break;
+ case POT_EXTRA_HEALING:
+ pline("You feel much better.");
+ flags.botl = 1;
+ u.uhp += d(2,20)+1;
+ if(u.uhp > u.uhpmax)
+ u.uhp = (u.uhpmax += 2);
+ if(Blind) Blind = 1;
+ if(Sick) Sick = 0;
+ break;
+ case POT_LEVITATION:
+ if(!Levitation)
+ float_up();
+ else
+ nothing++;
+ Levitation += rnd(100);
+ u.uprops[PROP(RIN_LEVITATION)].p_tofn = float_down;
+ break;
+ default:
+ impossible("What a funny potion! (%u)", otmp->otyp);
+ return(0);
+ }
+ if(nothing) {
+ unkn++;
+ pline("You have a peculiar feeling for a moment, then it passes.");
+ }
+ if(otmp->dknown && !objects[otmp->otyp].oc_name_known) {
+ if(!unkn) {
+ objects[otmp->otyp].oc_name_known = 1;
+ more_experienced(0,10);
+ } else if(!objects[otmp->otyp].oc_uname)
+ docall(otmp);
+ }
+use_it:
+ useup(otmp);
+ return(1);
+}
+
+pluslvl()
+{
+ register num;
+
+ pline("You feel more experienced.");
+ num = rnd(10);
+ u.uhpmax += num;
+ u.uhp += num;
+ if(u.ulevel < 14) {
+ extern long newuexp();
+
+ u.uexp = newuexp()+1;
+ pline("Welcome to experience level %u.", ++u.ulevel);
+ }
+ flags.botl = 1;
+}
+
+strange_feeling(obj,txt)
+register struct obj *obj;
+register char *txt;
+{
+ if(flags.beginner)
+ pline("You have a strange feeling for a moment, then it passes.");
+ else
+ pline(txt);
+ if(!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname)
+ docall(obj);
+ useup(obj);
+}
+
+char *bottlenames[] = {
+ "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
+};
+
+potionhit(mon, obj)
+register struct monst *mon;
+register struct obj *obj;
+{
+ extern char *xname();
+ register char *botlnam = bottlenames[rn2(SIZE(bottlenames))];
+ boolean uclose, isyou = (mon == &youmonst);
+
+ if(isyou) {
+ uclose = TRUE;
+ pline("The %s crashes on your head and breaks into shivers.",
+ botlnam);
+ losehp(rnd(2), "thrown potion");
+ } else {
+ uclose = (dist(mon->mx,mon->my) < 3);
+ /* perhaps 'E' and 'a' have no head? */
+ pline("The %s crashes on %s's head and breaks into shivers.",
+ botlnam, monnam(mon));
+ if(rn2(5) && mon->mhp > 1)
+ mon->mhp--;
+ }
+ pline("The %s evaporates.", xname(obj));
+
+ if(!isyou && !rn2(3)) switch(obj->otyp) {
+
+ case POT_RESTORE_STRENGTH:
+ case POT_GAIN_STRENGTH:
+ case POT_HEALING:
+ case POT_EXTRA_HEALING:
+ if(mon->mhp < mon->mhpmax) {
+ mon->mhp = mon->mhpmax;
+ pline("%s looks sound and hale again!", Monnam(mon));
+ }
+ break;
+ case POT_SICKNESS:
+ if(mon->mhpmax > 3)
+ mon->mhpmax /= 2;
+ if(mon->mhp > 2)
+ mon->mhp /= 2;
+ break;
+ case POT_CONFUSION:
+ case POT_BOOZE:
+ mon->mconf = 1;
+ break;
+ case POT_INVISIBILITY:
+ unpmon(mon);
+ mon->minvis = 1;
+ pmon(mon);
+ break;
+ case POT_PARALYSIS:
+ mon->mfroz = 1;
+ break;
+ case POT_SPEED:
+ mon->mspeed = MFAST;
+ break;
+ case POT_BLINDNESS:
+ mon->mblinded |= 64 + rn2(64);
+ break;
+/*
+ case POT_GAIN_LEVEL:
+ case POT_LEVITATION:
+ case POT_FRUIT_JUICE:
+ case POT_MONSTER_DETECTION:
+ case POT_OBJECT_DETECTION:
+ break;
+*/
+ }
+ if(uclose && rn2(5))
+ potionbreathe(obj);
+ obfree(obj, Null(obj));
+}
+
+potionbreathe(obj)
+register struct obj *obj;
+{
+ switch(obj->otyp) {
+ case POT_RESTORE_STRENGTH:
+ case POT_GAIN_STRENGTH:
+ if(u.ustr < u.ustrmax) u.ustr++, flags.botl = 1;
+ break;
+ case POT_HEALING:
+ case POT_EXTRA_HEALING:
+ if(u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
+ break;
+ case POT_SICKNESS:
+ if(u.uhp <= 5) u.uhp = 1; else u.uhp -= 5;
+ flags.botl = 1;
+ break;
+ case POT_CONFUSION:
+ case POT_BOOZE:
+ if(!Confusion)
+ pline("You feel somewhat dizzy.");
+ Confusion += rnd(5);
+ break;
+ case POT_INVISIBILITY:
+ pline("For an instant you couldn't see your right hand.");
+ break;
+ case POT_PARALYSIS:
+ pline("Something seems to be holding you.");
+ nomul(-rnd(5));
+ break;
+ case POT_SPEED:
+ Fast += rnd(5);
+ pline("Your knees seem more flexible now.");
+ break;
+ case POT_BLINDNESS:
+ if(!Blind) pline("It suddenly gets dark.");
+ Blind += rnd(5);
+ seeoff(0);
+ break;
+/*
+ case POT_GAIN_LEVEL:
+ case POT_LEVITATION:
+ case POT_FRUIT_JUICE:
+ case POT_MONSTER_DETECTION:
+ case POT_OBJECT_DETECTION:
+ break;
+*/
+ }
+ /* note: no obfree() */
+}
+
+/*
+ * -- rudimentary -- to do this correctly requires much more work
+ * -- all sharp weapons get one or more qualities derived from the potions
+ * -- texts on scrolls may be (partially) wiped out; do they become blank?
+ * -- or does their effect change, like under Confusion?
+ * -- all objects may be made invisible by POT_INVISIBILITY
+ * -- If the flask is small, can one dip a large object? Does it magically
+ * -- become a jug? Etc.
+ */
+dodip(){
+ register struct obj *potion, *obj;
+
+ if(!(obj = getobj("#", "dip")))
+ return(0);
+ if(!(potion = getobj("!", "dip into")))
+ return(0);
+ pline("Interesting...");
+ if(obj->otyp == ARROW || obj->otyp == DART ||
+ obj->otyp == CROSSBOW_BOLT) {
+ if(potion->otyp == POT_SICKNESS) {
+ useup(potion);
+ if(obj->spe < 7) obj->spe++; /* %% */
+ }
+ }
+ return(1);
+}
+
+ghost_from_bottle(){
+ extern struct permonst pm_ghost;
+ register struct monst *mtmp;
+
+ if(!(mtmp = makemon(PM_GHOST,u.ux,u.uy))){
+ pline("This bottle turns out to be empty.");
+ return;
+ }
+ mnexto(mtmp);
+ pline("As you open the bottle, an enormous ghost emerges!");
+ pline("You are frightened to death, and unable to move.");
+ nomul(-3);
+}
diff --git a/games/hack/hack.pri.c b/games/hack/hack.pri.c
new file mode 100644
index 000000000000..13ee6b7d0b15
--- /dev/null
+++ b/games/hack/hack.pri.c
@@ -0,0 +1,660 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.pri.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+xchar scrlx, scrhx, scrly, scrhy; /* corners of new area on screen */
+
+extern char *hu_stat[]; /* in eat.c */
+extern char *CD;
+
+swallowed()
+{
+ char *ulook = "|@|";
+ ulook[1] = u.usym;
+
+ cls();
+ curs(u.ux-1, u.uy+1);
+ fputs("/-\\", stdout);
+ curx = u.ux+2;
+ curs(u.ux-1, u.uy+2);
+ fputs(ulook, stdout);
+ curx = u.ux+2;
+ curs(u.ux-1, u.uy+3);
+ fputs("\\-/", stdout);
+ curx = u.ux+2;
+ u.udispl = 1;
+ u.udisx = u.ux;
+ u.udisy = u.uy;
+}
+
+
+/*VARARGS1*/
+boolean panicking;
+
+panic(str,a1,a2,a3,a4,a5,a6)
+char *str;
+{
+ if(panicking++) exit(1); /* avoid loops - this should never happen*/
+ home();
+ puts(" Suddenly, the dungeon collapses.");
+ fputs(" ERROR: ", stdout);
+ printf(str,a1,a2,a3,a4,a5,a6);
+#ifdef DEBUG
+#ifdef UNIX
+ if(!fork())
+ abort(); /* generate core dump */
+#endif UNIX
+#endif DEBUG
+ more(); /* contains a fflush() */
+ done("panicked");
+}
+
+atl(x,y,ch)
+register x,y;
+{
+ register struct rm *crm = &levl[x][y];
+
+ if(x<0 || x>COLNO-1 || y<0 || y>ROWNO-1){
+ impossible("atl(%d,%d,%c)",x,y,ch);
+ return;
+ }
+ if(crm->seen && crm->scrsym == ch) return;
+ crm->scrsym = ch;
+ crm->new = 1;
+ on_scr(x,y);
+}
+
+on_scr(x,y)
+register x,y;
+{
+ if(x < scrlx) scrlx = x;
+ if(x > scrhx) scrhx = x;
+ if(y < scrly) scrly = y;
+ if(y > scrhy) scrhy = y;
+}
+
+/* call: (x,y) - display
+ (-1,0) - close (leave last symbol)
+ (-1,-1)- close (undo last symbol)
+ (-1,let)-open: initialize symbol
+ (-2,let)-change let
+*/
+
+tmp_at(x,y) schar x,y; {
+static schar prevx, prevy;
+static char let;
+ if((int)x == -2){ /* change let call */
+ let = y;
+ return;
+ }
+ if((int)x == -1 && (int)y >= 0){ /* open or close call */
+ let = y;
+ prevx = -1;
+ return;
+ }
+ if(prevx >= 0 && cansee(prevx,prevy)) {
+ delay_output();
+ prl(prevx, prevy); /* in case there was a monster */
+ at(prevx, prevy, levl[prevx][prevy].scrsym);
+ }
+ if(x >= 0){ /* normal call */
+ if(cansee(x,y)) at(x,y,let);
+ prevx = x;
+ prevy = y;
+ } else { /* close call */
+ let = 0;
+ prevx = -1;
+ }
+}
+
+/* like the previous, but the symbols are first erased on completion */
+Tmp_at(x,y) schar x,y; {
+static char let;
+static xchar cnt;
+static coord tc[COLNO]; /* but watch reflecting beams! */
+register xx,yy;
+ if((int)x == -1) {
+ if(y > 0) { /* open call */
+ let = y;
+ cnt = 0;
+ return;
+ }
+ /* close call (do not distinguish y==0 and y==-1) */
+ while(cnt--) {
+ xx = tc[cnt].x;
+ yy = tc[cnt].y;
+ prl(xx, yy);
+ at(xx, yy, levl[xx][yy].scrsym);
+ }
+ cnt = let = 0; /* superfluous */
+ return;
+ }
+ if((int)x == -2) { /* change let call */
+ let = y;
+ return;
+ }
+ /* normal call */
+ if(cansee(x,y)) {
+ if(cnt) delay_output();
+ at(x,y,let);
+ tc[cnt].x = x;
+ tc[cnt].y = y;
+ if(++cnt >= COLNO) panic("Tmp_at overflow?");
+ levl[x][y].new = 0; /* prevent pline-nscr erasing --- */
+ }
+}
+
+setclipped(){
+ error("Hack needs a screen of size at least %d by %d.\n",
+ ROWNO+2, COLNO);
+}
+
+at(x,y,ch)
+register xchar x,y;
+char ch;
+{
+#ifndef lint
+ /* if xchar is unsigned, lint will complain about if(x < 0) */
+ if(x < 0 || x > COLNO-1 || y < 0 || y > ROWNO-1) {
+ impossible("At gets 0%o at %d %d.", ch, x, y);
+ return;
+ }
+#endif lint
+ if(!ch) {
+ impossible("At gets null at %d %d.", x, y);
+ return;
+ }
+ y += 2;
+ curs(x,y);
+ (void) putchar(ch);
+ curx++;
+}
+
+prme(){
+ if(!Invisible) at(u.ux,u.uy,u.usym);
+}
+
+doredraw()
+{
+ docrt();
+ return(0);
+}
+
+docrt()
+{
+ register x,y;
+ register struct rm *room;
+ register struct monst *mtmp;
+
+ if(u.uswallow) {
+ swallowed();
+ return;
+ }
+ cls();
+
+/* Some ridiculous code to get display of @ and monsters (almost) right */
+ if(!Invisible) {
+ levl[(u.udisx = u.ux)][(u.udisy = u.uy)].scrsym = u.usym;
+ levl[u.udisx][u.udisy].seen = 1;
+ u.udispl = 1;
+ } else u.udispl = 0;
+
+ seemons(); /* reset old positions */
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ mtmp->mdispl = 0;
+ seemons(); /* force new positions to be shown */
+/* This nonsense should disappear soon --------------------------------- */
+
+ for(y = 0; y < ROWNO; y++)
+ for(x = 0; x < COLNO; x++)
+ if((room = &levl[x][y])->new) {
+ room->new = 0;
+ at(x,y,room->scrsym);
+ } else if(room->seen)
+ at(x,y,room->scrsym);
+ scrlx = COLNO;
+ scrly = ROWNO;
+ scrhx = scrhy = 0;
+ flags.botlx = 1;
+ bot();
+}
+
+docorner(xmin,ymax) register xmin,ymax; {
+ register x,y;
+ register struct rm *room;
+ register struct monst *mtmp;
+
+ if(u.uswallow) { /* Can be done more efficiently */
+ swallowed();
+ return;
+ }
+
+ seemons(); /* reset old positions */
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->mx >= xmin && mtmp->my < ymax)
+ mtmp->mdispl = 0;
+ seemons(); /* force new positions to be shown */
+
+ for(y = 0; y < ymax; y++) {
+ if(y > ROWNO && CD) break;
+ curs(xmin,y+2);
+ cl_end();
+ if(y < ROWNO) {
+ for(x = xmin; x < COLNO; x++) {
+ if((room = &levl[x][y])->new) {
+ room->new = 0;
+ at(x,y,room->scrsym);
+ } else
+ if(room->seen)
+ at(x,y,room->scrsym);
+ }
+ }
+ }
+ if(ymax > ROWNO) {
+ cornbot(xmin-1);
+ if(ymax > ROWNO+1 && CD) {
+ curs(1,ROWNO+3);
+ cl_eos();
+ }
+ }
+}
+
+curs_on_u(){
+ curs(u.ux, u.uy+2);
+}
+
+pru()
+{
+ if(u.udispl && (Invisible || u.udisx != u.ux || u.udisy != u.uy))
+ /* if(! levl[u.udisx][u.udisy].new) */
+ if(!vism_at(u.udisx, u.udisy))
+ newsym(u.udisx, u.udisy);
+ if(Invisible) {
+ u.udispl = 0;
+ prl(u.ux,u.uy);
+ } else
+ if(!u.udispl || u.udisx != u.ux || u.udisy != u.uy) {
+ atl(u.ux, u.uy, u.usym);
+ u.udispl = 1;
+ u.udisx = u.ux;
+ u.udisy = u.uy;
+ }
+ levl[u.ux][u.uy].seen = 1;
+}
+
+#ifndef NOWORM
+#include "def.wseg.h"
+extern struct wseg *m_atseg;
+#endif NOWORM
+
+/* print a position that is visible for @ */
+prl(x,y)
+{
+ register struct rm *room;
+ register struct monst *mtmp;
+ register struct obj *otmp;
+
+ if(x == u.ux && y == u.uy && (!Invisible)) {
+ pru();
+ return;
+ }
+ if(!isok(x,y)) return;
+ room = &levl[x][y];
+ if((!room->typ) ||
+ (IS_ROCK(room->typ) && levl[u.ux][u.uy].typ == CORR))
+ return;
+ if((mtmp = m_at(x,y)) && !mtmp->mhide &&
+ (!mtmp->minvis || See_invisible)) {
+#ifndef NOWORM
+ if(m_atseg)
+ pwseg(m_atseg);
+ else
+#endif NOWORM
+ pmon(mtmp);
+ }
+ else if((otmp = o_at(x,y)) && room->typ != POOL)
+ atl(x,y,otmp->olet);
+ else if(mtmp && (!mtmp->minvis || See_invisible)) {
+ /* must be a hiding monster, but not hiding right now */
+ /* assume for the moment that long worms do not hide */
+ pmon(mtmp);
+ }
+ else if(g_at(x,y) && room->typ != POOL)
+ atl(x,y,'$');
+ else if(!room->seen || room->scrsym == ' ') {
+ room->new = room->seen = 1;
+ newsym(x,y);
+ on_scr(x,y);
+ }
+ room->seen = 1;
+}
+
+char
+news0(x,y)
+register xchar x,y;
+{
+ register struct obj *otmp;
+ register struct trap *ttmp;
+ struct rm *room;
+ register char tmp;
+
+ room = &levl[x][y];
+ if(!room->seen) tmp = ' ';
+ else if(room->typ == POOL) tmp = POOL_SYM;
+ else if(!Blind && (otmp = o_at(x,y))) tmp = otmp->olet;
+ else if(!Blind && g_at(x,y)) tmp = '$';
+ else if(x == xupstair && y == yupstair) tmp = '<';
+ else if(x == xdnstair && y == ydnstair) tmp = '>';
+ else if((ttmp = t_at(x,y)) && ttmp->tseen) tmp = '^';
+ else switch(room->typ) {
+ case SCORR:
+ case SDOOR:
+ tmp = room->scrsym; /* %% wrong after killing mimic ! */
+ break;
+ case HWALL:
+ tmp = '-';
+ break;
+ case VWALL:
+ tmp = '|';
+ break;
+ case LDOOR:
+ case DOOR:
+ tmp = '+';
+ break;
+ case CORR:
+ tmp = CORR_SYM;
+ break;
+ case ROOM:
+ if(room->lit || cansee(x,y) || Blind) tmp = '.';
+ else tmp = ' ';
+ break;
+/*
+ case POOL:
+ tmp = POOL_SYM;
+ break;
+*/
+ default:
+ tmp = ERRCHAR;
+ }
+ return(tmp);
+}
+
+newsym(x,y)
+register x,y;
+{
+ atl(x,y,news0(x,y));
+}
+
+/* used with wand of digging (or pick-axe): fill scrsym and force display */
+/* also when a POOL evaporates */
+mnewsym(x,y)
+register x,y;
+{
+ register struct rm *room;
+ char newscrsym;
+
+ if(!vism_at(x,y)) {
+ room = &levl[x][y];
+ newscrsym = news0(x,y);
+ if(room->scrsym != newscrsym) {
+ room->scrsym = newscrsym;
+ room->seen = 0;
+ }
+ }
+}
+
+nosee(x,y)
+register x,y;
+{
+ register struct rm *room;
+
+ if(!isok(x,y)) return;
+ room = &levl[x][y];
+ if(room->scrsym == '.' && !room->lit && !Blind) {
+ room->scrsym = ' ';
+ room->new = 1;
+ on_scr(x,y);
+ }
+}
+
+#ifndef QUEST
+prl1(x,y)
+register x,y;
+{
+ if(u.dx) {
+ if(u.dy) {
+ prl(x-(2*u.dx),y);
+ prl(x-u.dx,y);
+ prl(x,y);
+ prl(x,y-u.dy);
+ prl(x,y-(2*u.dy));
+ } else {
+ prl(x,y-1);
+ prl(x,y);
+ prl(x,y+1);
+ }
+ } else {
+ prl(x-1,y);
+ prl(x,y);
+ prl(x+1,y);
+ }
+}
+
+nose1(x,y)
+register x,y;
+{
+ if(u.dx) {
+ if(u.dy) {
+ nosee(x,u.uy);
+ nosee(x,u.uy-u.dy);
+ nosee(x,y);
+ nosee(u.ux-u.dx,y);
+ nosee(u.ux,y);
+ } else {
+ nosee(x,y-1);
+ nosee(x,y);
+ nosee(x,y+1);
+ }
+ } else {
+ nosee(x-1,y);
+ nosee(x,y);
+ nosee(x+1,y);
+ }
+}
+#endif QUEST
+
+vism_at(x,y)
+register x,y;
+{
+ register struct monst *mtmp;
+
+ return((x == u.ux && y == u.uy && !Invisible)
+ ? 1 :
+ (mtmp = m_at(x,y))
+ ? ((Blind && Telepat) || canseemon(mtmp)) :
+ 0);
+}
+
+#ifdef NEWSCR
+pobj(obj) register struct obj *obj; {
+register int show = (!obj->oinvis || See_invisible) &&
+ cansee(obj->ox,obj->oy);
+ if(obj->odispl){
+ if(obj->odx != obj->ox || obj->ody != obj->oy || !show)
+ if(!vism_at(obj->odx,obj->ody)){
+ newsym(obj->odx, obj->ody);
+ obj->odispl = 0;
+ }
+ }
+ if(show && !vism_at(obj->ox,obj->oy)){
+ atl(obj->ox,obj->oy,obj->olet);
+ obj->odispl = 1;
+ obj->odx = obj->ox;
+ obj->ody = obj->oy;
+ }
+}
+#endif NEWSCR
+
+unpobj(obj) register struct obj *obj; {
+/* if(obj->odispl){
+ if(!vism_at(obj->odx, obj->ody))
+ newsym(obj->odx, obj->ody);
+ obj->odispl = 0;
+ }
+*/
+ if(!vism_at(obj->ox,obj->oy))
+ newsym(obj->ox,obj->oy);
+}
+
+seeobjs(){
+register struct obj *obj, *obj2;
+ for(obj = fobj; obj; obj = obj2) {
+ obj2 = obj->nobj;
+ if(obj->olet == FOOD_SYM && obj->otyp >= CORPSE
+ && obj->age + 250 < moves)
+ delobj(obj);
+ }
+ for(obj = invent; obj; obj = obj2) {
+ obj2 = obj->nobj;
+ if(obj->olet == FOOD_SYM && obj->otyp >= CORPSE
+ && obj->age + 250 < moves)
+ useup(obj);
+ }
+}
+
+seemons(){
+register struct monst *mtmp;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon){
+ if(mtmp->data->mlet == ';')
+ mtmp->minvis = (u.ustuck != mtmp &&
+ levl[mtmp->mx][mtmp->my].typ == POOL);
+ pmon(mtmp);
+#ifndef NOWORM
+ if(mtmp->wormno) wormsee(mtmp->wormno);
+#endif NOWORM
+ }
+}
+
+pmon(mon) register struct monst *mon; {
+register int show = (Blind && Telepat) || canseemon(mon);
+ if(mon->mdispl){
+ if(mon->mdx != mon->mx || mon->mdy != mon->my || !show)
+ unpmon(mon);
+ }
+ if(show && !mon->mdispl){
+ atl(mon->mx,mon->my,
+ (!mon->mappearance
+ || u.uprops[PROP(RIN_PROTECTION_FROM_SHAPE_CHANGERS)].p_flgs
+ ) ? mon->data->mlet : mon->mappearance);
+ mon->mdispl = 1;
+ mon->mdx = mon->mx;
+ mon->mdy = mon->my;
+ }
+}
+
+unpmon(mon) register struct monst *mon; {
+ if(mon->mdispl){
+ newsym(mon->mdx, mon->mdy);
+ mon->mdispl = 0;
+ }
+}
+
+nscr()
+{
+ register x,y;
+ register struct rm *room;
+
+ if(u.uswallow || u.ux == FAR || flags.nscrinh) return;
+ pru();
+ for(y = scrly; y <= scrhy; y++)
+ for(x = scrlx; x <= scrhx; x++)
+ if((room = &levl[x][y])->new) {
+ room->new = 0;
+ at(x,y,room->scrsym);
+ }
+ scrhx = scrhy = 0;
+ scrlx = COLNO;
+ scrly = ROWNO;
+}
+
+/* 100 suffices for bot(); no relation with COLNO */
+char oldbot[100], newbot[100];
+cornbot(lth)
+register int lth;
+{
+ if(lth < sizeof(oldbot)) {
+ oldbot[lth] = 0;
+ flags.botl = 1;
+ }
+}
+
+bot()
+{
+register char *ob = oldbot, *nb = newbot;
+register int i;
+extern char *eos();
+ if(flags.botlx) *ob = 0;
+ flags.botl = flags.botlx = 0;
+#ifdef GOLD_ON_BOTL
+ (void) sprintf(newbot,
+ "Level %-2d Gold %-5lu Hp %3d(%d) Ac %-2d Str ",
+ dlevel, u.ugold, u.uhp, u.uhpmax, u.uac);
+#else
+ (void) sprintf(newbot,
+ "Level %-2d Hp %3d(%d) Ac %-2d Str ",
+ dlevel, u.uhp, u.uhpmax, u.uac);
+#endif GOLD_ON_BOTL
+ if(u.ustr>18) {
+ if(u.ustr>117)
+ (void) strcat(newbot,"18/**");
+ else
+ (void) sprintf(eos(newbot), "18/%02d",u.ustr-18);
+ } else
+ (void) sprintf(eos(newbot), "%-2d ",u.ustr);
+#ifdef EXP_ON_BOTL
+ (void) sprintf(eos(newbot), " Exp %2d/%-5lu ", u.ulevel,u.uexp);
+#else
+ (void) sprintf(eos(newbot), " Exp %2u ", u.ulevel);
+#endif EXP_ON_BOTL
+ (void) strcat(newbot, hu_stat[u.uhs]);
+ if(flags.time)
+ (void) sprintf(eos(newbot), " %ld", moves);
+ if(strlen(newbot) >= COLNO) {
+ register char *bp0, *bp1;
+ bp0 = bp1 = newbot;
+ do {
+ if(*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ')
+ *bp1++ = *bp0;
+ } while(*bp0++);
+ }
+ for(i = 1; i<COLNO; i++) {
+ if(*ob != *nb){
+ curs(i,ROWNO+2);
+ (void) putchar(*nb ? *nb : ' ');
+ curx++;
+ }
+ if(*ob) ob++;
+ if(*nb) nb++;
+ }
+ (void) strcpy(oldbot, newbot);
+}
+
+#ifdef WAN_PROBING
+mstatusline(mtmp) register struct monst *mtmp; {
+ pline("Status of %s: ", monnam(mtmp));
+ pline("Level %-2d Gold %-5lu Hp %3d(%d) Ac %-2d Dam %d",
+ mtmp->data->mlevel, mtmp->mgold, mtmp->mhp, mtmp->mhpmax,
+ mtmp->data->ac, (mtmp->data->damn + 1) * (mtmp->data->damd + 1));
+}
+#endif WAN_PROBING
+
+cls(){
+ if(flags.toplin == 1)
+ more();
+ flags.toplin = 0;
+
+ clear_screen();
+
+ flags.botlx = 1;
+}
diff --git a/games/hack/hack.read.c b/games/hack/hack.read.c
new file mode 100644
index 000000000000..5c0de3a7a0c1
--- /dev/null
+++ b/games/hack/hack.read.c
@@ -0,0 +1,539 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.read.c - version 1.0.3 */
+
+#include "hack.h"
+
+extern struct monst *makemon();
+extern struct obj *mkobj_at();
+int identify();
+
+doread() {
+ register struct obj *scroll;
+ register boolean confused = (Confusion != 0);
+ register boolean known = FALSE;
+ extern struct obj *some_armor();
+
+ scroll = getobj("?", "read");
+ if(!scroll) return(0);
+ if(!scroll->dknown && Blind) {
+ pline("Being blind, you cannot read the formula on the scroll.");
+ return(0);
+ }
+ if(Blind)
+ pline("As you pronounce the formula on it, the scroll disappears.");
+ else
+ pline("As you read the scroll, it disappears.");
+ if(confused)
+ pline("Being confused, you mispronounce the magic words ... ");
+
+ switch(scroll->otyp) {
+#ifdef MAIL
+ case SCR_MAIL:
+ readmail(/* scroll */);
+ break;
+#endif MAIL
+ case SCR_ENCHANT_ARMOR:
+ { register struct obj *otmp = some_armor();
+ if(!otmp) {
+ strange_feeling(scroll,"Your skin glows then fades.");
+ return(1);
+ }
+ if(confused) {
+ pline("Your %s glows silver for a moment.",
+ objects[otmp->otyp].oc_name);
+ otmp->rustfree = 1;
+ break;
+ }
+ if(otmp->spe > 3 && rn2(otmp->spe)) {
+ pline("Your %s glows violently green for a while, then evaporates.",
+ objects[otmp->otyp].oc_name);
+ useup(otmp);
+ break;
+ }
+ pline("Your %s glows green for a moment.",
+ objects[otmp->otyp].oc_name);
+ otmp->cursed = 0;
+ otmp->spe++;
+ break;
+ }
+ case SCR_DESTROY_ARMOR:
+ if(confused) {
+ register struct obj *otmp = some_armor();
+ if(!otmp) {
+ strange_feeling(scroll,"Your bones itch.");
+ return(1);
+ }
+ pline("Your %s glows purple for a moment.",
+ objects[otmp->otyp].oc_name);
+ otmp->rustfree = 0;
+ break;
+ }
+ if(uarm) {
+ pline("Your armor turns to dust and falls to the floor!");
+ useup(uarm);
+ } else if(uarmh) {
+ pline("Your helmet turns to dust and is blown away!");
+ useup(uarmh);
+ } else if(uarmg) {
+ pline("Your gloves vanish!");
+ useup(uarmg);
+ selftouch("You");
+ } else {
+ strange_feeling(scroll,"Your skin itches.");
+ return(1);
+ }
+ break;
+ case SCR_CONFUSE_MONSTER:
+ if(confused) {
+ pline("Your hands begin to glow purple.");
+ Confusion += rnd(100);
+ } else {
+ pline("Your hands begin to glow blue.");
+ u.umconf = 1;
+ }
+ break;
+ case SCR_SCARE_MONSTER:
+ { register int ct = 0;
+ register struct monst *mtmp;
+
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(cansee(mtmp->mx,mtmp->my)) {
+ if(confused)
+ mtmp->mflee = mtmp->mfroz =
+ mtmp->msleep = 0;
+ else
+ mtmp->mflee = 1;
+ ct++;
+ }
+ if(!ct) {
+ if(confused)
+ pline("You hear sad wailing in the distance.");
+ else
+ pline("You hear maniacal laughter in the distance.");
+ }
+ break;
+ }
+ case SCR_BLANK_PAPER:
+ if(confused)
+ pline("You see strange patterns on this scroll.");
+ else
+ pline("This scroll seems to be blank.");
+ break;
+ case SCR_REMOVE_CURSE:
+ { register struct obj *obj;
+ if(confused)
+ pline("You feel like you need some help.");
+ else
+ pline("You feel like someone is helping you.");
+ for(obj = invent; obj ; obj = obj->nobj)
+ if(obj->owornmask)
+ obj->cursed = confused;
+ if(Punished && !confused) {
+ Punished = 0;
+ freeobj(uchain);
+ unpobj(uchain);
+ free((char *) uchain);
+ uball->spe = 0;
+ uball->owornmask &= ~W_BALL;
+ uchain = uball = (struct obj *) 0;
+ }
+ break;
+ }
+ case SCR_CREATE_MONSTER:
+ { register int cnt = 1;
+
+ if(!rn2(73)) cnt += rnd(4);
+ if(confused) cnt += 12;
+ while(cnt--)
+ (void) makemon(confused ? PM_ACID_BLOB :
+ (struct permonst *) 0, u.ux, u.uy);
+ break;
+ }
+ case SCR_ENCHANT_WEAPON:
+ if(uwep && confused) {
+ pline("Your %s glows silver for a moment.",
+ objects[uwep->otyp].oc_name);
+ uwep->rustfree = 1;
+ } else
+ if(!chwepon(scroll, 1)) /* tests for !uwep */
+ return(1);
+ break;
+ case SCR_DAMAGE_WEAPON:
+ if(uwep && confused) {
+ pline("Your %s glows purple for a moment.",
+ objects[uwep->otyp].oc_name);
+ uwep->rustfree = 0;
+ } else
+ if(!chwepon(scroll, -1)) /* tests for !uwep */
+ return(1);
+ break;
+ case SCR_TAMING:
+ { register int i,j;
+ register int bd = confused ? 5 : 1;
+ register struct monst *mtmp;
+
+ for(i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++)
+ if(mtmp = m_at(u.ux+i, u.uy+j))
+ (void) tamedog(mtmp, (struct obj *) 0);
+ break;
+ }
+ case SCR_GENOCIDE:
+ { extern char genocided[], fut_geno[];
+ char buf[BUFSZ];
+ register struct monst *mtmp, *mtmp2;
+
+ pline("You have found a scroll of genocide!");
+ known = TRUE;
+ if(confused)
+ *buf = u.usym;
+ else do {
+ pline("What monster do you want to genocide (Type the letter)? ");
+ getlin(buf);
+ } while(strlen(buf) != 1 || !monstersym(*buf));
+ if(!index(fut_geno, *buf))
+ charcat(fut_geno, *buf);
+ if(!index(genocided, *buf))
+ charcat(genocided, *buf);
+ else {
+ pline("Such monsters do not exist in this world.");
+ break;
+ }
+ for(mtmp = fmon; mtmp; mtmp = mtmp2){
+ mtmp2 = mtmp->nmon;
+ if(mtmp->data->mlet == *buf)
+ mondead(mtmp);
+ }
+ pline("Wiped out all %c's.", *buf);
+ if(*buf == u.usym) {
+ killer = "scroll of genocide";
+ u.uhp = -1;
+ }
+ break;
+ }
+ case SCR_LIGHT:
+ if(!Blind) known = TRUE;
+ litroom(!confused);
+ break;
+ case SCR_TELEPORTATION:
+ if(confused)
+ level_tele();
+ else {
+#ifdef QUEST
+ register int oux = u.ux, ouy = u.uy;
+ tele();
+ if(dist(oux, ouy) > 100) known = TRUE;
+#else QUEST
+ register int uroom = inroom(u.ux, u.uy);
+ tele();
+ if(uroom != inroom(u.ux, u.uy)) known = TRUE;
+#endif QUEST
+ }
+ break;
+ case SCR_GOLD_DETECTION:
+ /* Unfortunately this code has become slightly less elegant,
+ now that gold and traps no longer are of the same type. */
+ if(confused) {
+ register struct trap *ttmp;
+
+ if(!ftrap) {
+ strange_feeling(scroll, "Your toes stop itching.");
+ return(1);
+ } else {
+ for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
+ if(ttmp->tx != u.ux || ttmp->ty != u.uy)
+ goto outtrapmap;
+ /* only under me - no separate display required */
+ pline("Your toes itch!");
+ break;
+ outtrapmap:
+ cls();
+ for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
+ at(ttmp->tx, ttmp->ty, '$');
+ prme();
+ pline("You feel very greedy!");
+ }
+ } else {
+ register struct gold *gtmp;
+
+ if(!fgold) {
+ strange_feeling(scroll, "You feel materially poor.");
+ return(1);
+ } else {
+ known = TRUE;
+ for(gtmp = fgold; gtmp; gtmp = gtmp->ngold)
+ if(gtmp->gx != u.ux || gtmp->gy != u.uy)
+ goto outgoldmap;
+ /* only under me - no separate display required */
+ pline("You notice some gold between your feet.");
+ break;
+ outgoldmap:
+ cls();
+ for(gtmp = fgold; gtmp; gtmp = gtmp->ngold)
+ at(gtmp->gx, gtmp->gy, '$');
+ prme();
+ pline("You feel very greedy, and sense gold!");
+ }
+ }
+ /* common sequel */
+ more();
+ docrt();
+ break;
+ case SCR_FOOD_DETECTION:
+ { register ct = 0, ctu = 0;
+ register struct obj *obj;
+ register char foodsym = confused ? POTION_SYM : FOOD_SYM;
+
+ for(obj = fobj; obj; obj = obj->nobj)
+ if(obj->olet == FOOD_SYM) {
+ if(obj->ox == u.ux && obj->oy == u.uy) ctu++;
+ else ct++;
+ }
+ if(!ct && !ctu) {
+ strange_feeling(scroll,"Your nose twitches.");
+ return(1);
+ } else if(!ct) {
+ known = TRUE;
+ pline("You smell %s close nearby.",
+ confused ? "something" : "food");
+
+ } else {
+ known = TRUE;
+ cls();
+ for(obj = fobj; obj; obj = obj->nobj)
+ if(obj->olet == foodsym)
+ at(obj->ox, obj->oy, FOOD_SYM);
+ prme();
+ pline("Your nose tingles and you smell %s!",
+ confused ? "something" : "food");
+ more();
+ docrt();
+ }
+ break;
+ }
+ case SCR_IDENTIFY:
+ /* known = TRUE; */
+ if(confused)
+ pline("You identify this as an identify scroll.");
+ else
+ pline("This is an identify scroll.");
+ useup(scroll);
+ objects[SCR_IDENTIFY].oc_name_known = 1;
+ if(!confused)
+ while(
+ !ggetobj("identify", identify, rn2(5) ? 1 : rn2(5))
+ && invent
+ );
+ return(1);
+ case SCR_MAGIC_MAPPING:
+ { register struct rm *lev;
+ register int num, zx, zy;
+
+ known = TRUE;
+ pline("On this scroll %s a map!",
+ confused ? "was" : "is");
+ for(zy = 0; zy < ROWNO; zy++)
+ for(zx = 0; zx < COLNO; zx++) {
+ if(confused && rn2(7)) continue;
+ lev = &(levl[zx][zy]);
+ if((num = lev->typ) == 0)
+ continue;
+ if(num == SCORR) {
+ lev->typ = CORR;
+ lev->scrsym = CORR_SYM;
+ } else
+ if(num == SDOOR) {
+ lev->typ = DOOR;
+ lev->scrsym = '+';
+ /* do sth in doors ? */
+ } else if(lev->seen) continue;
+#ifndef QUEST
+ if(num != ROOM)
+#endif QUEST
+ {
+ lev->seen = lev->new = 1;
+ if(lev->scrsym == ' ' || !lev->scrsym)
+ newsym(zx,zy);
+ else
+ on_scr(zx,zy);
+ }
+ }
+ break;
+ }
+ case SCR_AMNESIA:
+ { register int zx, zy;
+
+ known = TRUE;
+ for(zx = 0; zx < COLNO; zx++) for(zy = 0; zy < ROWNO; zy++)
+ if(!confused || rn2(7))
+ if(!cansee(zx,zy))
+ levl[zx][zy].seen = 0;
+ docrt();
+ pline("Thinking of Maud you forget everything else.");
+ break;
+ }
+ case SCR_FIRE:
+ { register int num;
+ register struct monst *mtmp;
+
+ known = TRUE;
+ if(confused) {
+ pline("The scroll catches fire and you burn your hands.");
+ losehp(1, "scroll of fire");
+ } else {
+ pline("The scroll erupts in a tower of flame!");
+ if(Fire_resistance)
+ pline("You are uninjured.");
+ else {
+ num = rnd(6);
+ u.uhpmax -= num;
+ losehp(num, "scroll of fire");
+ }
+ }
+ num = (2*num + 1)/3;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+ if(dist(mtmp->mx,mtmp->my) < 3) {
+ mtmp->mhp -= num;
+ if(index("FY", mtmp->data->mlet))
+ mtmp->mhp -= 3*num; /* this might well kill 'F's */
+ if(mtmp->mhp < 1) {
+ killed(mtmp);
+ break; /* primitive */
+ }
+ }
+ }
+ break;
+ }
+ case SCR_PUNISHMENT:
+ known = TRUE;
+ if(confused) {
+ pline("You feel guilty.");
+ break;
+ }
+ pline("You are being punished for your misbehaviour!");
+ if(Punished){
+ pline("Your iron ball gets heavier.");
+ uball->owt += 15;
+ break;
+ }
+ Punished = INTRINSIC;
+ setworn(mkobj_at(CHAIN_SYM, u.ux, u.uy), W_CHAIN);
+ setworn(mkobj_at(BALL_SYM, u.ux, u.uy), W_BALL);
+ uball->spe = 1; /* special ball (see save) */
+ break;
+ default:
+ impossible("What weird language is this written in? (%u)",
+ scroll->otyp);
+ }
+ if(!objects[scroll->otyp].oc_name_known) {
+ if(known && !confused) {
+ objects[scroll->otyp].oc_name_known = 1;
+ more_experienced(0,10);
+ } else if(!objects[scroll->otyp].oc_uname)
+ docall(scroll);
+ }
+ useup(scroll);
+ return(1);
+}
+
+identify(otmp) /* also called by newmail() */
+register struct obj *otmp;
+{
+ objects[otmp->otyp].oc_name_known = 1;
+ otmp->known = otmp->dknown = 1;
+ prinv(otmp);
+ return(1);
+}
+
+litroom(on)
+register boolean on;
+{
+ register num,zx,zy;
+
+ /* first produce the text (provided he is not blind) */
+ if(Blind) goto do_it;
+ if(!on) {
+ if(u.uswallow || !xdnstair || levl[u.ux][u.uy].typ == CORR ||
+ !levl[u.ux][u.uy].lit) {
+ pline("It seems even darker in here than before.");
+ return;
+ } else
+ pline("It suddenly becomes dark in here.");
+ } else {
+ if(u.uswallow){
+ pline("%s's stomach is lit.", Monnam(u.ustuck));
+ return;
+ }
+ if(!xdnstair){
+ pline("Nothing Happens.");
+ return;
+ }
+#ifdef QUEST
+ pline("The cave lights up around you, then fades.");
+ return;
+#else QUEST
+ if(levl[u.ux][u.uy].typ == CORR) {
+ pline("The corridor lights up around you, then fades.");
+ return;
+ } else if(levl[u.ux][u.uy].lit) {
+ pline("The light here seems better now.");
+ return;
+ } else
+ pline("The room is lit.");
+#endif QUEST
+ }
+
+do_it:
+#ifdef QUEST
+ return;
+#else QUEST
+ if(levl[u.ux][u.uy].lit == on)
+ return;
+ if(levl[u.ux][u.uy].typ == DOOR) {
+ if(IS_ROOM(levl[u.ux][u.uy+1].typ)) zy = u.uy+1;
+ else if(IS_ROOM(levl[u.ux][u.uy-1].typ)) zy = u.uy-1;
+ else zy = u.uy;
+ if(IS_ROOM(levl[u.ux+1][u.uy].typ)) zx = u.ux+1;
+ else if(IS_ROOM(levl[u.ux-1][u.uy].typ)) zx = u.ux-1;
+ else zx = u.ux;
+ } else {
+ zx = u.ux;
+ zy = u.uy;
+ }
+ for(seelx = u.ux; (num = levl[seelx-1][zy].typ) != CORR && num != 0;
+ seelx--);
+ for(seehx = u.ux; (num = levl[seehx+1][zy].typ) != CORR && num != 0;
+ seehx++);
+ for(seely = u.uy; (num = levl[zx][seely-1].typ) != CORR && num != 0;
+ seely--);
+ for(seehy = u.uy; (num = levl[zx][seehy+1].typ) != CORR && num != 0;
+ seehy++);
+ for(zy = seely; zy <= seehy; zy++)
+ for(zx = seelx; zx <= seehx; zx++) {
+ levl[zx][zy].lit = on;
+ if(!Blind && dist(zx,zy) > 2)
+ if(on) prl(zx,zy); else nosee(zx,zy);
+ }
+ if(!on) seehx = 0;
+#endif QUEST
+}
+
+/* Test whether we may genocide all monsters with symbol ch */
+monstersym(ch) /* arnold@ucsfcgl */
+register char ch;
+{
+ register struct permonst *mp;
+ extern struct permonst pm_eel;
+
+ /*
+ * can't genocide certain monsters
+ */
+ if (index("12 &:", ch))
+ return FALSE;
+
+ if (ch == pm_eel.mlet)
+ return TRUE;
+ for (mp = mons; mp < &mons[CMNUM+2]; mp++)
+ if (mp->mlet == ch)
+ return TRUE;
+ return FALSE;
+}
diff --git a/games/hack/hack.rip.c b/games/hack/hack.rip.c
new file mode 100644
index 000000000000..bdc282308c7e
--- /dev/null
+++ b/games/hack/hack.rip.c
@@ -0,0 +1,81 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.rip.c - version 1.0.2 */
+
+#include <stdio.h>
+#include "hack.h"
+
+extern char plname[];
+
+static char *rip[] = {
+" ----------",
+" / \\",
+" / REST \\",
+" / IN \\",
+" / PEACE \\",
+" / \\",
+" | |",
+" | |",
+" | |",
+" | |",
+" | |",
+" | 1001 |",
+" *| * * * | *",
+" _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n",
+0
+};
+
+outrip(){
+ register char **dp = rip;
+ register char *dpx;
+ char buf[BUFSZ];
+ register x,y;
+
+ cls();
+ (void) strcpy(buf, plname);
+ buf[16] = 0;
+ center(6, buf);
+ (void) sprintf(buf, "%ld AU", u.ugold);
+ center(7, buf);
+ (void) sprintf(buf, "killed by%s",
+ !strncmp(killer, "the ", 4) ? "" :
+ !strcmp(killer, "starvation") ? "" :
+ index(vowels, *killer) ? " an" : " a");
+ center(8, buf);
+ (void) strcpy(buf, killer);
+ if(strlen(buf) > 16) {
+ register int i,i0,i1;
+ i0 = i1 = 0;
+ for(i = 0; i <= 16; i++)
+ if(buf[i] == ' ') i0 = i, i1 = i+1;
+ if(!i0) i0 = i1 = 16;
+ buf[i1 + 16] = 0;
+ center(10, buf+i1);
+ buf[i0] = 0;
+ }
+ center(9, buf);
+ (void) sprintf(buf, "%4d", getyear());
+ center(11, buf);
+ for(y=8; *dp; y++,dp++){
+ x = 0;
+ dpx = *dp;
+ while(dpx[x]) {
+ while(dpx[x] == ' ') x++;
+ curs(x,y);
+ while(dpx[x] && dpx[x] != ' '){
+ extern int done_stopprint;
+ if(done_stopprint)
+ return;
+ curx++;
+ (void) putchar(dpx[x++]);
+ }
+ }
+ }
+ getret();
+}
+
+center(line, text) int line; char *text; {
+register char *ip,*op;
+ ip = text;
+ op = &rip[line][28 - ((strlen(text)+1)/2)];
+ while(*ip) *op++ = *ip++;
+}
diff --git a/games/hack/hack.rumors.c b/games/hack/hack.rumors.c
new file mode 100644
index 000000000000..ee5f139993ce
--- /dev/null
+++ b/games/hack/hack.rumors.c
@@ -0,0 +1,63 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.rumors.c - version 1.0.3 */
+
+#include <stdio.h>
+#include "hack.h" /* for RUMORFILE and BSD (index) */
+#define CHARSZ 8 /* number of bits in a char */
+extern long *alloc();
+extern char *index();
+int n_rumors = 0;
+int n_used_rumors = -1;
+char *usedbits;
+
+init_rumors(rumf) register FILE *rumf; {
+register int i;
+ n_used_rumors = 0;
+ while(skipline(rumf)) n_rumors++;
+ rewind(rumf);
+ i = n_rumors/CHARSZ;
+ usedbits = (char *) alloc((unsigned)(i+1));
+ for( ; i>=0; i--) usedbits[i] = 0;
+}
+
+skipline(rumf) register FILE *rumf; {
+char line[COLNO];
+ while(1) {
+ if(!fgets(line, sizeof(line), rumf)) return(0);
+ if(index(line, '\n')) return(1);
+ }
+}
+
+outline(rumf) register FILE *rumf; {
+char line[COLNO];
+register char *ep;
+ if(!fgets(line, sizeof(line), rumf)) return;
+ if((ep = index(line, '\n')) != 0) *ep = 0;
+ pline("This cookie has a scrap of paper inside! It reads: ");
+ pline(line);
+}
+
+outrumor(){
+register int rn,i;
+register FILE *rumf;
+ if(n_rumors <= n_used_rumors ||
+ (rumf = fopen(RUMORFILE, "r")) == (FILE *) 0) return;
+ if(n_used_rumors < 0) init_rumors(rumf);
+ if(!n_rumors) goto none;
+ rn = rn2(n_rumors - n_used_rumors);
+ i = 0;
+ while(rn || used(i)) {
+ (void) skipline(rumf);
+ if(!used(i)) rn--;
+ i++;
+ }
+ usedbits[i/CHARSZ] |= (1 << (i % CHARSZ));
+ n_used_rumors++;
+ outline(rumf);
+none:
+ (void) fclose(rumf);
+}
+
+used(i) register int i; {
+ return(usedbits[i/CHARSZ] & (1 << (i % CHARSZ)));
+}
diff --git a/games/hack/hack.save.c b/games/hack/hack.save.c
new file mode 100644
index 000000000000..fe09e1e72e1e
--- /dev/null
+++ b/games/hack/hack.save.c
@@ -0,0 +1,238 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.save.c - version 1.0.3 */
+
+#include "hack.h"
+extern char genocided[60]; /* defined in Decl.c */
+extern char fut_geno[60]; /* idem */
+#include <signal.h>
+
+extern char SAVEF[], nul[];
+extern char pl_character[PL_CSIZ];
+extern long lseek();
+extern struct obj *restobjchn();
+extern struct monst *restmonchn();
+
+dosave(){
+ if(dosave0(0)) {
+ settty("Be seeing you ...\n");
+ exit(0);
+ }
+#ifdef lint
+ return(0);
+#endif lint
+}
+
+#ifndef NOSAVEONHANGUP
+hangup(){
+ (void) dosave0(1);
+ exit(1);
+}
+#endif NOSAVEONHANGUP
+
+/* returns 1 if save successful */
+dosave0(hu) int hu; {
+ register fd, ofd;
+ int tmp; /* not register ! */
+
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) signal(SIGINT, SIG_IGN);
+ if((fd = creat(SAVEF, FMASK)) < 0) {
+ if(!hu) pline("Cannot open save file. (Continue or Quit)");
+ (void) unlink(SAVEF); /* ab@unido */
+ return(0);
+ }
+ if(flags.moonphase == FULL_MOON) /* ut-sally!fletcher */
+ u.uluck--; /* and unido!ab */
+ savelev(fd,dlevel);
+ saveobjchn(fd, invent);
+ saveobjchn(fd, fcobj);
+ savemonchn(fd, fallen_down);
+ tmp = getuid();
+ bwrite(fd, (char *) &tmp, sizeof tmp);
+ bwrite(fd, (char *) &flags, sizeof(struct flag));
+ bwrite(fd, (char *) &dlevel, sizeof dlevel);
+ bwrite(fd, (char *) &maxdlevel, sizeof maxdlevel);
+ bwrite(fd, (char *) &moves, sizeof moves);
+ bwrite(fd, (char *) &u, sizeof(struct you));
+ if(u.ustuck)
+ bwrite(fd, (char *) &(u.ustuck->m_id), sizeof u.ustuck->m_id);
+ bwrite(fd, (char *) pl_character, sizeof pl_character);
+ bwrite(fd, (char *) genocided, sizeof genocided);
+ bwrite(fd, (char *) fut_geno, sizeof fut_geno);
+ savenames(fd);
+ for(tmp = 1; tmp <= maxdlevel; tmp++) {
+ extern int hackpid;
+ extern boolean level_exists[];
+
+ if(tmp == dlevel || !level_exists[tmp]) continue;
+ glo(tmp);
+ if((ofd = open(lock, 0)) < 0) {
+ if(!hu) pline("Error while saving: cannot read %s.", lock);
+ (void) close(fd);
+ (void) unlink(SAVEF);
+ if(!hu) done("tricked");
+ return(0);
+ }
+ getlev(ofd, hackpid, tmp);
+ (void) close(ofd);
+ bwrite(fd, (char *) &tmp, sizeof tmp); /* level number */
+ savelev(fd,tmp); /* actual level */
+ (void) unlink(lock);
+ }
+ (void) close(fd);
+ glo(dlevel);
+ (void) unlink(lock); /* get rid of current level --jgm */
+ glo(0);
+ (void) unlink(lock);
+ return(1);
+}
+
+dorecover(fd)
+register fd;
+{
+ register nfd;
+ int tmp; /* not a register ! */
+ unsigned mid; /* idem */
+ struct obj *otmp;
+ extern boolean restoring;
+
+ restoring = TRUE;
+ getlev(fd, 0, 0);
+ invent = restobjchn(fd);
+ for(otmp = invent; otmp; otmp = otmp->nobj)
+ if(otmp->owornmask)
+ setworn(otmp, otmp->owornmask);
+ fcobj = restobjchn(fd);
+ fallen_down = restmonchn(fd);
+ mread(fd, (char *) &tmp, sizeof tmp);
+ if(tmp != getuid()) { /* strange ... */
+ (void) close(fd);
+ (void) unlink(SAVEF);
+ puts("Saved game was not yours.");
+ restoring = FALSE;
+ return(0);
+ }
+ mread(fd, (char *) &flags, sizeof(struct flag));
+ mread(fd, (char *) &dlevel, sizeof dlevel);
+ mread(fd, (char *) &maxdlevel, sizeof maxdlevel);
+ mread(fd, (char *) &moves, sizeof moves);
+ mread(fd, (char *) &u, sizeof(struct you));
+ if(u.ustuck)
+ mread(fd, (char *) &mid, sizeof mid);
+ mread(fd, (char *) pl_character, sizeof pl_character);
+ mread(fd, (char *) genocided, sizeof genocided);
+ mread(fd, (char *) fut_geno, sizeof fut_geno);
+ restnames(fd);
+ while(1) {
+ if(read(fd, (char *) &tmp, sizeof tmp) != sizeof tmp)
+ break;
+ getlev(fd, 0, tmp);
+ glo(tmp);
+ if((nfd = creat(lock, FMASK)) < 0)
+ panic("Cannot open temp file %s!\n", lock);
+ savelev(nfd,tmp);
+ (void) close(nfd);
+ }
+ (void) lseek(fd, 0L, 0);
+ getlev(fd, 0, 0);
+ (void) close(fd);
+ (void) unlink(SAVEF);
+ if(Punished) {
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(otmp->olet == CHAIN_SYM) goto chainfnd;
+ panic("Cannot find the iron chain?");
+ chainfnd:
+ uchain = otmp;
+ if(!uball){
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(otmp->olet == BALL_SYM && otmp->spe)
+ goto ballfnd;
+ panic("Cannot find the iron ball?");
+ ballfnd:
+ uball = otmp;
+ }
+ }
+ if(u.ustuck) {
+ register struct monst *mtmp;
+
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->m_id == mid) goto monfnd;
+ panic("Cannot find the monster ustuck.");
+ monfnd:
+ u.ustuck = mtmp;
+ }
+#ifndef QUEST
+ setsee(); /* only to recompute seelx etc. - these weren't saved */
+#endif QUEST
+ docrt();
+ restoring = FALSE;
+ return(1);
+}
+
+struct obj *
+restobjchn(fd)
+register fd;
+{
+ register struct obj *otmp, *otmp2;
+ register struct obj *first = 0;
+ int xl;
+#ifdef lint
+ /* suppress "used before set" warning from lint */
+ otmp2 = 0;
+#endif lint
+ while(1) {
+ mread(fd, (char *) &xl, sizeof(xl));
+ if(xl == -1) break;
+ otmp = newobj(xl);
+ if(!first) first = otmp;
+ else otmp2->nobj = otmp;
+ mread(fd, (char *) otmp, (unsigned) xl + sizeof(struct obj));
+ if(!otmp->o_id) otmp->o_id = flags.ident++;
+ otmp2 = otmp;
+ }
+ if(first && otmp2->nobj){
+ impossible("Restobjchn: error reading objchn.");
+ otmp2->nobj = 0;
+ }
+ return(first);
+}
+
+struct monst *
+restmonchn(fd)
+register fd;
+{
+ register struct monst *mtmp, *mtmp2;
+ register struct monst *first = 0;
+ int xl;
+
+ struct permonst *monbegin;
+ long differ;
+
+ mread(fd, (char *)&monbegin, sizeof(monbegin));
+ differ = (char *)(&mons[0]) - (char *)(monbegin);
+
+#ifdef lint
+ /* suppress "used before set" warning from lint */
+ mtmp2 = 0;
+#endif lint
+ while(1) {
+ mread(fd, (char *) &xl, sizeof(xl));
+ if(xl == -1) break;
+ mtmp = newmonst(xl);
+ if(!first) first = mtmp;
+ else mtmp2->nmon = mtmp;
+ mread(fd, (char *) mtmp, (unsigned) xl + sizeof(struct monst));
+ if(!mtmp->m_id)
+ mtmp->m_id = flags.ident++;
+ mtmp->data = (struct permonst *)
+ ((char *) mtmp->data + differ);
+ if(mtmp->minvent)
+ mtmp->minvent = restobjchn(fd);
+ mtmp2 = mtmp;
+ }
+ if(first && mtmp2->nmon){
+ impossible("Restmonchn: error reading monchn.");
+ mtmp2->nmon = 0;
+ }
+ return(first);
+}
diff --git a/games/hack/hack.search.c b/games/hack/hack.search.c
new file mode 100644
index 000000000000..c0ef9932ee91
--- /dev/null
+++ b/games/hack/hack.search.c
@@ -0,0 +1,133 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.search.c - version 1.0.3 */
+
+#include "hack.h"
+
+extern struct monst *makemon();
+
+findit() /* returns number of things found */
+{
+ int num;
+ register xchar zx,zy;
+ register struct trap *ttmp;
+ register struct monst *mtmp;
+ xchar lx,hx,ly,hy;
+
+ if(u.uswallow) return(0);
+ for(lx = u.ux; (num = levl[lx-1][u.uy].typ) && num != CORR; lx--) ;
+ for(hx = u.ux; (num = levl[hx+1][u.uy].typ) && num != CORR; hx++) ;
+ for(ly = u.uy; (num = levl[u.ux][ly-1].typ) && num != CORR; ly--) ;
+ for(hy = u.uy; (num = levl[u.ux][hy+1].typ) && num != CORR; hy++) ;
+ num = 0;
+ for(zy = ly; zy <= hy; zy++)
+ for(zx = lx; zx <= hx; zx++) {
+ if(levl[zx][zy].typ == SDOOR) {
+ levl[zx][zy].typ = DOOR;
+ atl(zx, zy, '+');
+ num++;
+ } else if(levl[zx][zy].typ == SCORR) {
+ levl[zx][zy].typ = CORR;
+ atl(zx, zy, CORR_SYM);
+ num++;
+ } else if(ttmp = t_at(zx, zy)) {
+ if(ttmp->ttyp == PIERC){
+ (void) makemon(PM_PIERCER, zx, zy);
+ num++;
+ deltrap(ttmp);
+ } else if(!ttmp->tseen) {
+ ttmp->tseen = 1;
+ if(!vism_at(zx, zy))
+ atl(zx,zy,'^');
+ num++;
+ }
+ } else if(mtmp = m_at(zx,zy)) if(mtmp->mimic){
+ seemimic(mtmp);
+ num++;
+ }
+ }
+ return(num);
+}
+
+dosearch()
+{
+ register xchar x,y;
+ register struct trap *trap;
+ register struct monst *mtmp;
+
+ if(u.uswallow)
+ pline("What are you looking for? The exit?");
+ else
+ for(x = u.ux-1; x < u.ux+2; x++)
+ for(y = u.uy-1; y < u.uy+2; y++) if(x != u.ux || y != u.uy) {
+ if(levl[x][y].typ == SDOOR) {
+ if(rn2(7)) continue;
+ levl[x][y].typ = DOOR;
+ levl[x][y].seen = 0; /* force prl */
+ prl(x,y);
+ nomul(0);
+ } else if(levl[x][y].typ == SCORR) {
+ if(rn2(7)) continue;
+ levl[x][y].typ = CORR;
+ levl[x][y].seen = 0; /* force prl */
+ prl(x,y);
+ nomul(0);
+ } else {
+ /* Be careful not to find anything in an SCORR or SDOOR */
+ if(mtmp = m_at(x,y)) if(mtmp->mimic){
+ seemimic(mtmp);
+ pline("You find a mimic.");
+ return(1);
+ }
+ for(trap = ftrap; trap; trap = trap->ntrap)
+ if(trap->tx == x && trap->ty == y &&
+ !trap->tseen && !rn2(8)) {
+ nomul(0);
+ pline("You find a%s.", traps[trap->ttyp]);
+ if(trap->ttyp == PIERC) {
+ deltrap(trap);
+ (void) makemon(PM_PIERCER,x,y);
+ return(1);
+ }
+ trap->tseen = 1;
+ if(!vism_at(x,y)) atl(x,y,'^');
+ }
+ }
+ }
+ return(1);
+}
+
+doidtrap() {
+register struct trap *trap;
+register int x,y;
+ if(!getdir(1)) return(0);
+ x = u.ux + u.dx;
+ y = u.uy + u.dy;
+ for(trap = ftrap; trap; trap = trap->ntrap)
+ if(trap->tx == x && trap->ty == y && trap->tseen) {
+ if(u.dz)
+ if((u.dz < 0) != (!xdnstair && trap->ttyp == TRAPDOOR))
+ continue;
+ pline("That is a%s.", traps[trap->ttyp]);
+ return(0);
+ }
+ pline("I can't see a trap there.");
+ return(0);
+}
+
+wakeup(mtmp)
+register struct monst *mtmp;
+{
+ mtmp->msleep = 0;
+ setmangry(mtmp);
+ if(mtmp->mimic) seemimic(mtmp);
+}
+
+/* NOTE: we must check if(mtmp->mimic) before calling this routine */
+seemimic(mtmp)
+register struct monst *mtmp;
+{
+ mtmp->mimic = 0;
+ mtmp->mappearance = 0;
+ unpmon(mtmp);
+ pmon(mtmp);
+}
diff --git a/games/hack/hack.sh b/games/hack/hack.sh
new file mode 100644
index 000000000000..8136ec42055d
--- /dev/null
+++ b/games/hack/hack.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+HACKDIR=/usr/games/lib/hackdir
+HACK=$HACKDIR/hack
+MAXNROFPLAYERS=4
+
+cd $HACKDIR
+case $1 in
+ -s*)
+ exec $HACK $@
+ ;;
+ *)
+ exec $HACK $@ $MAXNROFPLAYERS
+ ;;
+esac
diff --git a/games/hack/hack.shk.c b/games/hack/hack.shk.c
new file mode 100644
index 000000000000..1478b1d8d576
--- /dev/null
+++ b/games/hack/hack.shk.c
@@ -0,0 +1,987 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.shk.c - version 1.0.3 */
+
+#include "hack.h"
+#ifdef QUEST
+int shlevel = 0;
+struct monst *shopkeeper = 0;
+struct obj *billobjs = 0;
+obfree(obj,merge) register struct obj *obj, *merge; {
+ free((char *) obj);
+}
+inshop(){ return(0); }
+shopdig(){}
+addtobill(){}
+subfrombill(){}
+splitbill(){}
+dopay(){ return(0); }
+paybill(){}
+doinvbill(){ return(0); }
+shkdead(){}
+shkcatch(){ return(0); }
+shk_move(){ return(0); }
+replshk(mtmp,mtmp2) struct monst *mtmp, *mtmp2; {}
+char *shkname(){ return(""); }
+
+#else QUEST
+#include "hack.mfndpos.h"
+#include "def.mkroom.h"
+#include "def.eshk.h"
+
+#define ESHK(mon) ((struct eshk *)(&(mon->mextra[0])))
+#define NOTANGRY(mon) mon->mpeaceful
+#define ANGRY(mon) !NOTANGRY(mon)
+
+extern char plname[], *xname();
+extern struct obj *o_on(), *bp_to_obj();
+
+/* Descriptor of current shopkeeper. Note that the bill need not be
+ per-shopkeeper, since it is valid only when in a shop. */
+static struct monst *shopkeeper = 0;
+static struct bill_x *bill;
+static int shlevel = 0; /* level of this shopkeeper */
+ struct obj *billobjs; /* objects on bill with bp->useup */
+ /* only accessed here and by save & restore */
+static long int total; /* filled by addupbill() */
+static long int followmsg; /* last time of follow message */
+
+/*
+ invariants: obj->unpaid iff onbill(obj) [unless bp->useup]
+ obj->quan <= bp->bquan
+ */
+
+
+char shtypes[] = { /* 8 shoptypes: 7 specialized, 1 mixed */
+ RING_SYM, WAND_SYM, WEAPON_SYM, FOOD_SYM, SCROLL_SYM,
+ POTION_SYM, ARMOR_SYM, 0
+};
+
+static char *shopnam[] = {
+ "engagement ring", "walking cane", "antique weapon",
+ "delicatessen", "second hand book", "liquor",
+ "used armor", "assorted antiques"
+};
+
+char *
+shkname(mtmp) /* called in do_name.c */
+register struct monst *mtmp;
+{
+ return(ESHK(mtmp)->shknam);
+}
+
+static void setpaid();
+
+shkdead(mtmp) /* called in mon.c */
+register struct monst *mtmp;
+{
+ register struct eshk *eshk = ESHK(mtmp);
+
+ if(eshk->shoplevel == dlevel)
+ rooms[eshk->shoproom].rtype = 0;
+ if(mtmp == shopkeeper) {
+ setpaid();
+ shopkeeper = 0;
+ bill = (struct bill_x *) -1000; /* dump core when referenced */
+ }
+}
+
+replshk(mtmp,mtmp2)
+register struct monst *mtmp, *mtmp2;
+{
+ if(mtmp == shopkeeper) {
+ shopkeeper = mtmp2;
+ bill = &(ESHK(shopkeeper)->bill[0]);
+ }
+}
+
+static void
+setpaid(){ /* caller has checked that shopkeeper exists */
+ /* either we paid or left the shop or he just died */
+register struct obj *obj;
+register struct monst *mtmp;
+ for(obj = invent; obj; obj = obj->nobj)
+ obj->unpaid = 0;
+ for(obj = fobj; obj; obj = obj->nobj)
+ obj->unpaid = 0;
+ for(obj = fcobj; obj; obj = obj->nobj)
+ obj->unpaid = 0;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ for(obj = mtmp->minvent; obj; obj = obj->nobj)
+ obj->unpaid = 0;
+ for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
+ for(obj = mtmp->minvent; obj; obj = obj->nobj)
+ obj->unpaid = 0;
+ while(obj = billobjs){
+ billobjs = obj->nobj;
+ free((char *) obj);
+ }
+ ESHK(shopkeeper)->billct = 0;
+}
+
+static
+addupbill(){ /* delivers result in total */
+ /* caller has checked that shopkeeper exists */
+register ct = ESHK(shopkeeper)->billct;
+register struct bill_x *bp = bill;
+ total = 0;
+ while(ct--){
+ total += bp->price * bp->bquan;
+ bp++;
+ }
+}
+
+inshop(){
+register roomno = inroom(u.ux,u.uy);
+
+ static void findshk();
+
+ /* Did we just leave a shop? */
+ if(u.uinshop &&
+ (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) {
+ if(shopkeeper) {
+ if(ESHK(shopkeeper)->billct) {
+ if(inroom(shopkeeper->mx, shopkeeper->my)
+ == u.uinshop - 1) /* ab@unido */
+ pline("Somehow you escaped the shop without paying!");
+ addupbill();
+ pline("You stole for a total worth of %ld zorkmids.",
+ total);
+ ESHK(shopkeeper)->robbed += total;
+ setpaid();
+ if((rooms[ESHK(shopkeeper)->shoproom].rtype == GENERAL)
+ == (rn2(3) == 0))
+ ESHK(shopkeeper)->following = 1;
+ }
+ shopkeeper = 0;
+ shlevel = 0;
+ }
+ u.uinshop = 0;
+ }
+
+ /* Did we just enter a zoo of some kind? */
+ if(roomno >= 0) {
+ register int rt = rooms[roomno].rtype;
+ register struct monst *mtmp;
+ if(rt == ZOO) {
+ pline("Welcome to David's treasure zoo!");
+ } else
+ if(rt == SWAMP) {
+ pline("It looks rather muddy down here.");
+ } else
+ if(rt == MORGUE) {
+ if(midnight())
+ pline("Go away! Go away!");
+ else
+ pline("You get an uncanny feeling ...");
+ } else
+ rt = 0;
+ if(rt != 0) {
+ rooms[roomno].rtype = 0;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(rt != ZOO || !rn2(3))
+ mtmp->msleep = 0;
+ }
+ }
+
+ /* Did we just enter a shop? */
+ if(roomno >= 0 && rooms[roomno].rtype >= 8) {
+ if(shlevel != dlevel || !shopkeeper
+ || ESHK(shopkeeper)->shoproom != roomno)
+ findshk(roomno);
+ if(!shopkeeper) {
+ rooms[roomno].rtype = 0;
+ u.uinshop = 0;
+ } else if(!u.uinshop){
+ if(!ESHK(shopkeeper)->visitct ||
+ strncmp(ESHK(shopkeeper)->customer, plname, PL_NSIZ)){
+
+ /* He seems to be new here */
+ ESHK(shopkeeper)->visitct = 0;
+ ESHK(shopkeeper)->following = 0;
+ (void) strncpy(ESHK(shopkeeper)->customer,plname,PL_NSIZ);
+ NOTANGRY(shopkeeper) = 1;
+ }
+ if(!ESHK(shopkeeper)->following) {
+ boolean box, pick;
+
+ pline("Hello %s! Welcome%s to %s's %s shop!",
+ plname,
+ ESHK(shopkeeper)->visitct++ ? " again" : "",
+ shkname(shopkeeper),
+ shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8] );
+ box = carrying(ICE_BOX);
+ pick = carrying(PICK_AXE);
+ if(box || pick) {
+ if(dochug(shopkeeper)) {
+ u.uinshop = 0; /* he died moving */
+ return(0);
+ }
+ pline("Will you please leave your %s outside?",
+ (box && pick) ? "box and pick-axe" :
+ box ? "box" : "pick-axe");
+ }
+ }
+ u.uinshop = roomno + 1;
+ }
+ }
+ return(u.uinshop);
+}
+
+static void
+findshk(roomno)
+register roomno;
+{
+register struct monst *mtmp;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->isshk && ESHK(mtmp)->shoproom == roomno
+ && ESHK(mtmp)->shoplevel == dlevel) {
+ shopkeeper = mtmp;
+ bill = &(ESHK(shopkeeper)->bill[0]);
+ shlevel = dlevel;
+ if(ANGRY(shopkeeper) &&
+ strncmp(ESHK(shopkeeper)->customer,plname,PL_NSIZ))
+ NOTANGRY(shopkeeper) = 1;
+ /* billobjs = 0; -- this is wrong if we save in a shop */
+ /* (and it is harmless to have too many things in billobjs) */
+ return;
+ }
+ shopkeeper = 0;
+ shlevel = 0;
+ bill = (struct bill_x *) -1000; /* dump core when referenced */
+}
+
+static struct bill_x *
+onbill(obj) register struct obj *obj; {
+register struct bill_x *bp;
+ if(!shopkeeper) return(0);
+ for(bp = bill; bp < &bill[ESHK(shopkeeper)->billct]; bp++)
+ if(bp->bo_id == obj->o_id) {
+ if(!obj->unpaid) pline("onbill: paid obj on bill?");
+ return(bp);
+ }
+ if(obj->unpaid) pline("onbill: unpaid obj not on bill?");
+ return(0);
+}
+
+/* called with two args on merge */
+obfree(obj,merge) register struct obj *obj, *merge; {
+register struct bill_x *bp = onbill(obj);
+register struct bill_x *bpm;
+ if(bp) {
+ if(!merge){
+ bp->useup = 1;
+ obj->unpaid = 0; /* only for doinvbill */
+ obj->nobj = billobjs;
+ billobjs = obj;
+ return;
+ }
+ bpm = onbill(merge);
+ if(!bpm){
+ /* this used to be a rename */
+ impossible("obfree: not on bill??");
+ return;
+ } else {
+ /* this was a merger */
+ bpm->bquan += bp->bquan;
+ ESHK(shopkeeper)->billct--;
+ *bp = bill[ESHK(shopkeeper)->billct];
+ }
+ }
+ free((char *) obj);
+}
+
+static
+pay(tmp,shkp)
+long tmp;
+register struct monst *shkp;
+{
+ long robbed = ESHK(shkp)->robbed;
+
+ u.ugold -= tmp;
+ shkp->mgold += tmp;
+ flags.botl = 1;
+ if(robbed) {
+ robbed -= tmp;
+ if(robbed < 0) robbed = 0;
+ ESHK(shkp)->robbed = robbed;
+ }
+}
+
+dopay(){
+long ltmp;
+register struct bill_x *bp;
+register struct monst *shkp;
+int pass, tmp;
+
+ static int dopayobj();
+
+ multi = 0;
+ (void) inshop();
+ for(shkp = fmon; shkp; shkp = shkp->nmon)
+ if(shkp->isshk && dist(shkp->mx,shkp->my) < 3)
+ break;
+ if(!shkp && u.uinshop &&
+ inroom(shopkeeper->mx,shopkeeper->my) == ESHK(shopkeeper)->shoproom)
+ shkp = shopkeeper;
+
+ if(!shkp) {
+ pline("There is nobody here to receive your payment.");
+ return(0);
+ }
+ ltmp = ESHK(shkp)->robbed;
+ if(shkp != shopkeeper && NOTANGRY(shkp)) {
+ if(!ltmp) {
+ pline("You do not owe %s anything.", monnam(shkp));
+ } else
+ if(!u.ugold) {
+ pline("You have no money.");
+ } else {
+ long ugold = u.ugold;
+
+ if(u.ugold > ltmp) {
+ pline("You give %s the %ld gold pieces he asked for.",
+ monnam(shkp), ltmp);
+ pay(ltmp, shkp);
+ } else {
+ pline("You give %s all your gold.", monnam(shkp));
+ pay(u.ugold, shkp);
+ }
+ if(ugold < ltmp/2) {
+ pline("Unfortunately, he doesn't look satisfied.");
+ } else {
+ ESHK(shkp)->robbed = 0;
+ ESHK(shkp)->following = 0;
+ if(ESHK(shkp)->shoplevel != dlevel) {
+ /* For convenience's sake, let him disappear */
+ shkp->minvent = 0; /* %% */
+ shkp->mgold = 0;
+ mondead(shkp);
+ }
+ }
+ }
+ return(1);
+ }
+
+ if(!ESHK(shkp)->billct){
+ pline("You do not owe %s anything.", monnam(shkp));
+ if(!u.ugold){
+ pline("Moreover, you have no money.");
+ return(1);
+ }
+ if(ESHK(shkp)->robbed){
+#define min(a,b) ((a<b)?a:b)
+ pline("But since his shop has been robbed recently,");
+ pline("you %srepay %s's expenses.",
+ (u.ugold < ESHK(shkp)->robbed) ? "partially " : "",
+ monnam(shkp));
+ pay(min(u.ugold, ESHK(shkp)->robbed), shkp);
+ ESHK(shkp)->robbed = 0;
+ return(1);
+ }
+ if(ANGRY(shkp)){
+ pline("But in order to appease %s,",
+ amonnam(shkp, "angry"));
+ if(u.ugold >= 1000){
+ ltmp = 1000;
+ pline(" you give him 1000 gold pieces.");
+ } else {
+ ltmp = u.ugold;
+ pline(" you give him all your money.");
+ }
+ pay(ltmp, shkp);
+ if(strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)
+ || rn2(3)){
+ pline("%s calms down.", Monnam(shkp));
+ NOTANGRY(shkp) = 1;
+ } else pline("%s is as angry as ever.",
+ Monnam(shkp));
+ }
+ return(1);
+ }
+ if(shkp != shopkeeper) {
+ impossible("dopay: not to shopkeeper?");
+ if(shopkeeper) setpaid();
+ return(0);
+ }
+ for(pass = 0; pass <= 1; pass++) {
+ tmp = 0;
+ while(tmp < ESHK(shopkeeper)->billct) {
+ bp = &bill[tmp];
+ if(!pass && !bp->useup) {
+ tmp++;
+ continue;
+ }
+ if(!dopayobj(bp)) return(1);
+ bill[tmp] = bill[--ESHK(shopkeeper)->billct];
+ }
+ }
+ pline("Thank you for shopping in %s's %s store!",
+ shkname(shopkeeper),
+ shopnam[rooms[ESHK(shopkeeper)->shoproom].rtype - 8]);
+ NOTANGRY(shopkeeper) = 1;
+ return(1);
+}
+
+/* return 1 if paid successfully */
+/* 0 if not enough money */
+/* -1 if object could not be found (but was paid) */
+static
+dopayobj(bp) register struct bill_x *bp; {
+register struct obj *obj;
+long ltmp;
+
+ /* find the object on one of the lists */
+ obj = bp_to_obj(bp);
+
+ if(!obj) {
+ impossible("Shopkeeper administration out of order.");
+ setpaid(); /* be nice to the player */
+ return(0);
+ }
+
+ if(!obj->unpaid && !bp->useup){
+ impossible("Paid object on bill??");
+ return(1);
+ }
+ obj->unpaid = 0;
+ ltmp = bp->price * bp->bquan;
+ if(ANGRY(shopkeeper)) ltmp += ltmp/3;
+ if(u.ugold < ltmp){
+ pline("You don't have gold enough to pay %s.",
+ doname(obj));
+ obj->unpaid = 1;
+ return(0);
+ }
+ pay(ltmp, shopkeeper);
+ pline("You bought %s for %ld gold piece%s.",
+ doname(obj), ltmp, plur(ltmp));
+ if(bp->useup) {
+ register struct obj *otmp = billobjs;
+ if(obj == billobjs)
+ billobjs = obj->nobj;
+ else {
+ while(otmp && otmp->nobj != obj) otmp = otmp->nobj;
+ if(otmp) otmp->nobj = obj->nobj;
+ else pline("Error in shopkeeper administration.");
+ }
+ free((char *) obj);
+ }
+ return(1);
+}
+
+/* routine called after dying (or quitting) with nonempty bill */
+paybill(){
+ if(shlevel == dlevel && shopkeeper && ESHK(shopkeeper)->billct){
+ addupbill();
+ if(total > u.ugold){
+ shopkeeper->mgold += u.ugold;
+ u.ugold = 0;
+ pline("%s comes and takes all your possessions.",
+ Monnam(shopkeeper));
+ } else {
+ u.ugold -= total;
+ shopkeeper->mgold += total;
+ pline("%s comes and takes the %ld zorkmids you owed him.",
+ Monnam(shopkeeper), total);
+ }
+ setpaid(); /* in case we create bones */
+ }
+}
+
+/* find obj on one of the lists */
+struct obj *
+bp_to_obj(bp)
+register struct bill_x *bp;
+{
+ register struct obj *obj;
+ register struct monst *mtmp;
+ register unsigned id = bp->bo_id;
+
+ if(bp->useup)
+ obj = o_on(id, billobjs);
+ else if(!(obj = o_on(id, invent)) &&
+ !(obj = o_on(id, fobj)) &&
+ !(obj = o_on(id, fcobj))) {
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(obj = o_on(id, mtmp->minvent))
+ break;
+ for(mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
+ if(obj = o_on(id, mtmp->minvent))
+ break;
+ }
+ return(obj);
+}
+
+static int getprice();
+
+/* called in hack.c when we pickup an object */
+addtobill(obj) register struct obj *obj; {
+register struct bill_x *bp;
+ if(!inshop() ||
+ (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) ||
+ (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y) ||
+ onbill(obj) /* perhaps we threw it away earlier */
+ ) return;
+ if(ESHK(shopkeeper)->billct == BILLSZ){
+ pline("You got that for free!");
+ return;
+ }
+ bp = &bill[ESHK(shopkeeper)->billct];
+ bp->bo_id = obj->o_id;
+ bp->bquan = obj->quan;
+ bp->useup = 0;
+ bp->price = getprice(obj);
+ ESHK(shopkeeper)->billct++;
+ obj->unpaid = 1;
+}
+
+splitbill(obj,otmp) register struct obj *obj, *otmp; {
+ /* otmp has been split off from obj */
+register struct bill_x *bp;
+register int tmp;
+ bp = onbill(obj);
+ if(!bp) {
+ impossible("splitbill: not on bill?");
+ return;
+ }
+ if(bp->bquan < otmp->quan) {
+ impossible("Negative quantity on bill??");
+ }
+ if(bp->bquan == otmp->quan) {
+ impossible("Zero quantity on bill??");
+ }
+ bp->bquan -= otmp->quan;
+
+ /* addtobill(otmp); */
+ if(ESHK(shopkeeper)->billct == BILLSZ) otmp->unpaid = 0;
+ else {
+ tmp = bp->price;
+ bp = &bill[ESHK(shopkeeper)->billct];
+ bp->bo_id = otmp->o_id;
+ bp->bquan = otmp->quan;
+ bp->useup = 0;
+ bp->price = tmp;
+ ESHK(shopkeeper)->billct++;
+ }
+}
+
+subfrombill(obj) register struct obj *obj; {
+long ltmp;
+register int tmp;
+register struct obj *otmp;
+register struct bill_x *bp;
+ if(!inshop() || (u.ux == ESHK(shopkeeper)->shk.x && u.uy == ESHK(shopkeeper)->shk.y) ||
+ (u.ux == ESHK(shopkeeper)->shd.x && u.uy == ESHK(shopkeeper)->shd.y))
+ return;
+ if((bp = onbill(obj)) != 0){
+ obj->unpaid = 0;
+ if(bp->bquan > obj->quan){
+ otmp = newobj(0);
+ *otmp = *obj;
+ bp->bo_id = otmp->o_id = flags.ident++;
+ otmp->quan = (bp->bquan -= obj->quan);
+ otmp->owt = 0; /* superfluous */
+ otmp->onamelth = 0;
+ bp->useup = 1;
+ otmp->nobj = billobjs;
+ billobjs = otmp;
+ return;
+ }
+ ESHK(shopkeeper)->billct--;
+ *bp = bill[ESHK(shopkeeper)->billct];
+ return;
+ }
+ if(obj->unpaid){
+ pline("%s didn't notice.", Monnam(shopkeeper));
+ obj->unpaid = 0;
+ return; /* %% */
+ }
+ /* he dropped something of his own - probably wants to sell it */
+ if(shopkeeper->msleep || shopkeeper->mfroz ||
+ inroom(shopkeeper->mx,shopkeeper->my) != ESHK(shopkeeper)->shoproom)
+ return;
+ if(ESHK(shopkeeper)->billct == BILLSZ ||
+ ((tmp = shtypes[rooms[ESHK(shopkeeper)->shoproom].rtype-8]) && tmp != obj->olet)
+ || index("_0", obj->olet)) {
+ pline("%s seems not interested.", Monnam(shopkeeper));
+ return;
+ }
+ ltmp = getprice(obj) * obj->quan;
+ if(ANGRY(shopkeeper)) {
+ ltmp /= 3;
+ NOTANGRY(shopkeeper) = 1;
+ } else ltmp /= 2;
+ if(ESHK(shopkeeper)->robbed){
+ if((ESHK(shopkeeper)->robbed -= ltmp) < 0)
+ ESHK(shopkeeper)->robbed = 0;
+pline("Thank you for your contribution to restock this recently plundered shop.");
+ return;
+ }
+ if(ltmp > shopkeeper->mgold)
+ ltmp = shopkeeper->mgold;
+ pay(-ltmp, shopkeeper);
+ if(!ltmp)
+ pline("%s gladly accepts %s but cannot pay you at present.",
+ Monnam(shopkeeper), doname(obj));
+ else
+ pline("You sold %s and got %ld gold piece%s.", doname(obj), ltmp,
+ plur(ltmp));
+}
+
+doinvbill(mode)
+int mode; /* 0: deliver count 1: paged */
+{
+ register struct bill_x *bp;
+ register struct obj *obj;
+ long totused, thisused;
+ char buf[BUFSZ];
+
+ if(mode == 0) {
+ register int cnt = 0;
+
+ if(shopkeeper)
+ for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++)
+ if(bp->useup ||
+ ((obj = bp_to_obj(bp)) && obj->quan < bp->bquan))
+ cnt++;
+ return(cnt);
+ }
+
+ if(!shopkeeper) {
+ impossible("doinvbill: no shopkeeper?");
+ return(0);
+ }
+
+ set_pager(0);
+ if(page_line("Unpaid articles already used up:") || page_line(""))
+ goto quit;
+
+ totused = 0;
+ for(bp = bill; bp - bill < ESHK(shopkeeper)->billct; bp++) {
+ obj = bp_to_obj(bp);
+ if(!obj) {
+ impossible("Bad shopkeeper administration.");
+ goto quit;
+ }
+ if(bp->useup || bp->bquan > obj->quan) {
+ register int cnt, oquan, uquan;
+
+ oquan = obj->quan;
+ uquan = (bp->useup ? bp->bquan : bp->bquan - oquan);
+ thisused = bp->price * uquan;
+ totused += thisused;
+ obj->quan = uquan; /* cheat doname */
+ (void) sprintf(buf, "x - %s", doname(obj));
+ obj->quan = oquan; /* restore value */
+ for(cnt = 0; buf[cnt]; cnt++);
+ while(cnt < 50)
+ buf[cnt++] = ' ';
+ (void) sprintf(&buf[cnt], " %5ld zorkmids", thisused);
+ if(page_line(buf))
+ goto quit;
+ }
+ }
+ (void) sprintf(buf, "Total:%50ld zorkmids", totused);
+ if(page_line("") || page_line(buf))
+ goto quit;
+ set_pager(1);
+ return(0);
+quit:
+ set_pager(2);
+ return(0);
+}
+
+static
+getprice(obj) register struct obj *obj; {
+register int tmp, ac;
+ static int realhunger();
+
+ switch(obj->olet){
+ case AMULET_SYM:
+ tmp = 10*rnd(500);
+ break;
+ case TOOL_SYM:
+ tmp = 10*rnd((obj->otyp == EXPENSIVE_CAMERA) ? 150 : 30);
+ break;
+ case RING_SYM:
+ tmp = 10*rnd(100);
+ break;
+ case WAND_SYM:
+ tmp = 10*rnd(100);
+ break;
+ case SCROLL_SYM:
+ tmp = 10*rnd(50);
+#ifdef MAIL
+ if(obj->otyp == SCR_MAIL)
+ tmp = rnd(5);
+#endif MAIL
+ break;
+ case POTION_SYM:
+ tmp = 10*rnd(50);
+ break;
+ case FOOD_SYM:
+ tmp = 10*rnd(5 + (2000/realhunger()));
+ break;
+ case GEM_SYM:
+ tmp = 10*rnd(20);
+ break;
+ case ARMOR_SYM:
+ ac = ARM_BONUS(obj);
+ if(ac <= -10) /* probably impossible */
+ ac = -9;
+ tmp = 100 + ac*ac*rnd(10+ac);
+ break;
+ case WEAPON_SYM:
+ if(obj->otyp < BOOMERANG)
+ tmp = 5*rnd(10);
+ else if(obj->otyp == LONG_SWORD ||
+ obj->otyp == TWO_HANDED_SWORD)
+ tmp = 10*rnd(150);
+ else tmp = 10*rnd(75);
+ break;
+ case CHAIN_SYM:
+ pline("Strange ..., carrying a chain?");
+ case BALL_SYM:
+ tmp = 10;
+ break;
+ default:
+ tmp = 10000;
+ }
+ return(tmp);
+}
+
+static
+realhunger(){ /* not completely foolproof */
+register tmp = u.uhunger;
+register struct obj *otmp = invent;
+ while(otmp){
+ if(otmp->olet == FOOD_SYM && !otmp->unpaid)
+ tmp += objects[otmp->otyp].nutrition;
+ otmp = otmp->nobj;
+ }
+ return((tmp <= 0) ? 1 : tmp);
+}
+
+shkcatch(obj)
+register struct obj *obj;
+{
+ register struct monst *shkp = shopkeeper;
+
+ if(u.uinshop && shkp && !shkp->mfroz && !shkp->msleep &&
+ u.dx && u.dy &&
+ inroom(u.ux+u.dx, u.uy+u.dy) + 1 == u.uinshop &&
+ shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y &&
+ u.ux == ESHK(shkp)->shd.x && u.uy == ESHK(shkp)->shd.y) {
+ pline("%s nimbly catches the %s.", Monnam(shkp), xname(obj));
+ obj->nobj = shkp->minvent;
+ shkp->minvent = obj;
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * shk_move: return 1: he moved 0: he didnt -1: let m_move do it
+ */
+shk_move(shkp)
+register struct monst *shkp;
+{
+ register struct monst *mtmp;
+ register struct permonst *mdat = shkp->data;
+ register xchar gx,gy,omx,omy,nx,ny,nix,niy;
+ register schar appr,i;
+ register int udist;
+ int z;
+ schar shkroom,chi,chcnt,cnt;
+ boolean uondoor, satdoor, avoid, badinv;
+ coord poss[9];
+ int info[9];
+ struct obj *ib = 0;
+
+ omx = shkp->mx;
+ omy = shkp->my;
+
+ if((udist = dist(omx,omy)) < 3) {
+ if(ANGRY(shkp)) {
+ (void) hitu(shkp, d(mdat->damn, mdat->damd)+1);
+ return(0);
+ }
+ if(ESHK(shkp)->following) {
+ if(strncmp(ESHK(shkp)->customer, plname, PL_NSIZ)){
+ pline("Hello %s! I was looking for %s.",
+ plname, ESHK(shkp)->customer);
+ ESHK(shkp)->following = 0;
+ return(0);
+ }
+ if(!ESHK(shkp)->robbed) { /* impossible? */
+ ESHK(shkp)->following = 0;
+ return(0);
+ }
+ if(moves > followmsg+4) {
+ pline("Hello %s! Didn't you forget to pay?",
+ plname);
+ followmsg = moves;
+ }
+ if(udist < 2)
+ return(0);
+ }
+ }
+
+ shkroom = inroom(omx,omy);
+ appr = 1;
+ gx = ESHK(shkp)->shk.x;
+ gy = ESHK(shkp)->shk.y;
+ satdoor = (gx == omx && gy == omy);
+ if(ESHK(shkp)->following || ((z = holetime()) >= 0 && z*z <= udist)){
+ gx = u.ux;
+ gy = u.uy;
+ if(shkroom < 0 || shkroom != inroom(u.ux,u.uy))
+ if(udist > 4)
+ return(-1); /* leave it to m_move */
+ } else if(ANGRY(shkp)) {
+ long saveBlind = Blind;
+ Blind = 0;
+ if(shkp->mcansee && !Invis && cansee(omx,omy)) {
+ gx = u.ux;
+ gy = u.uy;
+ }
+ Blind = saveBlind;
+ avoid = FALSE;
+ } else {
+#define GDIST(x,y) ((x-gx)*(x-gx)+(y-gy)*(y-gy))
+ if(Invis)
+ avoid = FALSE;
+ else {
+ uondoor = (u.ux == ESHK(shkp)->shd.x &&
+ u.uy == ESHK(shkp)->shd.y);
+ if(uondoor) {
+ if(ESHK(shkp)->billct)
+ pline("Hello %s! Will you please pay before leaving?",
+ plname);
+ badinv = (carrying(PICK_AXE) || carrying(ICE_BOX));
+ if(satdoor && badinv)
+ return(0);
+ avoid = !badinv;
+ } else {
+ avoid = (u.uinshop && dist(gx,gy) > 8);
+ badinv = FALSE;
+ }
+
+ if(((!ESHK(shkp)->robbed && !ESHK(shkp)->billct) || avoid)
+ && GDIST(omx,omy) < 3){
+ if(!badinv && !online(omx,omy))
+ return(0);
+ if(satdoor)
+ appr = gx = gy = 0;
+ }
+ }
+ }
+ if(omx == gx && omy == gy)
+ return(0);
+ if(shkp->mconf) {
+ avoid = FALSE;
+ appr = 0;
+ }
+ nix = omx;
+ niy = omy;
+ cnt = mfndpos(shkp,poss,info,ALLOW_SSM);
+ if(avoid && uondoor) { /* perhaps we cannot avoid him */
+ for(i=0; i<cnt; i++)
+ if(!(info[i] & NOTONL)) goto notonl_ok;
+ avoid = FALSE;
+ notonl_ok:
+ ;
+ }
+ chi = -1;
+ chcnt = 0;
+ for(i=0; i<cnt; i++){
+ nx = poss[i].x;
+ ny = poss[i].y;
+ if(levl[nx][ny].typ == ROOM
+ || shkroom != ESHK(shkp)->shoproom
+ || ESHK(shkp)->following) {
+#ifdef STUPID
+ /* cater for stupid compilers */
+ register int zz;
+#endif STUPID
+ if(uondoor && (ib = sobj_at(ICE_BOX, nx, ny))) {
+ nix = nx; niy = ny; chi = i; break;
+ }
+ if(avoid && (info[i] & NOTONL))
+ continue;
+ if((!appr && !rn2(++chcnt)) ||
+#ifdef STUPID
+ (appr && (zz = GDIST(nix,niy)) && zz > GDIST(nx,ny))
+#else
+ (appr && GDIST(nx,ny) < GDIST(nix,niy))
+#endif STUPID
+ ) {
+ nix = nx;
+ niy = ny;
+ chi = i;
+ }
+ }
+ }
+ if(nix != omx || niy != omy){
+ if(info[chi] & ALLOW_M){
+ mtmp = m_at(nix,niy);
+ if(hitmm(shkp,mtmp) == 1 && rn2(3) &&
+ hitmm(mtmp,shkp) == 2) return(2);
+ return(0);
+ } else if(info[chi] & ALLOW_U){
+ (void) hitu(shkp, d(mdat->damn, mdat->damd)+1);
+ return(0);
+ }
+ shkp->mx = nix;
+ shkp->my = niy;
+ pmon(shkp);
+ if(ib) {
+ freeobj(ib);
+ mpickobj(shkp, ib);
+ }
+ return(1);
+ }
+ return(0);
+}
+
+/* He is digging in the shop. */
+shopdig(fall)
+register int fall;
+{
+ if(!fall) {
+ if(u.utraptype == TT_PIT)
+ pline("\"Be careful, sir, or you might fall through the floor.\"");
+ else
+ pline("\"Please, do not damage the floor here.\"");
+ } else if(dist(shopkeeper->mx, shopkeeper->my) < 3) {
+ register struct obj *obj, *obj2;
+
+ pline("%s grabs your backpack!", shkname(shopkeeper));
+ for(obj = invent; obj; obj = obj2) {
+ obj2 = obj->nobj;
+ if(obj->owornmask) continue;
+ freeinv(obj);
+ obj->nobj = shopkeeper->minvent;
+ shopkeeper->minvent = obj;
+ if(obj->unpaid)
+ subfrombill(obj);
+ }
+ }
+}
+#endif QUEST
+
+online(x,y) {
+ return(x==u.ux || y==u.uy ||
+ (x-u.ux)*(x-u.ux) == (y-u.uy)*(y-u.uy));
+}
+
+/* Does this monster follow me downstairs? */
+follower(mtmp)
+register struct monst *mtmp;
+{
+ return( mtmp->mtame || index("1TVWZi&, ", mtmp->data->mlet)
+#ifndef QUEST
+ || (mtmp->isshk && ESHK(mtmp)->following)
+#endif QUEST
+ );
+}
diff --git a/games/hack/hack.shknam.c b/games/hack/hack.shknam.c
new file mode 100644
index 000000000000..9d4b860ff6b6
--- /dev/null
+++ b/games/hack/hack.shknam.c
@@ -0,0 +1,140 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.shknam.c - version 1.0.2 */
+
+#include "hack.h"
+
+char *shkliquors[] = {
+ /* Ukraine */
+ "Njezjin", "Tsjernigof", "Gomel", "Ossipewsk", "Gorlowka",
+ /* N. Russia */
+ "Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja",
+ "Narodnaja", "Kyzyl",
+ /* Silezie */
+ "Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice",
+ "Brzeg", "Krnov", "Hradec Kralove",
+ /* Schweiz */
+ "Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm",
+ "Flims", "Vals", "Schuls", "Zum Loch",
+ 0
+};
+
+char *shkbooks[] = {
+ /* Eire */
+ "Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon", "Lahinch",
+ "Loughrea", "Croagh", "Maumakeogh", "Ballyjamesduff",
+ "Kinnegad", "Lugnaquillia", "Enniscorthy", "Gweebarra",
+ "Kittamagh", "Nenagh", "Sneem", "Ballingeary", "Kilgarvan",
+ "Cahersiveen", "Glenbeigh", "Kilmihil", "Kiltamagh",
+ "Droichead Atha", "Inniscrone", "Clonegal", "Lisnaskea",
+ "Culdaff", "Dunfanaghy", "Inishbofin", "Kesh",
+ 0
+};
+
+char *shkarmors[] = {
+ /* Turquie */
+ "Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep",
+ "Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak",
+ "Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt",
+ "Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni",
+ "Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat",
+ "Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan",
+ 0
+};
+
+char *shkwands[] = {
+ /* Wales */
+ "Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach",
+ "Rhaeader", "Llandrindod", "Llanfair-ym-muallt",
+ "Y-Fenni", "Measteg", "Rhydaman", "Beddgelert",
+ "Curig", "Llanrwst", "Llanerchymedd", "Caergybi",
+ /* Scotland */
+ "Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar",
+ "Kerloch", "Beinn a Ghlo", "Drumnadrochit", "Morven",
+ "Uist", "Storr", "Sgurr na Ciche", "Cannich", "Gairloch",
+ "Kyleakin", "Dunvegan",
+ 0
+};
+
+char *shkrings[] = {
+ /* Hollandse familienamen */
+ "Feyfer", "Flugi", "Gheel", "Havic", "Haynin", "Hoboken",
+ "Imbyze", "Juyn", "Kinsky", "Massis", "Matray", "Moy",
+ "Olycan", "Sadelin", "Svaving", "Tapper", "Terwen", "Wirix",
+ "Ypey",
+ /* Skandinaviske navne */
+ "Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko",
+ "Enontekis", "Rovaniemi", "Avasaksa", "Haparanda",
+ "Lulea", "Gellivare", "Oeloe", "Kajaani", "Fauske",
+ 0
+};
+
+char *shkfoods[] = {
+ /* Indonesia */
+ "Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan",
+ "Bandjar", "Parbalingga", "Bojolali", "Sarangan",
+ "Ngebel", "Djombang", "Ardjawinangun", "Berbek",
+ "Papar", "Baliga", "Tjisolok", "Siboga", "Banjoewangi",
+ "Trenggalek", "Karangkobar", "Njalindoeng", "Pasawahan",
+ "Pameunpeuk", "Patjitan", "Kediri", "Pemboeang", "Tringanoe",
+ "Makin", "Tipor", "Semai", "Berhala", "Tegal", "Samoe",
+ 0
+};
+
+char *shkweapons[] = {
+ /* Perigord */
+ "Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard",
+ "Melac", "Neuvicq", "Vanzac", "Picq", "Urignac", "Corignac",
+ "Fleac", "Lonzac", "Vergt", "Queyssac", "Liorac", "Echourgnac",
+ "Cazelon", "Eypau", "Carignan", "Monbazillac", "Jonzac",
+ "Pons", "Jumilhac", "Fenouilledes", "Laguiolet", "Saujon",
+ "Eymoutiers", "Eygurande", "Eauze", "Labouheyre",
+ 0
+};
+
+char *shkgeneral[] = {
+ /* Suriname */
+ "Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi",
+ "Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo",
+ "Akalapi", "Sipaliwini",
+ /* Greenland */
+ "Annootok", "Upernavik", "Angmagssalik",
+ /* N. Canada */
+ "Aklavik", "Inuvik", "Tuktoyaktuk",
+ "Chicoutimi", "Ouiatchouane", "Chibougamau",
+ "Matagami", "Kipawa", "Kinojevis",
+ "Abitibi", "Maganasipi",
+ /* Iceland */
+ "Akureyri", "Kopasker", "Budereyri", "Akranes", "Bordeyri",
+ "Holmavik",
+ 0
+};
+
+struct shk_nx {
+ char x;
+ char **xn;
+} shk_nx[] = {
+ { POTION_SYM, shkliquors },
+ { SCROLL_SYM, shkbooks },
+ { ARMOR_SYM, shkarmors },
+ { WAND_SYM, shkwands },
+ { RING_SYM, shkrings },
+ { FOOD_SYM, shkfoods },
+ { WEAPON_SYM, shkweapons },
+ { 0, shkgeneral }
+};
+
+findname(nampt, let) char *nampt; char let; {
+register struct shk_nx *p = shk_nx;
+register char **q;
+register int i;
+ while(p->x && p->x != let) p++;
+ q = p->xn;
+ for(i=0; i<dlevel; i++) if(!q[i]){
+ /* Not enough names, try general name */
+ if(let) findname(nampt, 0);
+ else (void) strcpy(nampt, "Dirk");
+ return;
+ }
+ (void) strncpy(nampt, q[i], PL_NSIZ);
+ nampt[PL_NSIZ-1] = 0;
+}
diff --git a/games/hack/hack.steal.c b/games/hack/hack.steal.c
new file mode 100644
index 000000000000..f123d37f790b
--- /dev/null
+++ b/games/hack/hack.steal.c
@@ -0,0 +1,203 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.steal.c - version 1.0.3 */
+
+#include "hack.h"
+
+long /* actually returns something that fits in an int */
+somegold(){
+ return( (u.ugold < 100) ? u.ugold :
+ (u.ugold > 10000) ? rnd(10000) : rnd((int) u.ugold) );
+}
+
+stealgold(mtmp) register struct monst *mtmp; {
+register struct gold *gold = g_at(u.ux, u.uy);
+register long tmp;
+ if(gold && ( !u.ugold || gold->amount > u.ugold || !rn2(5))) {
+ mtmp->mgold += gold->amount;
+ freegold(gold);
+ if(Invisible) newsym(u.ux, u.uy);
+ pline("%s quickly snatches some gold from between your feet!",
+ Monnam(mtmp));
+ if(!u.ugold || !rn2(5)) {
+ rloc(mtmp);
+ mtmp->mflee = 1;
+ }
+ } else if(u.ugold) {
+ u.ugold -= (tmp = somegold());
+ pline("Your purse feels lighter.");
+ mtmp->mgold += tmp;
+ rloc(mtmp);
+ mtmp->mflee = 1;
+ flags.botl = 1;
+ }
+}
+
+/* steal armor after he finishes taking it off */
+unsigned stealoid; /* object to be stolen */
+unsigned stealmid; /* monster doing the stealing */
+stealarm(){
+ register struct monst *mtmp;
+ register struct obj *otmp;
+
+ for(otmp = invent; otmp; otmp = otmp->nobj)
+ if(otmp->o_id == stealoid) {
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->m_id == stealmid) {
+ if(dist(mtmp->mx,mtmp->my) < 3) {
+ freeinv(otmp);
+ pline("%s steals %s!", Monnam(mtmp), doname(otmp));
+ mpickobj(mtmp,otmp);
+ mtmp->mflee = 1;
+ rloc(mtmp);
+ }
+ break;
+ }
+ break;
+ }
+ stealoid = 0;
+}
+
+/* returns 1 when something was stolen */
+/* (or at least, when N should flee now) */
+/* avoid stealing the object stealoid */
+steal(mtmp)
+struct monst *mtmp;
+{
+ register struct obj *otmp;
+ register tmp;
+ register named = 0;
+
+ if(!invent){
+ if(Blind)
+ pline("Somebody tries to rob you, but finds nothing to steal.");
+ else
+ pline("%s tries to rob you, but she finds nothing to steal!",
+ Monnam(mtmp));
+ return(1); /* let her flee */
+ }
+ tmp = 0;
+ for(otmp = invent; otmp; otmp = otmp->nobj) if(otmp != uarm2)
+ tmp += ((otmp->owornmask & (W_ARMOR | W_RING)) ? 5 : 1);
+ tmp = rn2(tmp);
+ for(otmp = invent; otmp; otmp = otmp->nobj) if(otmp != uarm2)
+ if((tmp -= ((otmp->owornmask & (W_ARMOR | W_RING)) ? 5 : 1))
+ < 0) break;
+ if(!otmp) {
+ impossible("Steal fails!");
+ return(0);
+ }
+ if(otmp->o_id == stealoid)
+ return(0);
+ if((otmp->owornmask & (W_ARMOR | W_RING))){
+ switch(otmp->olet) {
+ case RING_SYM:
+ ringoff(otmp);
+ break;
+ case ARMOR_SYM:
+ if(multi < 0 || otmp == uarms){
+ setworn((struct obj *) 0, otmp->owornmask & W_ARMOR);
+ break;
+ }
+ { int curssv = otmp->cursed;
+ otmp->cursed = 0;
+ stop_occupation();
+ pline("%s seduces you and %s off your %s.",
+ Amonnam(mtmp, Blind ? "gentle" : "beautiful"),
+ otmp->cursed ? "helps you to take"
+ : "you start taking",
+ (otmp == uarmg) ? "gloves" :
+ (otmp == uarmh) ? "helmet" : "armor");
+ named++;
+ (void) armoroff(otmp);
+ otmp->cursed = curssv;
+ if(multi < 0){
+ extern char *nomovemsg;
+ extern int (*afternmv)();
+ /*
+ multi = 0;
+ nomovemsg = 0;
+ afternmv = 0;
+ */
+ stealoid = otmp->o_id;
+ stealmid = mtmp->m_id;
+ afternmv = stealarm;
+ return(0);
+ }
+ break;
+ }
+ default:
+ impossible("Tried to steal a strange worn thing.");
+ }
+ }
+ else if(otmp == uwep)
+ setuwep((struct obj *) 0);
+ if(otmp->olet == CHAIN_SYM) {
+ impossible("How come you are carrying that chain?");
+ }
+ if(Punished && otmp == uball){
+ Punished = 0;
+ freeobj(uchain);
+ free((char *) uchain);
+ uchain = (struct obj *) 0;
+ uball->spe = 0;
+ uball = (struct obj *) 0; /* superfluous */
+ }
+ freeinv(otmp);
+ pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp));
+ mpickobj(mtmp,otmp);
+ return((multi < 0) ? 0 : 1);
+}
+
+mpickobj(mtmp,otmp)
+register struct monst *mtmp;
+register struct obj *otmp;
+{
+ otmp->nobj = mtmp->minvent;
+ mtmp->minvent = otmp;
+}
+
+stealamulet(mtmp)
+register struct monst *mtmp;
+{
+ register struct obj *otmp;
+
+ for(otmp = invent; otmp; otmp = otmp->nobj) {
+ if(otmp->olet == AMULET_SYM) {
+ /* might be an imitation one */
+ if(otmp == uwep) setuwep((struct obj *) 0);
+ freeinv(otmp);
+ mpickobj(mtmp,otmp);
+ pline("%s stole %s!", Monnam(mtmp), doname(otmp));
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/* release the objects the killed animal has stolen */
+relobj(mtmp,show)
+register struct monst *mtmp;
+register show;
+{
+ register struct obj *otmp, *otmp2;
+
+ for(otmp = mtmp->minvent; otmp; otmp = otmp2){
+ otmp->ox = mtmp->mx;
+ otmp->oy = mtmp->my;
+ otmp2 = otmp->nobj;
+ otmp->nobj = fobj;
+ fobj = otmp;
+ stackobj(fobj);
+ if(show & cansee(mtmp->mx,mtmp->my))
+ atl(otmp->ox,otmp->oy,otmp->olet);
+ }
+ mtmp->minvent = (struct obj *) 0;
+ if(mtmp->mgold || mtmp->data->mlet == 'L') {
+ register long tmp;
+
+ tmp = (mtmp->mgold > 10000) ? 10000 : mtmp->mgold;
+ mkgold((long)(tmp + d(dlevel,30)), mtmp->mx, mtmp->my);
+ if(show & cansee(mtmp->mx,mtmp->my))
+ atl(mtmp->mx,mtmp->my,'$');
+ }
+}
diff --git a/games/hack/hack.termcap.c b/games/hack/hack.termcap.c
new file mode 100644
index 000000000000..e94d6729c17b
--- /dev/null
+++ b/games/hack/hack.termcap.c
@@ -0,0 +1,276 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.termcap.c - version 1.0.3 */
+
+#include <stdio.h>
+#include "config.h" /* for ROWNO and COLNO */
+#include "def.flag.h" /* for flags.nonull */
+extern char *tgetstr(), *tgoto(), *getenv();
+extern long *alloc();
+
+#ifndef lint
+extern /* it is defined in libtermlib (libtermcap) */
+#endif lint
+ short ospeed; /* terminal baudrate; used by tputs */
+static char tbuf[512];
+static char *HO, *CL, *CE, *UP, *CM, *ND, *XD, *BC, *SO, *SE, *TI, *TE;
+static char *VS, *VE;
+static int SG;
+static char PC = '\0';
+char *CD; /* tested in pri.c: docorner() */
+int CO, LI; /* used in pri.c and whatis.c */
+
+startup()
+{
+ register char *term;
+ register char *tptr;
+ char *tbufptr, *pc;
+
+ tptr = (char *) alloc(1024);
+
+ tbufptr = tbuf;
+ if(!(term = getenv("TERM")))
+ error("Can't get TERM.");
+ if(!strncmp(term, "5620", 4))
+ flags.nonull = 1; /* this should be a termcap flag */
+ if(tgetent(tptr, term) < 1)
+ error("Unknown terminal type: %s.", term);
+ if(pc = tgetstr("pc", &tbufptr))
+ PC = *pc;
+ if(!(BC = tgetstr("bc", &tbufptr))) {
+ if(!tgetflag("bs"))
+ error("Terminal must backspace.");
+ BC = tbufptr;
+ tbufptr += 2;
+ *BC = '\b';
+ }
+ HO = tgetstr("ho", &tbufptr);
+ CO = tgetnum("co");
+ LI = tgetnum("li");
+ if(CO < COLNO || LI < ROWNO+2)
+ setclipped();
+ if(!(CL = tgetstr("cl", &tbufptr)))
+ error("Hack needs CL.");
+ ND = tgetstr("nd", &tbufptr);
+ if(tgetflag("os"))
+ error("Hack can't have OS.");
+ CE = tgetstr("ce", &tbufptr);
+ UP = tgetstr("up", &tbufptr);
+ /* It seems that xd is no longer supported, and we should use
+ a linefeed instead; unfortunately this requires resetting
+ CRMOD, and many output routines will have to be modified
+ slightly. Let's leave that till the next release. */
+ XD = tgetstr("xd", &tbufptr);
+/* not: XD = tgetstr("do", &tbufptr); */
+ if(!(CM = tgetstr("cm", &tbufptr))) {
+ if(!UP && !HO)
+ error("Hack needs CM or UP or HO.");
+ printf("Playing hack on terminals without cm is suspect...\n");
+ getret();
+ }
+ SO = tgetstr("so", &tbufptr);
+ SE = tgetstr("se", &tbufptr);
+ SG = tgetnum("sg"); /* -1: not fnd; else # of spaces left by so */
+ if(!SO || !SE || (SG > 0)) SO = SE = 0;
+ CD = tgetstr("cd", &tbufptr);
+ set_whole_screen(); /* uses LI and CD */
+ if(tbufptr-tbuf > sizeof(tbuf)) error("TERMCAP entry too big...\n");
+ free(tptr);
+}
+
+start_screen()
+{
+ xputs(TI);
+ xputs(VS);
+}
+
+end_screen()
+{
+ xputs(VE);
+ xputs(TE);
+}
+
+/* Cursor movements */
+extern xchar curx, cury;
+
+curs(x, y)
+register int x, y; /* not xchar: perhaps xchar is unsigned and
+ curx-x would be unsigned as well */
+{
+
+ if (y == cury && x == curx)
+ return;
+ if(!ND && (curx != x || x <= 3)) { /* Extremely primitive */
+ cmov(x, y); /* bunker!wtm */
+ return;
+ }
+ if(abs(cury-y) <= 3 && abs(curx-x) <= 3)
+ nocmov(x, y);
+ else if((x <= 3 && abs(cury-y)<= 3) || (!CM && x<abs(curx-x))) {
+ (void) putchar('\r');
+ curx = 1;
+ nocmov(x, y);
+ } else if(!CM) {
+ nocmov(x, y);
+ } else
+ cmov(x, y);
+}
+
+nocmov(x, y)
+{
+ if (cury > y) {
+ if(UP) {
+ while (cury > y) { /* Go up. */
+ xputs(UP);
+ cury--;
+ }
+ } else if(CM) {
+ cmov(x, y);
+ } else if(HO) {
+ home();
+ curs(x, y);
+ } /* else impossible("..."); */
+ } else if (cury < y) {
+ if(XD) {
+ while(cury < y) {
+ xputs(XD);
+ cury++;
+ }
+ } else if(CM) {
+ cmov(x, y);
+ } else {
+ while(cury < y) {
+ xputc('\n');
+ curx = 1;
+ cury++;
+ }
+ }
+ }
+ if (curx < x) { /* Go to the right. */
+ if(!ND) cmov(x, y); else /* bah */
+ /* should instead print what is there already */
+ while (curx < x) {
+ xputs(ND);
+ curx++;
+ }
+ } else if (curx > x) {
+ while (curx > x) { /* Go to the left. */
+ xputs(BC);
+ curx--;
+ }
+ }
+}
+
+cmov(x, y)
+register x, y;
+{
+ xputs(tgoto(CM, x-1, y-1));
+ cury = y;
+ curx = x;
+}
+
+xputc(c) char c; {
+ (void) fputc(c, stdout);
+}
+
+xputs(s) char *s; {
+ tputs(s, 1, xputc);
+}
+
+cl_end() {
+ if(CE)
+ xputs(CE);
+ else { /* no-CE fix - free after Harold Rynes */
+ /* this looks terrible, especially on a slow terminal
+ but is better than nothing */
+ register cx = curx, cy = cury;
+
+ while(curx < COLNO) {
+ xputc(' ');
+ curx++;
+ }
+ curs(cx, cy);
+ }
+}
+
+clear_screen() {
+ xputs(CL);
+ curx = cury = 1;
+}
+
+home()
+{
+ if(HO)
+ xputs(HO);
+ else if(CM)
+ xputs(tgoto(CM, 0, 0));
+ else
+ curs(1, 1); /* using UP ... */
+ curx = cury = 1;
+}
+
+standoutbeg()
+{
+ if(SO) xputs(SO);
+}
+
+standoutend()
+{
+ if(SE) xputs(SE);
+}
+
+backsp()
+{
+ xputs(BC);
+ curx--;
+}
+
+bell()
+{
+ (void) putchar('\007'); /* curx does not change */
+ (void) fflush(stdout);
+}
+
+static short tmspc10[] = { /* from termcap */
+ 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5
+};
+
+delay_output() {
+ /* delay 50 ms - could also use a 'nap'-system call */
+ /* BUG: if the padding character is visible, as it is on the 5620
+ then this looks terrible. */
+ if(!flags.nonull)
+ tputs("50", 1, xputc);
+
+ /* cbosgd!cbcephus!pds for SYS V R2 */
+ /* is this terminfo, or what? */
+ /* tputs("$<50>", 1, xputc); */
+
+ else if(ospeed > 0 || ospeed < SIZE(tmspc10)) if(CM) {
+ /* delay by sending cm(here) an appropriate number of times */
+ register int cmlen = strlen(tgoto(CM, curx-1, cury-1));
+ register int i = 500 + tmspc10[ospeed]/2;
+
+ while(i > 0) {
+ cmov(curx, cury);
+ i -= cmlen*tmspc10[ospeed];
+ }
+ }
+}
+
+cl_eos() /* free after Robert Viduya */
+{ /* must only be called with curx = 1 */
+
+ if(CD)
+ xputs(CD);
+ else {
+ register int cx = curx, cy = cury;
+ while(cury <= LI-2) {
+ cl_end();
+ xputc('\n');
+ curx = 1;
+ cury++;
+ }
+ cl_end();
+ curs(cx, cy);
+ }
+}
diff --git a/games/hack/hack.timeout.c b/games/hack/hack.timeout.c
new file mode 100644
index 000000000000..d828297684ff
--- /dev/null
+++ b/games/hack/hack.timeout.c
@@ -0,0 +1,62 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.timeout.c - version 1.0.3 */
+
+#include "hack.h"
+
+timeout(){
+register struct prop *upp;
+ if(Stoned) stoned_dialogue();
+ for(upp = u.uprops; upp < u.uprops+SIZE(u.uprops); upp++)
+ if((upp->p_flgs & TIMEOUT) && !--upp->p_flgs) {
+ if(upp->p_tofn) (*upp->p_tofn)();
+ else switch(upp - u.uprops){
+ case STONED:
+ killer = "cockatrice";
+ done("died");
+ break;
+ case SICK:
+ pline("You die because of food poisoning.");
+ killer = u.usick_cause;
+ done("died");
+ break;
+ case FAST:
+ pline("You feel yourself slowing down.");
+ break;
+ case CONFUSION:
+ pline("You feel less confused now.");
+ break;
+ case BLIND:
+ pline("You can see again.");
+ setsee();
+ break;
+ case INVIS:
+ on_scr(u.ux,u.uy);
+ pline("You are no longer invisible.");
+ break;
+ case WOUNDED_LEGS:
+ heal_legs();
+ break;
+ }
+ }
+}
+
+/* He is being petrified - dialogue by inmet!tower */
+char *stoned_texts[] = {
+ "You are slowing down.", /* 5 */
+ "Your limbs are stiffening.", /* 4 */
+ "Your limbs have turned to stone.", /* 3 */
+ "You have turned to stone.", /* 2 */
+ "You are a statue." /* 1 */
+};
+
+stoned_dialogue()
+{
+ register long i = (Stoned & TIMEOUT);
+
+ if(i > 0 && i <= SIZE(stoned_texts))
+ pline(stoned_texts[SIZE(stoned_texts) - i]);
+ if(i == 5)
+ Fast = 0;
+ if(i == 3)
+ nomul(-3);
+}
diff --git a/games/hack/hack.topl.c b/games/hack/hack.topl.c
new file mode 100644
index 000000000000..13a033d19c5e
--- /dev/null
+++ b/games/hack/hack.topl.c
@@ -0,0 +1,192 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.topl.c - version 1.0.2 */
+
+#include "hack.h"
+#include <stdio.h>
+extern char *eos();
+extern int CO;
+
+char toplines[BUFSZ];
+xchar tlx, tly; /* set by pline; used by addtopl */
+
+struct topl {
+ struct topl *next_topl;
+ char *topl_text;
+} *old_toplines, *last_redone_topl;
+#define OTLMAX 20 /* max nr of old toplines remembered */
+
+doredotopl(){
+ if(last_redone_topl)
+ last_redone_topl = last_redone_topl->next_topl;
+ if(!last_redone_topl)
+ last_redone_topl = old_toplines;
+ if(last_redone_topl){
+ (void) strcpy(toplines, last_redone_topl->topl_text);
+ }
+ redotoplin();
+ return(0);
+}
+
+redotoplin() {
+ home();
+ if(index(toplines, '\n')) cl_end();
+ putstr(toplines);
+ cl_end();
+ tlx = curx;
+ tly = cury;
+ flags.toplin = 1;
+ if(tly > 1)
+ more();
+}
+
+remember_topl() {
+register struct topl *tl;
+register int cnt = OTLMAX;
+ if(last_redone_topl &&
+ !strcmp(toplines, last_redone_topl->topl_text)) return;
+ if(old_toplines &&
+ !strcmp(toplines, old_toplines->topl_text)) return;
+ last_redone_topl = 0;
+ tl = (struct topl *)
+ alloc((unsigned)(strlen(toplines) + sizeof(struct topl) + 1));
+ tl->next_topl = old_toplines;
+ tl->topl_text = (char *)(tl + 1);
+ (void) strcpy(tl->topl_text, toplines);
+ old_toplines = tl;
+ while(cnt && tl){
+ cnt--;
+ tl = tl->next_topl;
+ }
+ if(tl && tl->next_topl){
+ free((char *) tl->next_topl);
+ tl->next_topl = 0;
+ }
+}
+
+addtopl(s) char *s; {
+ curs(tlx,tly);
+ if(tlx + strlen(s) > CO) putsym('\n');
+ putstr(s);
+ tlx = curx;
+ tly = cury;
+ flags.toplin = 1;
+}
+
+xmore(s)
+char *s; /* allowed chars besides space/return */
+{
+ if(flags.toplin) {
+ curs(tlx, tly);
+ if(tlx + 8 > CO) putsym('\n'), tly++;
+ }
+
+ if(flags.standout)
+ standoutbeg();
+ putstr("--More--");
+ if(flags.standout)
+ standoutend();
+
+ xwaitforspace(s);
+ if(flags.toplin && tly > 1) {
+ home();
+ cl_end();
+ docorner(1, tly-1);
+ }
+ flags.toplin = 0;
+}
+
+more(){
+ xmore("");
+}
+
+cmore(s)
+register char *s;
+{
+ xmore(s);
+}
+
+clrlin(){
+ if(flags.toplin) {
+ home();
+ cl_end();
+ if(tly > 1) docorner(1, tly-1);
+ remember_topl();
+ }
+ flags.toplin = 0;
+}
+
+/*VARARGS1*/
+pline(line,arg1,arg2,arg3,arg4,arg5,arg6)
+register char *line,*arg1,*arg2,*arg3,*arg4,*arg5,*arg6;
+{
+ char pbuf[BUFSZ];
+ register char *bp = pbuf, *tl;
+ register int n,n0;
+
+ if(!line || !*line) return;
+ if(!index(line, '%')) (void) strcpy(pbuf,line); else
+ (void) sprintf(pbuf,line,arg1,arg2,arg3,arg4,arg5,arg6);
+ if(flags.toplin == 1 && !strcmp(pbuf, toplines)) return;
+ nscr(); /* %% */
+
+ /* If there is room on the line, print message on same line */
+ /* But messages like "You die..." deserve their own line */
+ n0 = strlen(bp);
+ if(flags.toplin == 1 && tly == 1 &&
+ n0 + strlen(toplines) + 3 < CO-8 && /* leave room for --More-- */
+ strncmp(bp, "You ", 4)) {
+ (void) strcat(toplines, " ");
+ (void) strcat(toplines, bp);
+ tlx += 2;
+ addtopl(bp);
+ return;
+ }
+ if(flags.toplin == 1) more();
+ remember_topl();
+ toplines[0] = 0;
+ while(n0){
+ if(n0 >= CO){
+ /* look for appropriate cut point */
+ n0 = 0;
+ for(n = 0; n < CO; n++) if(bp[n] == ' ')
+ n0 = n;
+ if(!n0) for(n = 0; n < CO-1; n++)
+ if(!letter(bp[n])) n0 = n;
+ if(!n0) n0 = CO-2;
+ }
+ (void) strncpy((tl = eos(toplines)), bp, n0);
+ tl[n0] = 0;
+ bp += n0;
+
+ /* remove trailing spaces, but leave one */
+ while(n0 > 1 && tl[n0-1] == ' ' && tl[n0-2] == ' ')
+ tl[--n0] = 0;
+
+ n0 = strlen(bp);
+ if(n0 && tl[0]) (void) strcat(tl, "\n");
+ }
+ redotoplin();
+}
+
+putsym(c) char c; {
+ switch(c) {
+ case '\b':
+ backsp();
+ return;
+ case '\n':
+ curx = 1;
+ cury++;
+ if(cury > tly) tly = cury;
+ break;
+ default:
+ if(curx == CO)
+ putsym('\n'); /* 1 <= curx <= CO; avoid CO */
+ else
+ curx++;
+ }
+ (void) putchar(c);
+}
+
+putstr(s) register char *s; {
+ while(*s) putsym(*s++);
+}
diff --git a/games/hack/hack.track.c b/games/hack/hack.track.c
new file mode 100644
index 000000000000..6b41c2cdb118
--- /dev/null
+++ b/games/hack/hack.track.c
@@ -0,0 +1,38 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.track.c - version 1.0.2 */
+
+#include "hack.h"
+
+#define UTSZ 50
+
+coord utrack[UTSZ];
+int utcnt = 0;
+int utpnt = 0;
+
+initrack(){
+ utcnt = utpnt = 0;
+}
+
+/* add to track */
+settrack(){
+ if(utcnt < UTSZ) utcnt++;
+ if(utpnt == UTSZ) utpnt = 0;
+ utrack[utpnt].x = u.ux;
+ utrack[utpnt].y = u.uy;
+ utpnt++;
+}
+
+coord *
+gettrack(x,y) register x,y; {
+register int i,cnt,dist;
+coord tc;
+ cnt = utcnt;
+ for(i = utpnt-1; cnt--; i--){
+ if(i == -1) i = UTSZ-1;
+ tc = utrack[i];
+ dist = (x-tc.x)*(x-tc.x) + (y-tc.y)*(y-tc.y);
+ if(dist < 3)
+ return(dist ? &(utrack[i]) : 0);
+ }
+ return(0);
+}
diff --git a/games/hack/hack.trap.c b/games/hack/hack.trap.c
new file mode 100644
index 000000000000..e426dd1ccb98
--- /dev/null
+++ b/games/hack/hack.trap.c
@@ -0,0 +1,447 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.trap.c - version 1.0.3 */
+
+#include "hack.h"
+
+extern struct monst *makemon();
+
+char vowels[] = "aeiou";
+
+char *traps[] = {
+ " bear trap",
+ "n arrow trap",
+ " dart trap",
+ " trapdoor",
+ " teleportation trap",
+ " pit",
+ " sleeping gas trap",
+ " piercer",
+ " mimic"
+};
+
+struct trap *
+maketrap(x,y,typ)
+register x,y,typ;
+{
+ register struct trap *ttmp;
+
+ ttmp = newtrap();
+ ttmp->ttyp = typ;
+ ttmp->tseen = 0;
+ ttmp->once = 0;
+ ttmp->tx = x;
+ ttmp->ty = y;
+ ttmp->ntrap = ftrap;
+ ftrap = ttmp;
+ return(ttmp);
+}
+
+dotrap(trap) register struct trap *trap; {
+ register int ttype = trap->ttyp;
+
+ nomul(0);
+ if(trap->tseen && !rn2(5) && ttype != PIT)
+ pline("You escape a%s.", traps[ttype]);
+ else {
+ trap->tseen = 1;
+ switch(ttype) {
+ case SLP_GAS_TRAP:
+ pline("A cloud of gas puts you to sleep!");
+ nomul(-rnd(25));
+ break;
+ case BEAR_TRAP:
+ if(Levitation) {
+ pline("You float over a bear trap.");
+ break;
+ }
+ u.utrap = 4 + rn2(4);
+ u.utraptype = TT_BEARTRAP;
+ pline("A bear trap closes on your foot!");
+ break;
+ case PIERC:
+ deltrap(trap);
+ if(makemon(PM_PIERCER,u.ux,u.uy)) {
+ pline("A piercer suddenly drops from the ceiling!");
+ if(uarmh)
+ pline("Its blow glances off your helmet.");
+ else
+ (void) thitu(3,d(4,6),"falling piercer");
+ }
+ break;
+ case ARROW_TRAP:
+ pline("An arrow shoots out at you!");
+ if(!thitu(8,rnd(6),"arrow")){
+ mksobj_at(ARROW, u.ux, u.uy);
+ fobj->quan = 1;
+ }
+ break;
+ case TRAPDOOR:
+ if(!xdnstair) {
+pline("A trap door in the ceiling opens and a rock falls on your head!");
+if(uarmh) pline("Fortunately, you are wearing a helmet!");
+ losehp(uarmh ? 2 : d(2,10),"falling rock");
+ mksobj_at(ROCK, u.ux, u.uy);
+ fobj->quan = 1;
+ stackobj(fobj);
+ if(Invisible) newsym(u.ux, u.uy);
+ } else {
+ register int newlevel = dlevel + 1;
+ while(!rn2(4) && newlevel < 29)
+ newlevel++;
+ pline("A trap door opens up under you!");
+ if(Levitation || u.ustuck) {
+ pline("For some reason you don't fall in.");
+ break;
+ }
+
+ goto_level(newlevel, FALSE);
+ }
+ break;
+ case DART_TRAP:
+ pline("A little dart shoots out at you!");
+ if(thitu(7,rnd(3),"little dart")) {
+ if(!rn2(6))
+ poisoned("dart","poison dart");
+ } else {
+ mksobj_at(DART, u.ux, u.uy);
+ fobj->quan = 1;
+ }
+ break;
+ case TELEP_TRAP:
+ if(trap->once) {
+ deltrap(trap);
+ newsym(u.ux,u.uy);
+ vtele();
+ } else {
+ newsym(u.ux,u.uy);
+ tele();
+ }
+ break;
+ case PIT:
+ if(Levitation) {
+ pline("A pit opens up under you!");
+ pline("You don't fall in!");
+ break;
+ }
+ pline("You fall into a pit!");
+ u.utrap = rn1(6,2);
+ u.utraptype = TT_PIT;
+ losehp(rnd(6),"fall into a pit");
+ selftouch("Falling, you");
+ break;
+ default:
+ impossible("You hit a trap of type %u", trap->ttyp);
+ }
+ }
+}
+
+mintrap(mtmp) register struct monst *mtmp; {
+ register struct trap *trap = t_at(mtmp->mx, mtmp->my);
+ register int wasintrap = mtmp->mtrapped;
+
+ if(!trap) {
+ mtmp->mtrapped = 0; /* perhaps teleported? */
+ } else if(wasintrap) {
+ if(!rn2(40)) mtmp->mtrapped = 0;
+ } else {
+ register int tt = trap->ttyp;
+ int in_sight = cansee(mtmp->mx,mtmp->my);
+ extern char mlarge[];
+
+ if(mtmp->mtrapseen & (1 << tt)) {
+ /* he has been in such a trap - perhaps he escapes */
+ if(rn2(4)) return(0);
+ }
+ mtmp->mtrapseen |= (1 << tt);
+ switch (tt) {
+ case BEAR_TRAP:
+ if(index(mlarge, mtmp->data->mlet)) {
+ if(in_sight)
+ pline("%s is caught in a bear trap!",
+ Monnam(mtmp));
+ else
+ if(mtmp->data->mlet == 'o')
+ pline("You hear the roaring of an angry bear!");
+ mtmp->mtrapped = 1;
+ }
+ break;
+ case PIT:
+ /* there should be a mtmp/data -> floating */
+ if(!index("EywBfk'& ", mtmp->data->mlet)) { /* ab */
+ mtmp->mtrapped = 1;
+ if(in_sight)
+ pline("%s falls in a pit!", Monnam(mtmp));
+ }
+ break;
+ case SLP_GAS_TRAP:
+ if(!mtmp->msleep && !mtmp->mfroz) {
+ mtmp->msleep = 1;
+ if(in_sight)
+ pline("%s suddenly falls asleep!",
+ Monnam(mtmp));
+ }
+ break;
+ case TELEP_TRAP:
+ rloc(mtmp);
+ if(in_sight && !cansee(mtmp->mx,mtmp->my))
+ pline("%s suddenly disappears!",
+ Monnam(mtmp));
+ break;
+ case ARROW_TRAP:
+ if(in_sight) {
+ pline("%s is hit by an arrow!",
+ Monnam(mtmp));
+ }
+ mtmp->mhp -= 3;
+ break;
+ case DART_TRAP:
+ if(in_sight) {
+ pline("%s is hit by a dart!",
+ Monnam(mtmp));
+ }
+ mtmp->mhp -= 2;
+ /* not mondied here !! */
+ break;
+ case TRAPDOOR:
+ if(!xdnstair) {
+ mtmp->mhp -= 10;
+ if(in_sight)
+pline("A trap door in the ceiling opens and a rock hits %s!", monnam(mtmp));
+ break;
+ }
+ if(mtmp->data->mlet != 'w'){
+ fall_down(mtmp);
+ if(in_sight)
+ pline("Suddenly, %s disappears out of sight.", monnam(mtmp));
+ return(2); /* no longer on this level */
+ }
+ break;
+ case PIERC:
+ break;
+ default:
+ impossible("Some monster encountered a strange trap.");
+ }
+ }
+ return(mtmp->mtrapped);
+}
+
+selftouch(arg) char *arg; {
+ if(uwep && uwep->otyp == DEAD_COCKATRICE){
+ pline("%s touch the dead cockatrice.", arg);
+ pline("You turn to stone.");
+ killer = objects[uwep->otyp].oc_name;
+ done("died");
+ }
+}
+
+float_up(){
+ if(u.utrap) {
+ if(u.utraptype == TT_PIT) {
+ u.utrap = 0;
+ pline("You float up, out of the pit!");
+ } else {
+ pline("You float up, only your leg is still stuck.");
+ }
+ } else
+ pline("You start to float in the air!");
+}
+
+float_down(){
+ register struct trap *trap;
+ pline("You float gently to the ground.");
+ if(trap = t_at(u.ux,u.uy))
+ switch(trap->ttyp) {
+ case PIERC:
+ break;
+ case TRAPDOOR:
+ if(!xdnstair || u.ustuck) break;
+ /* fall into next case */
+ default:
+ dotrap(trap);
+ }
+ pickup(1);
+}
+
+vtele() {
+#include "def.mkroom.h"
+ register struct mkroom *croom;
+ for(croom = &rooms[0]; croom->hx >= 0; croom++)
+ if(croom->rtype == VAULT) {
+ register x,y;
+
+ x = rn2(2) ? croom->lx : croom->hx;
+ y = rn2(2) ? croom->ly : croom->hy;
+ if(teleok(x,y)) {
+ teleds(x,y);
+ return;
+ }
+ }
+ tele();
+}
+
+tele() {
+ extern coord getpos();
+ coord cc;
+ register int nux,nuy;
+
+ if(Teleport_control) {
+ pline("To what position do you want to be teleported?");
+ cc = getpos(1, "the desired position"); /* 1: force valid */
+ /* possible extensions: introduce a small error if
+ magic power is low; allow transfer to solid rock */
+ if(teleok(cc.x, cc.y)){
+ teleds(cc.x, cc.y);
+ return;
+ }
+ pline("Sorry ...");
+ }
+ do {
+ nux = rnd(COLNO-1);
+ nuy = rn2(ROWNO);
+ } while(!teleok(nux, nuy));
+ teleds(nux, nuy);
+}
+
+teleds(nux, nuy)
+register int nux,nuy;
+{
+ if(Punished) unplacebc();
+ unsee();
+ u.utrap = 0;
+ u.ustuck = 0;
+ u.ux = nux;
+ u.uy = nuy;
+ setsee();
+ if(Punished) placebc(1);
+ if(u.uswallow){
+ u.uswldtim = u.uswallow = 0;
+ docrt();
+ }
+ nomul(0);
+ if(levl[nux][nuy].typ == POOL && !Levitation)
+ drown();
+ (void) inshop();
+ pickup(1);
+ if(!Blind) read_engr_at(u.ux,u.uy);
+}
+
+teleok(x,y) register int x,y; { /* might throw him into a POOL */
+ return( isok(x,y) && !IS_ROCK(levl[x][y].typ) && !m_at(x,y) &&
+ !sobj_at(ENORMOUS_ROCK,x,y) && !t_at(x,y)
+ );
+ /* Note: gold is permitted (because of vaults) */
+}
+
+dotele() {
+ extern char pl_character[];
+
+ if(
+#ifdef WIZARD
+ !wizard &&
+#endif WIZARD
+ (!Teleportation || u.ulevel < 6 ||
+ (pl_character[0] != 'W' && u.ulevel < 10))) {
+ pline("You are not able to teleport at will.");
+ return(0);
+ }
+ if(u.uhunger <= 100 || u.ustr < 6) {
+ pline("You miss the strength for a teleport spell.");
+ return(1);
+ }
+ tele();
+ morehungry(100);
+ return(1);
+}
+
+placebc(attach) int attach; {
+ if(!uchain || !uball){
+ impossible("Where are your chain and ball??");
+ return;
+ }
+ uball->ox = uchain->ox = u.ux;
+ uball->oy = uchain->oy = u.uy;
+ if(attach){
+ uchain->nobj = fobj;
+ fobj = uchain;
+ if(!carried(uball)){
+ uball->nobj = fobj;
+ fobj = uball;
+ }
+ }
+}
+
+unplacebc(){
+ if(!carried(uball)){
+ freeobj(uball);
+ unpobj(uball);
+ }
+ freeobj(uchain);
+ unpobj(uchain);
+}
+
+level_tele() {
+register int newlevel;
+ if(Teleport_control) {
+ char buf[BUFSZ];
+
+ do {
+ pline("To what level do you want to teleport? [type a number] ");
+ getlin(buf);
+ } while(!digit(buf[0]) && (buf[0] != '-' || !digit(buf[1])));
+ newlevel = atoi(buf);
+ } else {
+ newlevel = 5 + rn2(20); /* 5 - 24 */
+ if(dlevel == newlevel)
+ if(!xdnstair) newlevel--; else newlevel++;
+ }
+ if(newlevel >= 30) {
+ if(newlevel > MAXLEVEL) newlevel = MAXLEVEL;
+ pline("You arrive at the center of the earth ...");
+ pline("Unfortunately it is here that hell is located.");
+ if(Fire_resistance) {
+ pline("But the fire doesn't seem to harm you.");
+ } else {
+ pline("You burn to a crisp.");
+ dlevel = maxdlevel = newlevel;
+ killer = "visit to the hell";
+ done("burned");
+ }
+ }
+ if(newlevel < 0) {
+ newlevel = 0;
+ pline("You are now high above the clouds ...");
+ if(Levitation) {
+ pline("You float gently down to earth.");
+ done("escaped");
+ }
+ pline("Unfortunately, you don't know how to fly.");
+ pline("You fall down a few thousand feet and break your neck.");
+ dlevel = 0;
+ killer = "fall";
+ done("died");
+ }
+
+ goto_level(newlevel, FALSE); /* calls done("escaped") if newlevel==0 */
+}
+
+drown()
+{
+ pline("You fall into a pool!");
+ pline("You can't swim!");
+ if(rn2(3) < u.uluck+2) {
+ /* most scrolls become unreadable */
+ register struct obj *obj;
+
+ for(obj = invent; obj; obj = obj->nobj)
+ if(obj->olet == SCROLL_SYM && rn2(12) > u.uluck)
+ obj->otyp = SCR_BLANK_PAPER;
+ /* we should perhaps merge these scrolls ? */
+
+ pline("You attempt a teleport spell."); /* utcsri!carroll */
+ (void) dotele();
+ if(levl[u.ux][u.uy].typ != POOL) return;
+ }
+ pline("You drown ...");
+ killer = "pool of water";
+ done("drowned");
+}
diff --git a/games/hack/hack.tty.c b/games/hack/hack.tty.c
new file mode 100644
index 000000000000..68fefc4e3958
--- /dev/null
+++ b/games/hack/hack.tty.c
@@ -0,0 +1,338 @@
+/*-
+ * Copyright (c) 1988 The Regents of the University of California.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hack.tty.c 5.3 (Berkeley) 5/13/91";
+#endif /* not lint */
+
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.tty.c - version 1.0.3 */
+/* With thanks to the people who sent code for SYSV - hpscdi!jon,
+ arnold@ucsf-cgl, wcs@bo95b, cbcephus!pds and others. */
+
+#include "hack.h"
+#include <stdio.h>
+
+/*
+ * The distinctions here are not BSD - rest but rather USG - rest, as
+ * BSD still has the old sgttyb structure, but SYSV has termio. Thus:
+ */
+#ifdef BSD
+#define V7
+#else
+#define USG
+#endif BSD
+
+/*
+ * Some systems may have getchar() return EOF for various reasons, and
+ * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
+ */
+#ifndef BSD
+#define NR_OF_EOFS 20
+#endif BSD
+
+
+#ifdef USG
+
+#include <termio.h>
+#define termstruct termio
+#define kill_sym c_cc[VKILL]
+#define erase_sym c_cc[VERASE]
+#define EXTABS TAB3
+#define tabflgs c_oflag
+#define echoflgs c_lflag
+#define cbrkflgs c_lflag
+#define CBRKMASK ICANON
+#define CBRKON ! /* reverse condition */
+#define OSPEED(x) ((x).c_cflag & CBAUD)
+#define GTTY(x) (ioctl(0, TCGETA, x))
+#define STTY(x) (ioctl(0, TCSETA, x)) /* TCSETAF? TCSETAW? */
+
+#else /* V7 */
+
+#include <sgtty.h>
+#define termstruct sgttyb
+#define kill_sym sg_kill
+#define erase_sym sg_erase
+#define EXTABS XTABS
+#define tabflgs sg_flags
+#define echoflgs sg_flags
+#define cbrkflgs sg_flags
+#define CBRKMASK CBREAK
+#define CBRKON /* empty */
+#define OSPEED(x) (x).sg_ospeed
+#define GTTY(x) (gtty(0, x))
+#define STTY(x) (stty(0, x))
+
+#endif USG
+
+extern short ospeed;
+static char erase_char, kill_char;
+static boolean settty_needed = FALSE;
+struct termstruct inittyb, curttyb;
+
+/*
+ * Get initial state of terminal, set ospeed (for termcap routines)
+ * and switch off tab expansion if necessary.
+ * Called by startup() in termcap.c and after returning from ! or ^Z
+ */
+gettty(){
+ if(GTTY(&inittyb) < 0)
+ perror("Hack (gettty)");
+ curttyb = inittyb;
+ ospeed = OSPEED(inittyb);
+ erase_char = inittyb.erase_sym;
+ kill_char = inittyb.kill_sym;
+ getioctls();
+
+ /* do not expand tabs - they might be needed inside a cm sequence */
+ if(curttyb.tabflgs & EXTABS) {
+ curttyb.tabflgs &= ~EXTABS;
+ setctty();
+ }
+ settty_needed = TRUE;
+}
+
+/* reset terminal to original state */
+settty(s) char *s; {
+ clear_screen();
+ end_screen();
+ if(s) printf(s);
+ (void) fflush(stdout);
+ if(STTY(&inittyb) < 0)
+ perror("Hack (settty)");
+ flags.echo = (inittyb.echoflgs & ECHO) ? ON : OFF;
+ flags.cbreak = (CBRKON(inittyb.cbrkflgs & CBRKMASK)) ? ON : OFF;
+ setioctls();
+}
+
+setctty(){
+ if(STTY(&curttyb) < 0)
+ perror("Hack (setctty)");
+}
+
+
+setftty(){
+register int ef = 0; /* desired value of flags & ECHO */
+register int cf = CBRKON(CBRKMASK); /* desired value of flags & CBREAK */
+register int change = 0;
+ flags.cbreak = ON;
+ flags.echo = OFF;
+ /* Should use (ECHO|CRMOD) here instead of ECHO */
+ if((curttyb.echoflgs & ECHO) != ef){
+ curttyb.echoflgs &= ~ECHO;
+/* curttyb.echoflgs |= ef; */
+ change++;
+ }
+ if((curttyb.cbrkflgs & CBRKMASK) != cf){
+ curttyb.cbrkflgs &= ~CBRKMASK;
+ curttyb.cbrkflgs |= cf;
+#ifdef USG
+ /* be satisfied with one character; no timeout */
+ curttyb.c_cc[VMIN] = 1; /* was VEOF */
+ curttyb.c_cc[VTIME] = 0; /* was VEOL */
+#endif USG
+ change++;
+ }
+ if(change){
+ setctty();
+ }
+ start_screen();
+}
+
+
+/* fatal error */
+/*VARARGS1*/
+error(s,x,y) char *s; {
+ if(settty_needed)
+ settty((char *) 0);
+ printf(s,x,y);
+ putchar('\n');
+ exit(1);
+}
+
+/*
+ * Read a line closed with '\n' into the array char bufp[BUFSZ].
+ * (The '\n' is not stored. The string is closed with a '\0'.)
+ * Reading can be interrupted by an escape ('\033') - now the
+ * resulting string is "\033".
+ */
+getlin(bufp)
+register char *bufp;
+{
+ register char *obufp = bufp;
+ register int c;
+
+ flags.toplin = 2; /* nonempty, no --More-- required */
+ for(;;) {
+ (void) fflush(stdout);
+ if((c = getchar()) == EOF) {
+ *bufp = 0;
+ return;
+ }
+ if(c == '\033') {
+ *obufp = c;
+ obufp[1] = 0;
+ return;
+ }
+ if(c == erase_char || c == '\b') {
+ if(bufp != obufp) {
+ bufp--;
+ putstr("\b \b"); /* putsym converts \b */
+ } else bell();
+ } else if(c == '\n') {
+ *bufp = 0;
+ return;
+ } else if(' ' <= c && c < '\177') {
+ /* avoid isprint() - some people don't have it
+ ' ' is not always a printing char */
+ *bufp = c;
+ bufp[1] = 0;
+ putstr(bufp);
+ if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
+ bufp++;
+ } else if(c == kill_char || c == '\177') { /* Robert Viduya */
+ /* this test last - @ might be the kill_char */
+ while(bufp != obufp) {
+ bufp--;
+ putstr("\b \b");
+ }
+ } else
+ bell();
+ }
+}
+
+getret() {
+ cgetret("");
+}
+
+cgetret(s)
+register char *s;
+{
+ putsym('\n');
+ if(flags.standout)
+ standoutbeg();
+ putstr("Hit ");
+ putstr(flags.cbreak ? "space" : "return");
+ putstr(" to continue: ");
+ if(flags.standout)
+ standoutend();
+ xwaitforspace(s);
+}
+
+char morc; /* tell the outside world what char he used */
+
+xwaitforspace(s)
+register char *s; /* chars allowed besides space or return */
+{
+register int c;
+
+ morc = 0;
+
+ while((c = readchar()) != '\n') {
+ if(flags.cbreak) {
+ if(c == ' ') break;
+ if(s && index(s,c)) {
+ morc = c;
+ break;
+ }
+ bell();
+ }
+ }
+}
+
+char *
+parse()
+{
+ static char inputline[COLNO];
+ register foo;
+
+ flags.move = 1;
+ if(!Invisible) curs_on_u(); else home();
+ while((foo = readchar()) >= '0' && foo <= '9')
+ multi = 10*multi+foo-'0';
+ if(multi) {
+ multi--;
+ save_cm = inputline;
+ }
+ inputline[0] = foo;
+ inputline[1] = 0;
+ if(foo == 'f' || foo == 'F'){
+ inputline[1] = getchar();
+#ifdef QUEST
+ if(inputline[1] == foo) inputline[2] = getchar(); else
+#endif QUEST
+ inputline[2] = 0;
+ }
+ if(foo == 'm' || foo == 'M'){
+ inputline[1] = getchar();
+ inputline[2] = 0;
+ }
+ clrlin();
+ return(inputline);
+}
+
+char
+readchar() {
+ register int sym;
+
+ (void) fflush(stdout);
+ if((sym = getchar()) == EOF)
+#ifdef NR_OF_EOFS
+ { /*
+ * Some SYSV systems seem to return EOFs for various reasons
+ * (?like when one hits break or for interrupted systemcalls?),
+ * and we must see several before we quit.
+ */
+ register int cnt = NR_OF_EOFS;
+ while (cnt--) {
+ clearerr(stdin); /* omit if clearerr is undefined */
+ if((sym = getchar()) != EOF) goto noteof;
+ }
+ end_of_input();
+ noteof: ;
+ }
+#else
+ end_of_input();
+#endif NR_OF_EOFS
+ if(flags.toplin == 1)
+ flags.toplin = 2;
+ return((char) sym);
+}
+
+end_of_input()
+{
+ settty("End of input?\n");
+ clearlocks();
+ exit(0);
+}
diff --git a/games/hack/hack.u_init.c b/games/hack/hack.u_init.c
new file mode 100644
index 000000000000..bc06fa5ecfd5
--- /dev/null
+++ b/games/hack/hack.u_init.c
@@ -0,0 +1,357 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.u_init.c - version 1.0.3 */
+
+#include "hack.h"
+#include <stdio.h>
+#include <signal.h>
+#define Strcpy (void) strcpy
+#define Strcat (void) strcat
+#define UNDEF_TYP 0
+#define UNDEF_SPE '\177'
+extern struct obj *addinv();
+extern char *eos();
+extern char plname[];
+
+struct you zerou;
+char pl_character[PL_CSIZ];
+char *(roles[]) = { /* must all have distinct first letter */
+ /* roles[4] may be changed to -man */
+ "Tourist", "Speleologist", "Fighter", "Knight",
+ "Cave-man", "Wizard"
+};
+#define NR_OF_ROLES SIZE(roles)
+char rolesyms[NR_OF_ROLES + 1]; /* filled by u_init() */
+
+struct trobj {
+ uchar trotyp;
+ schar trspe;
+ char trolet;
+ Bitfield(trquan,6);
+ Bitfield(trknown,1);
+};
+
+#ifdef WIZARD
+struct trobj Extra_objs[] = {
+ { 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+#endif WIZARD
+
+struct trobj Cave_man[] = {
+ { MACE, 1, WEAPON_SYM, 1, 1 },
+ { BOW, 1, WEAPON_SYM, 1, 1 },
+ { ARROW, 0, WEAPON_SYM, 25, 1 }, /* quan is variable */
+ { LEATHER_ARMOR, 0, ARMOR_SYM, 1, 1 },
+ { 0, 0, 0, 0, 0}
+};
+
+struct trobj Fighter[] = {
+ { TWO_HANDED_SWORD, 0, WEAPON_SYM, 1, 1 },
+ { RING_MAIL, 0, ARMOR_SYM, 1, 1 },
+ { 0, 0, 0, 0, 0 }
+};
+
+struct trobj Knight[] = {
+ { LONG_SWORD, 0, WEAPON_SYM, 1, 1 },
+ { SPEAR, 2, WEAPON_SYM, 1, 1 },
+ { RING_MAIL, 1, ARMOR_SYM, 1, 1 },
+ { HELMET, 0, ARMOR_SYM, 1, 1 },
+ { SHIELD, 0, ARMOR_SYM, 1, 1 },
+ { PAIR_OF_GLOVES, 0, ARMOR_SYM, 1, 1 },
+ { 0, 0, 0, 0, 0 }
+};
+
+struct trobj Speleologist[] = {
+ { STUDDED_LEATHER_ARMOR, 0, ARMOR_SYM, 1, 1 },
+ { UNDEF_TYP, 0, POTION_SYM, 2, 0 },
+ { FOOD_RATION, 0, FOOD_SYM, 3, 1 },
+ { PICK_AXE, UNDEF_SPE, TOOL_SYM, 1, 0 },
+ { ICE_BOX, 0, TOOL_SYM, 1, 0 },
+ { 0, 0, 0, 0, 0}
+};
+
+struct trobj Tinopener[] = {
+ { CAN_OPENER, 0, TOOL_SYM, 1, 1 },
+ { 0, 0, 0, 0, 0 }
+};
+
+struct trobj Tourist[] = {
+ { UNDEF_TYP, 0, FOOD_SYM, 10, 1 },
+ { POT_EXTRA_HEALING, 0, POTION_SYM, 2, 0 },
+ { EXPENSIVE_CAMERA, 0, TOOL_SYM, 1, 1 },
+ { DART, 2, WEAPON_SYM, 25, 1 }, /* quan is variable */
+ { 0, 0, 0, 0, 0 }
+};
+
+struct trobj Wizard[] = {
+ { ELVEN_CLOAK, 0, ARMOR_SYM, 1, 1 },
+ { UNDEF_TYP, UNDEF_SPE, WAND_SYM, 2, 0 },
+ { UNDEF_TYP, UNDEF_SPE, RING_SYM, 2, 0 },
+ { UNDEF_TYP, UNDEF_SPE, POTION_SYM, 2, 0 },
+ { UNDEF_TYP, UNDEF_SPE, SCROLL_SYM, 3, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+u_init(){
+register int i;
+char exper = 'y', pc;
+extern char readchar();
+ if(flags.female) /* should have been set in HACKOPTIONS */
+ roles[4] = "Cave-woman";
+ for(i = 0; i < NR_OF_ROLES; i++)
+ rolesyms[i] = roles[i][0];
+ rolesyms[i] = 0;
+
+ if(pc = pl_character[0]) {
+ if('a' <= pc && pc <= 'z') pc += 'A'-'a';
+ if((i = role_index(pc)) >= 0)
+ goto got_suffix; /* implies experienced */
+ printf("\nUnknown role: %c\n", pc);
+ pl_character[0] = pc = 0;
+ }
+
+ printf("\nAre you an experienced player? [ny] ");
+
+ while(!index("ynYN \n\004", (exper = readchar())))
+ bell();
+ if(exper == '\004') /* Give him an opportunity to get out */
+ end_of_input();
+ printf("%c\n", exper); /* echo */
+ if(index("Nn \n", exper)) {
+ exper = 0;
+ goto beginner;
+ }
+
+ printf("\nTell me what kind of character you are:\n");
+ printf("Are you");
+ for(i = 0; i < NR_OF_ROLES; i++) {
+ printf(" a %s", roles[i]);
+ if(i == 2) /* %% */
+ printf(",\n\t");
+ else if(i < NR_OF_ROLES - 2)
+ printf(",");
+ else if(i == NR_OF_ROLES - 2)
+ printf(" or");
+ }
+ printf("? [%s] ", rolesyms);
+
+ while(pc = readchar()) {
+ if('a' <= pc && pc <= 'z') pc += 'A'-'a';
+ if((i = role_index(pc)) >= 0) {
+ printf("%c\n", pc); /* echo */
+ (void) fflush(stdout); /* should be seen */
+ break;
+ }
+ if(pc == '\n')
+ break;
+ if(pc == '\004') /* Give him the opportunity to get out */
+ end_of_input();
+ bell();
+ }
+ if(pc == '\n')
+ pc = 0;
+
+beginner:
+ if(!pc) {
+ printf("\nI'll choose a character for you.\n");
+ i = rn2(NR_OF_ROLES);
+ pc = rolesyms[i];
+ printf("This game you will be a%s %s.\n",
+ exper ? "n experienced" : "",
+ roles[i]);
+ getret();
+ /* give him some feedback in case mklev takes much time */
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if(exper) {
+ roles[i][0] = pc;
+ }
+
+got_suffix:
+
+ (void) strncpy(pl_character, roles[i], PL_CSIZ-1);
+ pl_character[PL_CSIZ-1] = 0;
+ flags.beginner = 1;
+ u = zerou;
+ u.usym = '@';
+ u.ulevel = 1;
+ init_uhunger();
+#ifdef QUEST
+ u.uhorizon = 6;
+#endif QUEST
+ uarm = uarm2 = uarmh = uarms = uarmg = uwep = uball = uchain =
+ uleft = uright = 0;
+
+ switch(pc) {
+ case 'c':
+ case 'C':
+ Cave_man[2].trquan = 12 + rnd(9)*rnd(9);
+ u.uhp = u.uhpmax = 16;
+ u.ustr = u.ustrmax = 18;
+ ini_inv(Cave_man);
+ break;
+ case 't':
+ case 'T':
+ Tourist[3].trquan = 20 + rnd(20);
+ u.ugold = u.ugold0 = rnd(1000);
+ u.uhp = u.uhpmax = 10;
+ u.ustr = u.ustrmax = 8;
+ ini_inv(Tourist);
+ if(!rn2(25)) ini_inv(Tinopener);
+ break;
+ case 'w':
+ case 'W':
+ for(i=1; i<=4; i++) if(!rn2(5))
+ Wizard[i].trquan += rn2(3) - 1;
+ u.uhp = u.uhpmax = 15;
+ u.ustr = u.ustrmax = 16;
+ ini_inv(Wizard);
+ break;
+ case 's':
+ case 'S':
+ Fast = INTRINSIC;
+ Stealth = INTRINSIC;
+ u.uhp = u.uhpmax = 12;
+ u.ustr = u.ustrmax = 10;
+ ini_inv(Speleologist);
+ if(!rn2(10)) ini_inv(Tinopener);
+ break;
+ case 'k':
+ case 'K':
+ u.uhp = u.uhpmax = 12;
+ u.ustr = u.ustrmax = 10;
+ ini_inv(Knight);
+ break;
+ case 'f':
+ case 'F':
+ u.uhp = u.uhpmax = 14;
+ u.ustr = u.ustrmax = 17;
+ ini_inv(Fighter);
+ break;
+ default: /* impossible */
+ u.uhp = u.uhpmax = 12;
+ u.ustr = u.ustrmax = 16;
+ }
+ find_ac();
+ if(!rn2(20)) {
+ register int d = rn2(7) - 2; /* biased variation */
+ u.ustr += d;
+ u.ustrmax += d;
+ }
+
+#ifdef WIZARD
+ if(wizard) wiz_inv();
+#endif WIZARD
+
+ /* make sure he can carry all he has - especially for T's */
+ while(inv_weight() > 0 && u.ustr < 118)
+ u.ustr++, u.ustrmax++;
+}
+
+ini_inv(trop) register struct trobj *trop; {
+register struct obj *obj;
+extern struct obj *mkobj();
+ while(trop->trolet) {
+ obj = mkobj(trop->trolet);
+ obj->known = trop->trknown;
+ /* not obj->dknown = 1; - let him look at it at least once */
+ obj->cursed = 0;
+ if(obj->olet == WEAPON_SYM){
+ obj->quan = trop->trquan;
+ trop->trquan = 1;
+ }
+ if(trop->trspe != UNDEF_SPE)
+ obj->spe = trop->trspe;
+ if(trop->trotyp != UNDEF_TYP)
+ obj->otyp = trop->trotyp;
+ else
+ if(obj->otyp == WAN_WISHING) /* gitpyr!robert */
+ obj->otyp = WAN_DEATH;
+ obj->owt = weight(obj); /* defined after setting otyp+quan */
+ obj = addinv(obj);
+ if(obj->olet == ARMOR_SYM){
+ switch(obj->otyp){
+ case SHIELD:
+ if(!uarms) setworn(obj, W_ARMS);
+ break;
+ case HELMET:
+ if(!uarmh) setworn(obj, W_ARMH);
+ break;
+ case PAIR_OF_GLOVES:
+ if(!uarmg) setworn(obj, W_ARMG);
+ break;
+ case ELVEN_CLOAK:
+ if(!uarm2)
+ setworn(obj, W_ARM);
+ break;
+ default:
+ if(!uarm) setworn(obj, W_ARM);
+ }
+ }
+ if(obj->olet == WEAPON_SYM)
+ if(!uwep) setuwep(obj);
+#ifndef PYRAMID_BUG
+ if(--trop->trquan) continue; /* make a similar object */
+#else
+ if(trop->trquan) { /* check if zero first */
+ --trop->trquan;
+ if(trop->trquan)
+ continue; /* make a similar object */
+ }
+#endif PYRAMID_BUG
+ trop++;
+ }
+}
+
+#ifdef WIZARD
+wiz_inv(){
+register struct trobj *trop = &Extra_objs[0];
+extern char *getenv();
+register char *ep = getenv("INVENT");
+register int type;
+ while(ep && *ep) {
+ type = atoi(ep);
+ ep = index(ep, ',');
+ if(ep) while(*ep == ',' || *ep == ' ') ep++;
+ if(type <= 0 || type > NROFOBJECTS) continue;
+ trop->trotyp = type;
+ trop->trolet = objects[type].oc_olet;
+ trop->trspe = 4;
+ trop->trknown = 1;
+ trop->trquan = 1;
+ ini_inv(trop);
+ }
+ /* give him a wand of wishing by default */
+ trop->trotyp = WAN_WISHING;
+ trop->trolet = WAND_SYM;
+ trop->trspe = 20;
+ trop->trknown = 1;
+ trop->trquan = 1;
+ ini_inv(trop);
+}
+#endif WIZARD
+
+plnamesuffix() {
+register char *p;
+ if(p = rindex(plname, '-')) {
+ *p = 0;
+ pl_character[0] = p[1];
+ pl_character[1] = 0;
+ if(!plname[0]) {
+ askname();
+ plnamesuffix();
+ }
+ }
+}
+
+role_index(pc)
+char pc;
+{ /* must be called only from u_init() */
+ /* so that rolesyms[] is defined */
+ register char *cp;
+
+ if(cp = index(rolesyms, pc))
+ return(cp - rolesyms);
+ return(-1);
+}
diff --git a/games/hack/hack.unix.c b/games/hack/hack.unix.c
new file mode 100644
index 000000000000..03b294db8b10
--- /dev/null
+++ b/games/hack/hack.unix.c
@@ -0,0 +1,430 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.unix.c - version 1.0.3 */
+
+/* This file collects some Unix dependencies; hack.pager.c contains some more */
+
+/*
+ * The time is used for:
+ * - seed for random()
+ * - year on tombstone and yymmdd in record file
+ * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
+ * - night and midnight (the undead are dangerous at midnight)
+ * - determination of what files are "very old"
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "hack.h" /* mainly for index() which depends on BSD */
+
+#include <sys/types.h> /* for time_t and stat */
+#include <sys/stat.h>
+#ifdef BSD
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif BSD
+
+extern char *getenv();
+extern time_t time();
+
+setrandom()
+{
+ (void) srandom((int) time ((time_t *) 0));
+}
+
+struct tm *
+getlt()
+{
+ time_t date;
+ struct tm *localtime();
+
+ (void) time(&date);
+ return(localtime(&date));
+}
+
+getyear()
+{
+ return(1900 + getlt()->tm_year);
+}
+
+char *
+getdate()
+{
+ static char datestr[7];
+ register struct tm *lt = getlt();
+
+ (void) sprintf(datestr, "%2d%2d%2d",
+ lt->tm_year, lt->tm_mon + 1, lt->tm_mday);
+ if(datestr[2] == ' ') datestr[2] = '0';
+ if(datestr[4] == ' ') datestr[4] = '0';
+ return(datestr);
+}
+
+phase_of_the_moon() /* 0-7, with 0: new, 4: full */
+{ /* moon period: 29.5306 days */
+ /* year: 365.2422 days */
+ register struct tm *lt = getlt();
+ register int epact, diy, golden;
+
+ diy = lt->tm_yday;
+ golden = (lt->tm_year % 19) + 1;
+ epact = (11 * golden + 18) % 30;
+ if ((epact == 25 && golden > 11) || epact == 24)
+ epact++;
+
+ return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
+}
+
+night()
+{
+ register int hour = getlt()->tm_hour;
+
+ return(hour < 6 || hour > 21);
+}
+
+midnight()
+{
+ return(getlt()->tm_hour == 0);
+}
+
+struct stat buf, hbuf;
+
+gethdate(name) char *name; {
+/* old version - for people short of space */
+/*
+/* register char *np;
+/* if(stat(name, &hbuf))
+/* error("Cannot get status of %s.",
+/* (np = rindex(name, '/')) ? np+1 : name);
+/*
+/* version using PATH from: seismo!gregc@ucsf-cgl.ARPA (Greg Couch) */
+
+
+/*
+ * The problem with #include <sys/param.h> is that this include file
+ * does not exist on all systems, and moreover, that it sometimes includes
+ * <sys/types.h> again, so that the compiler sees these typedefs twice.
+ */
+#define MAXPATHLEN 1024
+
+register char *np, *path;
+char filename[MAXPATHLEN+1];
+ if (index(name, '/') != NULL || (path = getenv("PATH")) == NULL)
+ path = "";
+
+ for (;;) {
+ if ((np = index(path, ':')) == NULL)
+ np = path + strlen(path); /* point to end str */
+ if (np - path <= 1) /* %% */
+ (void) strcpy(filename, name);
+ else {
+ (void) strncpy(filename, path, np - path);
+ filename[np - path] = '/';
+ (void) strcpy(filename + (np - path) + 1, name);
+ }
+ if (stat(filename, &hbuf) == 0)
+ return;
+ if (*np == '\0')
+ break;
+ path = np + 1;
+ }
+ error("Cannot get status of %s.",
+ (np = rindex(name, '/')) ? np+1 : name);
+}
+
+uptodate(fd) {
+ if(fstat(fd, &buf)) {
+ pline("Cannot get status of saved level? ");
+ return(0);
+ }
+ if(buf.st_mtime < hbuf.st_mtime) {
+ pline("Saved level is out of date. ");
+ return(0);
+ }
+ return(1);
+}
+
+/* see whether we should throw away this xlock file */
+veryold(fd) {
+ register int i;
+ time_t date;
+
+ if(fstat(fd, &buf)) return(0); /* cannot get status */
+ if(buf.st_size != sizeof(int)) return(0); /* not an xlock file */
+ (void) time(&date);
+ if(date - buf.st_mtime < 3L*24L*60L*60L) { /* recent */
+ extern int errno;
+ int lockedpid; /* should be the same size as hackpid */
+
+ if(read(fd, (char *)&lockedpid, sizeof(lockedpid)) !=
+ sizeof(lockedpid))
+ /* strange ... */
+ return(0);
+
+ /* From: Rick Adams <seismo!rick>
+ /* This will work on 4.1cbsd, 4.2bsd and system 3? & 5.
+ /* It will do nothing on V7 or 4.1bsd. */
+ if(!(kill(lockedpid, 0) == -1 && errno == ESRCH))
+ return(0);
+ }
+ (void) close(fd);
+ for(i = 1; i <= MAXLEVEL; i++) { /* try to remove all */
+ glo(i);
+ (void) unlink(lock);
+ }
+ glo(0);
+ if(unlink(lock)) return(0); /* cannot remove it */
+ return(1); /* success! */
+}
+
+getlock()
+{
+ extern int errno, hackpid, locknum;
+ register int i = 0, fd;
+
+ (void) fflush(stdout);
+
+ /* we ignore QUIT and INT at this point */
+ if (link(HLOCK, LLOCK) == -1) {
+ register int errnosv = errno;
+
+ perror(HLOCK);
+ printf("Cannot link %s to %s\n", LLOCK, HLOCK);
+ switch(errnosv) {
+ case ENOENT:
+ printf("Perhaps there is no (empty) file %s ?\n", HLOCK);
+ break;
+ case EACCES:
+ printf("It seems you don't have write permission here.\n");
+ break;
+ case EEXIST:
+ printf("(Try again or rm %s.)\n", LLOCK);
+ break;
+ default:
+ printf("I don't know what is wrong.");
+ }
+ getret();
+ error("");
+ /*NOTREACHED*/
+ }
+
+ regularize(lock);
+ glo(0);
+ if(locknum > 25) locknum = 25;
+
+ do {
+ if(locknum) lock[0] = 'a' + i++;
+
+ if((fd = open(lock, 0)) == -1) {
+ if(errno == ENOENT) goto gotlock; /* no such file */
+ perror(lock);
+ (void) unlink(LLOCK);
+ error("Cannot open %s", lock);
+ }
+
+ if(veryold(fd)) /* if true, this closes fd and unlinks lock */
+ goto gotlock;
+ (void) close(fd);
+ } while(i < locknum);
+
+ (void) unlink(LLOCK);
+ error(locknum ? "Too many hacks running now."
+ : "There is a game in progress under your name.");
+gotlock:
+ fd = creat(lock, FMASK);
+ if(unlink(LLOCK) == -1)
+ error("Cannot unlink %s.", LLOCK);
+ if(fd == -1) {
+ error("cannot creat lock file.");
+ } else {
+ if(write(fd, (char *) &hackpid, sizeof(hackpid))
+ != sizeof(hackpid)){
+ error("cannot write lock");
+ }
+ if(close(fd) == -1) {
+ error("cannot close lock");
+ }
+ }
+}
+
+#ifdef MAIL
+
+/*
+ * Notify user when new mail has arrived. [Idea from Merlyn Leroy, but
+ * I don't know the details of his implementation.]
+ * { Later note: he disliked my calling a general mailreader and felt that
+ * hack should do the paging itself. But when I get mail, I want to put it
+ * in some folder, reply, etc. - it would be unreasonable to put all these
+ * functions in hack. }
+ * The mail daemon '2' is at present not a real monster, but only a visual
+ * effect. Thus, makemon() is superfluous. This might become otherwise,
+ * however. The motion of '2' is less restrained than usual: diagonal moves
+ * from a DOOR are possible. He might also use SDOOR's. Also, '2' is visible
+ * in a ROOM, even when you are Blind.
+ * Its path should be longer when you are Telepat-hic and Blind.
+ *
+ * Interesting side effects:
+ * - You can get rich by sending yourself a lot of mail and selling
+ * it to the shopkeeper. Unfortunately mail isn't very valuable.
+ * - You might die in case '2' comes along at a critical moment during
+ * a fight and delivers a scroll the weight of which causes you to
+ * collapse.
+ *
+ * Possible extensions:
+ * - Open the file MAIL and do fstat instead of stat for efficiency.
+ * (But sh uses stat, so this cannot be too bad.)
+ * - Examine the mail and produce a scroll of mail called "From somebody".
+ * - Invoke MAILREADER in such a way that only this single letter is read.
+ *
+ * - Make him lose his mail when a Nymph steals the letter.
+ * - Do something to the text when the scroll is enchanted or cancelled.
+ */
+#include "def.mkroom.h"
+static struct stat omstat,nmstat;
+static char *mailbox;
+static long laststattime;
+
+getmailstatus() {
+ if(!(mailbox = getenv("MAIL")))
+ return;
+ if(stat(mailbox, &omstat)){
+#ifdef PERMANENT_MAILBOX
+ pline("Cannot get status of MAIL=%s .", mailbox);
+ mailbox = 0;
+#else
+ omstat.st_mtime = 0;
+#endif PERMANENT_MAILBOX
+ }
+}
+
+ckmailstatus() {
+ if(!mailbox
+#ifdef MAILCKFREQ
+ || moves < laststattime + MAILCKFREQ
+#endif MAILCKFREQ
+ )
+ return;
+ laststattime = moves;
+ if(stat(mailbox, &nmstat)){
+#ifdef PERMANENT_MAILBOX
+ pline("Cannot get status of MAIL=%s anymore.", mailbox);
+ mailbox = 0;
+#else
+ nmstat.st_mtime = 0;
+#endif PERMANENT_MAILBOX
+ } else if(nmstat.st_mtime > omstat.st_mtime) {
+ if(nmstat.st_size)
+ newmail();
+ getmailstatus(); /* might be too late ... */
+ }
+}
+
+newmail() {
+ /* produce a scroll of mail */
+ register struct obj *obj;
+ register struct monst *md;
+ extern char plname[];
+ extern struct obj *mksobj(), *addinv();
+ extern struct monst *makemon();
+ extern struct permonst pm_mail_daemon;
+
+ obj = mksobj(SCR_MAIL);
+ if(md = makemon(&pm_mail_daemon, u.ux, u.uy)) /* always succeeds */
+ mdrush(md,0);
+
+ pline("\"Hello, %s! I have some mail for you.\"", plname);
+ if(md) {
+ if(dist(md->mx,md->my) > 2)
+ pline("\"Catch!\"");
+ more();
+
+ /* let him disappear again */
+ mdrush(md,1);
+ mondead(md);
+ }
+
+ obj = addinv(obj);
+ (void) identify(obj); /* set known and do prinv() */
+}
+
+/* make md run through the cave */
+mdrush(md,away)
+register struct monst *md;
+boolean away;
+{
+ register int uroom = inroom(u.ux, u.uy);
+ if(uroom >= 0) {
+ register int tmp = rooms[uroom].fdoor;
+ register int cnt = rooms[uroom].doorct;
+ register int fx = u.ux, fy = u.uy;
+ while(cnt--) {
+ if(dist(fx,fy) < dist(doors[tmp].x, doors[tmp].y)){
+ fx = doors[tmp].x;
+ fy = doors[tmp].y;
+ }
+ tmp++;
+ }
+ tmp_at(-1, md->data->mlet); /* open call */
+ if(away) { /* interchange origin and destination */
+ unpmon(md);
+ tmp = fx; fx = md->mx; md->mx = tmp;
+ tmp = fy; fy = md->my; md->my = tmp;
+ }
+ while(fx != md->mx || fy != md->my) {
+ register int dx,dy,nfx = fx,nfy = fy,d1,d2;
+
+ tmp_at(fx,fy);
+ d1 = DIST(fx,fy,md->mx,md->my);
+ for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
+ if(dx || dy) {
+ d2 = DIST(fx+dx,fy+dy,md->mx,md->my);
+ if(d2 < d1) {
+ d1 = d2;
+ nfx = fx+dx;
+ nfy = fy+dy;
+ }
+ }
+ if(nfx != fx || nfy != fy) {
+ fx = nfx;
+ fy = nfy;
+ } else {
+ if(!away) {
+ md->mx = fx;
+ md->my = fy;
+ }
+ break;
+ }
+ }
+ tmp_at(-1,-1); /* close call */
+ }
+ if(!away)
+ pmon(md);
+}
+
+readmail() {
+#ifdef DEF_MAILREADER /* This implies that UNIX is defined */
+ register char *mr = 0;
+ more();
+ if(!(mr = getenv("MAILREADER")))
+ mr = DEF_MAILREADER;
+ if(child(1)){
+ execl(mr, mr, (char *) 0);
+ exit(1);
+ }
+#else DEF_MAILREADER
+ (void) page_file(mailbox, FALSE);
+#endif DEF_MAILREADER
+ /* get new stat; not entirely correct: there is a small time
+ window where we do not see new mail */
+ getmailstatus();
+}
+#endif MAIL
+
+regularize(s) /* normalize file name - we don't like ..'s or /'s */
+register char *s;
+{
+ register char *lp;
+
+ while((lp = index(s, '.')) || (lp = index(s, '/')))
+ *lp = '_';
+}
diff --git a/games/hack/hack.vault.c b/games/hack/hack.vault.c
new file mode 100644
index 000000000000..a82c9f9a509b
--- /dev/null
+++ b/games/hack/hack.vault.c
@@ -0,0 +1,259 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.vault.c - version 1.0.2 */
+
+#include "hack.h"
+#ifdef QUEST
+setgd(/* mtmp */) /* struct monst *mtmp; */ {}
+gd_move() { return(2); }
+gddead(mtmp) struct monst *mtmp; {}
+replgd(mtmp,mtmp2) struct monst *mtmp, *mtmp2; {}
+invault(){}
+
+#else
+
+
+#include "def.mkroom.h"
+extern struct monst *makemon();
+#define FCSIZ (ROWNO+COLNO)
+struct fakecorridor {
+ xchar fx,fy,ftyp;
+};
+
+struct egd {
+ int fcbeg, fcend; /* fcend: first unused pos */
+ xchar gdx, gdy; /* goal of guard's walk */
+ unsigned gddone:1;
+ struct fakecorridor fakecorr[FCSIZ];
+};
+
+static struct permonst pm_guard =
+ { "guard", '@', 12, 12, -1, 4, 10, sizeof(struct egd) };
+
+static struct monst *guard;
+static int gdlevel;
+#define EGD ((struct egd *)(&(guard->mextra[0])))
+
+static
+restfakecorr()
+{
+ register fcx,fcy,fcbeg;
+ register struct rm *crm;
+
+ while((fcbeg = EGD->fcbeg) < EGD->fcend) {
+ fcx = EGD->fakecorr[fcbeg].fx;
+ fcy = EGD->fakecorr[fcbeg].fy;
+ if((u.ux == fcx && u.uy == fcy) || cansee(fcx,fcy) ||
+ m_at(fcx,fcy))
+ return;
+ crm = &levl[fcx][fcy];
+ crm->typ = EGD->fakecorr[fcbeg].ftyp;
+ if(!crm->typ) crm->seen = 0;
+ newsym(fcx,fcy);
+ EGD->fcbeg++;
+ }
+ /* it seems he left the corridor - let the guard disappear */
+ mondead(guard);
+ guard = 0;
+}
+
+static
+goldincorridor()
+{
+ register int fci;
+
+ for(fci = EGD->fcbeg; fci < EGD->fcend; fci++)
+ if(g_at(EGD->fakecorr[fci].fx, EGD->fakecorr[fci].fy))
+ return(1);
+ return(0);
+}
+
+setgd(){
+register struct monst *mtmp;
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) if(mtmp->isgd){
+ guard = mtmp;
+ gdlevel = dlevel;
+ return;
+ }
+ guard = 0;
+}
+
+invault(){
+register tmp = inroom(u.ux, u.uy);
+ if(tmp < 0 || rooms[tmp].rtype != VAULT) {
+ u.uinvault = 0;
+ return;
+ }
+ if(++u.uinvault % 50 == 0 && (!guard || gdlevel != dlevel)) {
+ char buf[BUFSZ];
+ register x,y,dd,gx,gy;
+
+ /* first find the goal for the guard */
+ for(dd = 1; (dd < ROWNO || dd < COLNO); dd++) {
+ for(y = u.uy-dd; y <= u.uy+dd; y++) {
+ if(y < 0 || y > ROWNO-1) continue;
+ for(x = u.ux-dd; x <= u.ux+dd; x++) {
+ if(y != u.uy-dd && y != u.uy+dd && x != u.ux-dd)
+ x = u.ux+dd;
+ if(x < 0 || x > COLNO-1) continue;
+ if(levl[x][y].typ == CORR) goto fnd;
+ }
+ }
+ }
+ impossible("Not a single corridor on this level??");
+ tele();
+ return;
+fnd:
+ gx = x; gy = y;
+
+ /* next find a good place for a door in the wall */
+ x = u.ux; y = u.uy;
+ while(levl[x][y].typ == ROOM) {
+ register int dx,dy;
+
+ dx = (gx > x) ? 1 : (gx < x) ? -1 : 0;
+ dy = (gy > y) ? 1 : (gy < y) ? -1 : 0;
+ if(abs(gx-x) >= abs(gy-y))
+ x += dx;
+ else
+ y += dy;
+ }
+
+ /* make something interesting happen */
+ if(!(guard = makemon(&pm_guard,x,y))) return;
+ guard->isgd = guard->mpeaceful = 1;
+ EGD->gddone = 0;
+ gdlevel = dlevel;
+ if(!cansee(guard->mx, guard->my)) {
+ mondead(guard);
+ guard = 0;
+ return;
+ }
+
+ pline("Suddenly one of the Vault's guards enters!");
+ pmon(guard);
+ do {
+ pline("\"Hello stranger, who are you?\" - ");
+ getlin(buf);
+ } while (!letter(buf[0]));
+
+ if(!strcmp(buf, "Croesus") || !strcmp(buf, "Kroisos")) {
+ pline("\"Oh, yes - of course. Sorry to have disturbed you.\"");
+ mondead(guard);
+ guard = 0;
+ return;
+ }
+ clrlin();
+ pline("\"I don't know you.\"");
+ if(!u.ugold)
+ pline("\"Please follow me.\"");
+ else {
+ pline("\"Most likely all that gold was stolen from this vault.\"");
+ pline("\"Please drop your gold (say d$ ) and follow me.\"");
+ }
+ EGD->gdx = gx;
+ EGD->gdy = gy;
+ EGD->fcbeg = 0;
+ EGD->fakecorr[0].fx = x;
+ EGD->fakecorr[0].fy = y;
+ EGD->fakecorr[0].ftyp = levl[x][y].typ;
+ levl[x][y].typ = DOOR;
+ EGD->fcend = 1;
+ }
+}
+
+gd_move(){
+register int x,y,dx,dy,gx,gy,nx,ny,typ;
+register struct fakecorridor *fcp;
+register struct rm *crm;
+ if(!guard || gdlevel != dlevel){
+ impossible("Where is the guard?");
+ return(2); /* died */
+ }
+ if(u.ugold || goldincorridor())
+ return(0); /* didnt move */
+ if(dist(guard->mx,guard->my) > 1 || EGD->gddone) {
+ restfakecorr();
+ return(0); /* didnt move */
+ }
+ x = guard->mx;
+ y = guard->my;
+ /* look around (hor & vert only) for accessible places */
+ for(nx = x-1; nx <= x+1; nx++) for(ny = y-1; ny <= y+1; ny++) {
+ if(nx == x || ny == y) if(nx != x || ny != y)
+ if(isok(nx,ny))
+ if(!IS_WALL(typ = (crm = &levl[nx][ny])->typ) && typ != POOL) {
+ register int i;
+ for(i = EGD->fcbeg; i < EGD->fcend; i++)
+ if(EGD->fakecorr[i].fx == nx &&
+ EGD->fakecorr[i].fy == ny)
+ goto nextnxy;
+ if((i = inroom(nx,ny)) >= 0 && rooms[i].rtype == VAULT)
+ goto nextnxy;
+ /* seems we found a good place to leave him alone */
+ EGD->gddone = 1;
+ if(ACCESSIBLE(typ)) goto newpos;
+ crm->typ = (typ == SCORR) ? CORR : DOOR;
+ goto proceed;
+ }
+ nextnxy: ;
+ }
+ nx = x;
+ ny = y;
+ gx = EGD->gdx;
+ gy = EGD->gdy;
+ dx = (gx > x) ? 1 : (gx < x) ? -1 : 0;
+ dy = (gy > y) ? 1 : (gy < y) ? -1 : 0;
+ if(abs(gx-x) >= abs(gy-y)) nx += dx; else ny += dy;
+
+ while((typ = (crm = &levl[nx][ny])->typ) != 0) {
+ /* in view of the above we must have IS_WALL(typ) or typ == POOL */
+ /* must be a wall here */
+ if(isok(nx+nx-x,ny+ny-y) && typ != POOL &&
+ ZAP_POS(levl[nx+nx-x][ny+ny-y].typ)){
+ crm->typ = DOOR;
+ goto proceed;
+ }
+ if(dy && nx != x) {
+ nx = x; ny = y+dy;
+ continue;
+ }
+ if(dx && ny != y) {
+ ny = y; nx = x+dx; dy = 0;
+ continue;
+ }
+ /* I don't like this, but ... */
+ crm->typ = DOOR;
+ goto proceed;
+ }
+ crm->typ = CORR;
+proceed:
+ if(cansee(nx,ny)) {
+ mnewsym(nx,ny);
+ prl(nx,ny);
+ }
+ fcp = &(EGD->fakecorr[EGD->fcend]);
+ if(EGD->fcend++ == FCSIZ) panic("fakecorr overflow");
+ fcp->fx = nx;
+ fcp->fy = ny;
+ fcp->ftyp = typ;
+newpos:
+ if(EGD->gddone) nx = ny = 0;
+ guard->mx = nx;
+ guard->my = ny;
+ pmon(guard);
+ restfakecorr();
+ return(1);
+}
+
+gddead(){
+ guard = 0;
+}
+
+replgd(mtmp,mtmp2)
+register struct monst *mtmp, *mtmp2;
+{
+ if(mtmp == guard)
+ guard = mtmp2;
+}
+
+#endif QUEST
diff --git a/games/hack/hack.version.c b/games/hack/hack.version.c
new file mode 100644
index 000000000000..4c89fe739b90
--- /dev/null
+++ b/games/hack/hack.version.c
@@ -0,0 +1,16 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.version.c - version 1.0.3 */
+/* $Header: /a/cvs/386BSD/src/games/hack/hack.version.c,v 1.1.1.1 1993/06/12 14:40:26 rgrimes Exp $ */
+
+#include "date.h"
+
+doversion(){
+ pline("%s 1.0.3 - last edit %s.", (
+#ifdef QUEST
+ "Quest"
+#else
+ "Hack"
+#endif QUEST
+ ), datestring);
+ return(0);
+}
diff --git a/games/hack/hack.wield.c b/games/hack/hack.wield.c
new file mode 100644
index 000000000000..d0609a1cb6d4
--- /dev/null
+++ b/games/hack/hack.wield.c
@@ -0,0 +1,99 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.wield.c - version 1.0.3 */
+
+#include "hack.h"
+extern struct obj zeroobj;
+
+setuwep(obj) register struct obj *obj; {
+ setworn(obj, W_WEP);
+}
+
+dowield()
+{
+ register struct obj *wep;
+ register int res = 0;
+
+ multi = 0;
+ if(!(wep = getobj("#-)", "wield"))) /* nothing */;
+ else if(uwep == wep)
+ pline("You are already wielding that!");
+ else if(uwep && uwep->cursed)
+ pline("The %s welded to your hand!",
+ aobjnam(uwep, "are"));
+ else if(wep == &zeroobj) {
+ if(uwep == 0){
+ pline("You are already empty handed.");
+ } else {
+ setuwep((struct obj *) 0);
+ res++;
+ pline("You are empty handed.");
+ }
+ } else if(uarms && wep->otyp == TWO_HANDED_SWORD)
+ pline("You cannot wield a two-handed sword and wear a shield.");
+ else if(wep->owornmask & (W_ARMOR | W_RING))
+ pline("You cannot wield that!");
+ else {
+ setuwep(wep);
+ res++;
+ if(uwep->cursed)
+ pline("The %s %s to your hand!",
+ aobjnam(uwep, "weld"),
+ (uwep->quan == 1) ? "itself" : "themselves"); /* a3 */
+ else prinv(uwep);
+ }
+ return(res);
+}
+
+corrode_weapon(){
+ if(!uwep || uwep->olet != WEAPON_SYM) return; /* %% */
+ if(uwep->rustfree)
+ pline("Your %s not affected.", aobjnam(uwep, "are"));
+ else {
+ pline("Your %s!", aobjnam(uwep, "corrode"));
+ uwep->spe--;
+ }
+}
+
+chwepon(otmp,amount)
+register struct obj *otmp;
+register amount;
+{
+register char *color = (amount < 0) ? "black" : "green";
+register char *time;
+ if(!uwep || uwep->olet != WEAPON_SYM) {
+ strange_feeling(otmp,
+ (amount > 0) ? "Your hands twitch."
+ : "Your hands itch.");
+ return(0);
+ }
+
+ if(uwep->otyp == WORM_TOOTH && amount > 0) {
+ uwep->otyp = CRYSKNIFE;
+ pline("Your weapon seems sharper now.");
+ uwep->cursed = 0;
+ return(1);
+ }
+
+ if(uwep->otyp == CRYSKNIFE && amount < 0) {
+ uwep->otyp = WORM_TOOTH;
+ pline("Your weapon looks duller now.");
+ return(1);
+ }
+
+ /* there is a (soft) upper limit to uwep->spe */
+ if(amount > 0 && uwep->spe > 5 && rn2(3)) {
+ pline("Your %s violently green for a while and then evaporate%s.",
+ aobjnam(uwep, "glow"), plur(uwep->quan));
+ while(uwep) /* let all of them disappear */
+ /* note: uwep->quan = 1 is nogood if unpaid */
+ useup(uwep);
+ return(1);
+ }
+ if(!rn2(6)) amount *= 2;
+ time = (amount*amount == 1) ? "moment" : "while";
+ pline("Your %s %s for a %s.",
+ aobjnam(uwep, "glow"), color, time);
+ uwep->spe += amount;
+ if(amount > 0) uwep->cursed = 0;
+ return(1);
+}
diff --git a/games/hack/hack.wizard.c b/games/hack/hack.wizard.c
new file mode 100644
index 000000000000..835ecc3dfbd9
--- /dev/null
+++ b/games/hack/hack.wizard.c
@@ -0,0 +1,189 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.wizard.c - version 1.0.3 */
+
+/* wizard code - inspired by rogue code from Merlyn Leroy (digi-g!brian) */
+
+#include "hack.h"
+extern struct permonst pm_wizard;
+extern struct monst *makemon();
+
+#define WIZSHOT 6 /* one chance in WIZSHOT that wizard will try magic */
+#define BOLT_LIM 8 /* from this distance D and 1 will try to hit you */
+
+char wizapp[] = "@DNPTUVXcemntx";
+
+/* If he has found the Amulet, make the wizard appear after some time */
+amulet(){
+ register struct obj *otmp;
+ register struct monst *mtmp;
+
+ if(!flags.made_amulet || !flags.no_of_wizards)
+ return;
+ /* find wizard, and wake him if necessary */
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if(mtmp->data->mlet == '1' && mtmp->msleep && !rn2(40))
+ for(otmp = invent; otmp; otmp = otmp->nobj)
+ if(otmp->olet == AMULET_SYM && !otmp->spe) {
+ mtmp->msleep = 0;
+ if(dist(mtmp->mx,mtmp->my) > 2)
+ pline(
+ "You get the creepy feeling that somebody noticed your taking the Amulet."
+ );
+ return;
+ }
+}
+
+wiz_hit(mtmp)
+register struct monst *mtmp;
+{
+ /* if we have stolen or found the amulet, we disappear */
+ if(mtmp->minvent && mtmp->minvent->olet == AMULET_SYM &&
+ mtmp->minvent->spe == 0) {
+ /* vanish -- very primitive */
+ fall_down(mtmp);
+ return(1);
+ }
+
+ /* if it is lying around someplace, we teleport to it */
+ if(!carrying(AMULET_OF_YENDOR)) {
+ register struct obj *otmp;
+
+ for(otmp = fobj; otmp; otmp = otmp->nobj)
+ if(otmp->olet == AMULET_SYM && !otmp->spe) {
+ if((u.ux != otmp->ox || u.uy != otmp->oy) &&
+ !m_at(otmp->ox, otmp->oy)) {
+
+ /* teleport to it and pick it up */
+ mtmp->mx = otmp->ox;
+ mtmp->my = otmp->oy;
+ freeobj(otmp);
+ mpickobj(mtmp, otmp);
+ pmon(mtmp);
+ return(0);
+ }
+ goto hithim;
+ }
+ return(0); /* we don't know where it is */
+ }
+hithim:
+ if(rn2(2)) { /* hit - perhaps steal */
+
+ /* if hit 1/20 chance of stealing amulet & vanish
+ - amulet is on level 26 again. */
+ if(hitu(mtmp, d(mtmp->data->damn,mtmp->data->damd))
+ && !rn2(20) && stealamulet(mtmp))
+ ;
+ }
+ else
+ inrange(mtmp); /* try magic */
+ return(0);
+}
+
+inrange(mtmp)
+register struct monst *mtmp;
+{
+ register schar tx,ty;
+
+ /* do nothing if cancelled (but make '1' say something) */
+ if(mtmp->data->mlet != '1' && mtmp->mcan)
+ return;
+
+ /* spit fire only when both in a room or both in a corridor */
+ if(inroom(u.ux,u.uy) != inroom(mtmp->mx,mtmp->my)) return;
+ tx = u.ux - mtmp->mx;
+ ty = u.uy - mtmp->my;
+ if((!tx && abs(ty) < BOLT_LIM) || (!ty && abs(tx) < BOLT_LIM)
+ || (abs(tx) == abs(ty) && abs(tx) < BOLT_LIM)){
+ switch(mtmp->data->mlet) {
+ case 'D':
+ /* spit fire in the direction of @ (not nec. hitting) */
+ buzz(-1,mtmp->mx,mtmp->my,sgn(tx),sgn(ty));
+ break;
+ case '1':
+ if(rn2(WIZSHOT)) break;
+ /* if you zapped wizard with wand of cancellation,
+ he has to shake off the effects before he can throw
+ spells successfully. 1/2 the time they fail anyway */
+ if(mtmp->mcan || rn2(2)) {
+ if(canseemon(mtmp))
+ pline("%s makes a gesture, then curses.",
+ Monnam(mtmp));
+ else
+ pline("You hear mumbled cursing.");
+ if(!rn2(3)) {
+ mtmp->mspeed = 0;
+ mtmp->minvis = 0;
+ }
+ if(!rn2(3))
+ mtmp->mcan = 0;
+ } else {
+ if(canseemon(mtmp)){
+ if(!rn2(6) && !Invis) {
+ pline("%s hypnotizes you.", Monnam(mtmp));
+ nomul(rn2(3) + 3);
+ break;
+ } else
+ pline("%s chants an incantation.",
+ Monnam(mtmp));
+ } else
+ pline("You hear a mumbled incantation.");
+ switch(rn2(Invis ? 5 : 6)) {
+ case 0:
+ /* create a nasty monster from a deep level */
+ /* (for the moment, 'nasty' is not implemented) */
+ (void) makemon((struct permonst *)0, u.ux, u.uy);
+ break;
+ case 1:
+ pline("\"Destroy the thief, my pets!\"");
+ aggravate(); /* aggravate all the monsters */
+ /* fall into next case */
+ case 2:
+ if (flags.no_of_wizards == 1 && rnd(5) == 0)
+ /* if only 1 wizard, clone himself */
+ clonewiz(mtmp);
+ break;
+ case 3:
+ if(mtmp->mspeed == MSLOW)
+ mtmp->mspeed = 0;
+ else
+ mtmp->mspeed = MFAST;
+ break;
+ case 4:
+ mtmp->minvis = 1;
+ break;
+ case 5:
+ /* Only if not Invisible */
+ pline("You hear a clap of thunder!");
+ /* shoot a bolt of fire or cold, or a sleep ray */
+ buzz(-rnd(3),mtmp->mx,mtmp->my,sgn(tx),sgn(ty));
+ break;
+ }
+ }
+ }
+ if(u.uhp < 1) done_in_by(mtmp);
+ }
+}
+
+aggravate()
+{
+ register struct monst *mtmp;
+
+ for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+ mtmp->msleep = 0;
+ if(mtmp->mfroz && !rn2(5))
+ mtmp->mfroz = 0;
+ }
+}
+
+clonewiz(mtmp)
+register struct monst *mtmp;
+{
+ register struct monst *mtmp2;
+
+ if(mtmp2 = makemon(PM_WIZARD, mtmp->mx, mtmp->my)) {
+ flags.no_of_wizards = 2;
+ unpmon(mtmp2);
+ mtmp2->mappearance = wizapp[rn2(sizeof(wizapp)-1)];
+ pmon(mtmp);
+ }
+}
diff --git a/games/hack/hack.worm.c b/games/hack/hack.worm.c
new file mode 100644
index 000000000000..ba3505d19b4f
--- /dev/null
+++ b/games/hack/hack.worm.c
@@ -0,0 +1,183 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.worm.c - version 1.0.2 */
+
+#include "hack.h"
+#ifndef NOWORM
+#include "def.wseg.h"
+
+struct wseg *wsegs[32]; /* linked list, tail first */
+struct wseg *wheads[32];
+long wgrowtime[32];
+
+getwn(mtmp) struct monst *mtmp; {
+register tmp;
+ for(tmp=1; tmp<32; tmp++) if(!wsegs[tmp]) {
+ mtmp->wormno = tmp;
+ return(1);
+ }
+ return(0); /* level infested with worms */
+}
+
+/* called to initialize a worm unless cut in half */
+initworm(mtmp) struct monst *mtmp; {
+register struct wseg *wtmp;
+register tmp = mtmp->wormno;
+ if(!tmp) return;
+ wheads[tmp] = wsegs[tmp] = wtmp = newseg();
+ wgrowtime[tmp] = 0;
+ wtmp->wx = mtmp->mx;
+ wtmp->wy = mtmp->my;
+/* wtmp->wdispl = 0; */
+ wtmp->nseg = 0;
+}
+
+worm_move(mtmp) struct monst *mtmp; {
+register struct wseg *wtmp, *whd;
+register tmp = mtmp->wormno;
+ wtmp = newseg();
+ wtmp->wx = mtmp->mx;
+ wtmp->wy = mtmp->my;
+ wtmp->nseg = 0;
+/* wtmp->wdispl = 0; */
+ (whd = wheads[tmp])->nseg = wtmp;
+ wheads[tmp] = wtmp;
+ if(cansee(whd->wx,whd->wy)){
+ unpmon(mtmp);
+ atl(whd->wx, whd->wy, '~');
+ whd->wdispl = 1;
+ } else whd->wdispl = 0;
+ if(wgrowtime[tmp] <= moves) {
+ if(!wgrowtime[tmp]) wgrowtime[tmp] = moves + rnd(5);
+ else wgrowtime[tmp] += 2+rnd(15);
+ mtmp->mhpmax += 3;
+ mtmp->mhp += 3;
+ return;
+ }
+ whd = wsegs[tmp];
+ wsegs[tmp] = whd->nseg;
+ remseg(whd);
+}
+
+worm_nomove(mtmp) register struct monst *mtmp; {
+register tmp;
+register struct wseg *wtmp;
+ tmp = mtmp->wormno;
+ wtmp = wsegs[tmp];
+ if(wtmp == wheads[tmp]) return;
+ if(wtmp == 0 || wtmp->nseg == 0) panic("worm_nomove?");
+ wsegs[tmp] = wtmp->nseg;
+ remseg(wtmp);
+ mtmp->mhp -= 3; /* mhpmax not changed ! */
+}
+
+wormdead(mtmp) register struct monst *mtmp; {
+register tmp = mtmp->wormno;
+register struct wseg *wtmp, *wtmp2;
+ if(!tmp) return;
+ mtmp->wormno = 0;
+ for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2){
+ wtmp2 = wtmp->nseg;
+ remseg(wtmp);
+ }
+ wsegs[tmp] = 0;
+}
+
+wormhit(mtmp) register struct monst *mtmp; {
+register tmp = mtmp->wormno;
+register struct wseg *wtmp;
+ if(!tmp) return; /* worm without tail */
+ for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp->nseg)
+ (void) hitu(mtmp,1);
+}
+
+wormsee(tmp) register unsigned tmp; {
+register struct wseg *wtmp = wsegs[tmp];
+ if(!wtmp) panic("wormsee: wtmp==0");
+ for(; wtmp->nseg; wtmp = wtmp->nseg)
+ if(!cansee(wtmp->wx,wtmp->wy) && wtmp->wdispl){
+ newsym(wtmp->wx, wtmp->wy);
+ wtmp->wdispl = 0;
+ }
+}
+
+pwseg(wtmp) register struct wseg *wtmp; {
+ if(!wtmp->wdispl){
+ atl(wtmp->wx, wtmp->wy, '~');
+ wtmp->wdispl = 1;
+ }
+}
+
+cutworm(mtmp,x,y,weptyp)
+register struct monst *mtmp;
+register xchar x,y;
+register uchar weptyp; /* uwep->otyp or 0 */
+{
+ register struct wseg *wtmp, *wtmp2;
+ register struct monst *mtmp2;
+ register tmp,tmp2;
+ if(mtmp->mx == x && mtmp->my == y) return; /* hit headon */
+
+ /* cutting goes best with axe or sword */
+ tmp = rnd(20);
+ if(weptyp == LONG_SWORD || weptyp == TWO_HANDED_SWORD ||
+ weptyp == AXE) tmp += 5;
+ if(tmp < 12) return;
+
+ /* if tail then worm just loses a tail segment */
+ tmp = mtmp->wormno;
+ wtmp = wsegs[tmp];
+ if(wtmp->wx == x && wtmp->wy == y){
+ wsegs[tmp] = wtmp->nseg;
+ remseg(wtmp);
+ return;
+ }
+
+ /* cut the worm in two halves */
+ mtmp2 = newmonst(0);
+ *mtmp2 = *mtmp;
+ mtmp2->mxlth = mtmp2->mnamelth = 0;
+
+ /* sometimes the tail end dies */
+ if(rn2(3) || !getwn(mtmp2)){
+ monfree(mtmp2);
+ tmp2 = 0;
+ } else {
+ tmp2 = mtmp2->wormno;
+ wsegs[tmp2] = wsegs[tmp];
+ wgrowtime[tmp2] = 0;
+ }
+ do {
+ if(wtmp->nseg->wx == x && wtmp->nseg->wy == y){
+ if(tmp2) wheads[tmp2] = wtmp;
+ wsegs[tmp] = wtmp->nseg->nseg;
+ remseg(wtmp->nseg);
+ wtmp->nseg = 0;
+ if(tmp2){
+ pline("You cut the worm in half.");
+ mtmp2->mhpmax = mtmp2->mhp =
+ d(mtmp2->data->mlevel, 8);
+ mtmp2->mx = wtmp->wx;
+ mtmp2->my = wtmp->wy;
+ mtmp2->nmon = fmon;
+ fmon = mtmp2;
+ pmon(mtmp2);
+ } else {
+ pline("You cut off part of the worm's tail.");
+ remseg(wtmp);
+ }
+ mtmp->mhp /= 2;
+ return;
+ }
+ wtmp2 = wtmp->nseg;
+ if(!tmp2) remseg(wtmp);
+ wtmp = wtmp2;
+ } while(wtmp->nseg);
+ panic("Cannot find worm segment");
+}
+
+remseg(wtmp) register struct wseg *wtmp; {
+ if(wtmp->wdispl)
+ newsym(wtmp->wx, wtmp->wy);
+ free((char *) wtmp);
+}
+#endif NOWORM
diff --git a/games/hack/hack.worn.c b/games/hack/hack.worn.c
new file mode 100644
index 000000000000..bfec47f21996
--- /dev/null
+++ b/games/hack/hack.worn.c
@@ -0,0 +1,65 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.worn.c - version 1.0.2 */
+
+#include "hack.h"
+
+struct worn {
+ long w_mask;
+ struct obj **w_obj;
+} worn[] = {
+ { W_ARM, &uarm },
+ { W_ARM2, &uarm2 },
+ { W_ARMH, &uarmh },
+ { W_ARMS, &uarms },
+ { W_ARMG, &uarmg },
+ { W_RINGL, &uleft },
+ { W_RINGR, &uright },
+ { W_WEP, &uwep },
+ { W_BALL, &uball },
+ { W_CHAIN, &uchain },
+ { 0, 0 }
+};
+
+setworn(obj, mask)
+register struct obj *obj;
+long mask;
+{
+ register struct worn *wp;
+ register struct obj *oobj;
+
+ for(wp = worn; wp->w_mask; wp++) if(wp->w_mask & mask) {
+ oobj = *(wp->w_obj);
+ if(oobj && !(oobj->owornmask & wp->w_mask))
+ impossible("Setworn: mask = %ld.", wp->w_mask);
+ if(oobj) oobj->owornmask &= ~wp->w_mask;
+ if(obj && oobj && wp->w_mask == W_ARM){
+ if(uarm2) {
+ impossible("Setworn: uarm2 set?");
+ } else
+ setworn(uarm, W_ARM2);
+ }
+ *(wp->w_obj) = obj;
+ if(obj) obj->owornmask |= wp->w_mask;
+ }
+ if(uarm2 && !uarm) {
+ uarm = uarm2;
+ uarm2 = 0;
+ uarm->owornmask ^= (W_ARM | W_ARM2);
+ }
+}
+
+/* called e.g. when obj is destroyed */
+setnotworn(obj) register struct obj *obj; {
+ register struct worn *wp;
+
+ for(wp = worn; wp->w_mask; wp++)
+ if(obj == *(wp->w_obj)) {
+ *(wp->w_obj) = 0;
+ obj->owornmask &= ~wp->w_mask;
+ }
+ if(uarm2 && !uarm) {
+ uarm = uarm2;
+ uarm2 = 0;
+ uarm->owornmask ^= (W_ARM | W_ARM2);
+ }
+}
diff --git a/games/hack/hack.zap.c b/games/hack/hack.zap.c
new file mode 100644
index 000000000000..e1472b56906f
--- /dev/null
+++ b/games/hack/hack.zap.c
@@ -0,0 +1,642 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.zap.c - version 1.0.3 */
+
+#include "hack.h"
+
+extern struct obj *mkobj_at();
+extern struct monst *makemon(), *mkmon_at(), youmonst;
+struct monst *bhit();
+char *exclam();
+
+char *fl[]= {
+ "magic missile",
+ "bolt of fire",
+ "sleep ray",
+ "bolt of cold",
+ "death ray"
+};
+
+/* Routines for IMMEDIATE wands. */
+/* bhitm: monster mtmp was hit by the effect of wand otmp */
+bhitm(mtmp, otmp)
+register struct monst *mtmp;
+register struct obj *otmp;
+{
+ wakeup(mtmp);
+ switch(otmp->otyp) {
+ case WAN_STRIKING:
+ if(u.uswallow || rnd(20) < 10+mtmp->data->ac) {
+ register int tmp = d(2,12);
+ hit("wand", mtmp, exclam(tmp));
+ mtmp->mhp -= tmp;
+ if(mtmp->mhp < 1) killed(mtmp);
+ } else miss("wand", mtmp);
+ break;
+ case WAN_SLOW_MONSTER:
+ mtmp->mspeed = MSLOW;
+ break;
+ case WAN_SPEED_MONSTER:
+ mtmp->mspeed = MFAST;
+ break;
+ case WAN_UNDEAD_TURNING:
+ if(index(UNDEAD,mtmp->data->mlet)) {
+ mtmp->mhp -= rnd(8);
+ if(mtmp->mhp < 1) killed(mtmp);
+ else mtmp->mflee = 1;
+ }
+ break;
+ case WAN_POLYMORPH:
+ if( newcham(mtmp,&mons[rn2(CMNUM)]) )
+ objects[otmp->otyp].oc_name_known = 1;
+ break;
+ case WAN_CANCELLATION:
+ mtmp->mcan = 1;
+ break;
+ case WAN_TELEPORTATION:
+ rloc(mtmp);
+ break;
+ case WAN_MAKE_INVISIBLE:
+ mtmp->minvis = 1;
+ break;
+#ifdef WAN_PROBING
+ case WAN_PROBING:
+ mstatusline(mtmp);
+ break;
+#endif WAN_PROBING
+ default:
+ impossible("What an interesting wand (%u)", otmp->otyp);
+ }
+}
+
+bhito(obj, otmp) /* object obj was hit by the effect of wand otmp */
+register struct obj *obj, *otmp; /* returns TRUE if sth was done */
+{
+ register int res = TRUE;
+
+ if(obj == uball || obj == uchain)
+ res = FALSE;
+ else
+ switch(otmp->otyp) {
+ case WAN_POLYMORPH:
+ /* preserve symbol and quantity, but turn rocks into gems */
+ mkobj_at((obj->otyp == ROCK || obj->otyp == ENORMOUS_ROCK)
+ ? GEM_SYM : obj->olet,
+ obj->ox, obj->oy) -> quan = obj->quan;
+ delobj(obj);
+ break;
+ case WAN_STRIKING:
+ if(obj->otyp == ENORMOUS_ROCK)
+ fracture_rock(obj);
+ else
+ res = FALSE;
+ break;
+ case WAN_CANCELLATION:
+ if(obj->spe && obj->olet != AMULET_SYM) {
+ obj->known = 0;
+ obj->spe = 0;
+ }
+ break;
+ case WAN_TELEPORTATION:
+ rloco(obj);
+ break;
+ case WAN_MAKE_INVISIBLE:
+ obj->oinvis = 1;
+ break;
+ case WAN_UNDEAD_TURNING:
+ res = revive(obj);
+ break;
+ case WAN_SLOW_MONSTER: /* no effect on objects */
+ case WAN_SPEED_MONSTER:
+#ifdef WAN_PROBING
+ case WAN_PROBING:
+#endif WAN_PROBING
+ res = FALSE;
+ break;
+ default:
+ impossible("What an interesting wand (%u)", otmp->otyp);
+ }
+ return(res);
+}
+
+dozap()
+{
+ register struct obj *obj;
+ xchar zx,zy;
+
+ obj = getobj("/", "zap");
+ if(!obj) return(0);
+ if(obj->spe < 0 || (obj->spe == 0 && rn2(121))) {
+ pline("Nothing Happens.");
+ return(1);
+ }
+ if(obj->spe == 0)
+ pline("You wrest one more spell from the worn-out wand.");
+ if(!(objects[obj->otyp].bits & NODIR) && !getdir(1))
+ return(1); /* make him pay for knowing !NODIR */
+ obj->spe--;
+ if(objects[obj->otyp].bits & IMMEDIATE) {
+ if(u.uswallow)
+ bhitm(u.ustuck, obj);
+ else if(u.dz) {
+ if(u.dz > 0) {
+ register struct obj *otmp = o_at(u.ux, u.uy);
+ if(otmp)
+ (void) bhito(otmp, obj);
+ }
+ } else
+ (void) bhit(u.dx,u.dy,rn1(8,6),0,bhitm,bhito,obj);
+ } else {
+ switch(obj->otyp){
+ case WAN_LIGHT:
+ litroom(TRUE);
+ break;
+ case WAN_SECRET_DOOR_DETECTION:
+ if(!findit()) return(1);
+ break;
+ case WAN_CREATE_MONSTER:
+ { register int cnt = 1;
+ if(!rn2(23)) cnt += rn2(7) + 1;
+ while(cnt--)
+ (void) makemon((struct permonst *) 0, u.ux, u.uy);
+ }
+ break;
+ case WAN_WISHING:
+ { char buf[BUFSZ];
+ register struct obj *otmp;
+ extern struct obj *readobjnam(), *addinv();
+ if(u.uluck + rn2(5) < 0) {
+ pline("Unfortunately, nothing happens.");
+ break;
+ }
+ pline("You may wish for an object. What do you want? ");
+ getlin(buf);
+ if(buf[0] == '\033') buf[0] = 0;
+ otmp = readobjnam(buf);
+ otmp = addinv(otmp);
+ prinv(otmp);
+ break;
+ }
+ case WAN_DIGGING:
+ /* Original effect (approximately):
+ * from CORR: dig until we pierce a wall
+ * from ROOM: piece wall and dig until we reach
+ * an ACCESSIBLE place.
+ * Currently: dig for digdepth positions;
+ * also down on request of Lennart Augustsson.
+ */
+ { register struct rm *room;
+ register int digdepth;
+ if(u.uswallow) {
+ register struct monst *mtmp = u.ustuck;
+
+ pline("You pierce %s's stomach wall!",
+ monnam(mtmp));
+ mtmp->mhp = 1; /* almost dead */
+ unstuck(mtmp);
+ mnexto(mtmp);
+ break;
+ }
+ if(u.dz) {
+ if(u.dz < 0) {
+ pline("You loosen a rock from the ceiling.");
+ pline("It falls on your head!");
+ losehp(1, "falling rock");
+ mksobj_at(ROCK, u.ux, u.uy);
+ fobj->quan = 1;
+ stackobj(fobj);
+ if(Invisible) newsym(u.ux, u.uy);
+ } else {
+ dighole();
+ }
+ break;
+ }
+ zx = u.ux+u.dx;
+ zy = u.uy+u.dy;
+ digdepth = 8 + rn2(18);
+ Tmp_at(-1, '*'); /* open call */
+ while(--digdepth >= 0) {
+ if(!isok(zx,zy)) break;
+ room = &levl[zx][zy];
+ Tmp_at(zx,zy);
+ if(!xdnstair){
+ if(zx < 3 || zx > COLNO-3 ||
+ zy < 3 || zy > ROWNO-3)
+ break;
+ if(room->typ == HWALL ||
+ room->typ == VWALL){
+ room->typ = ROOM;
+ break;
+ }
+ } else
+ if(room->typ == HWALL || room->typ == VWALL ||
+ room->typ == SDOOR || room->typ == LDOOR){
+ room->typ = DOOR;
+ digdepth -= 2;
+ } else
+ if(room->typ == SCORR || !room->typ) {
+ room->typ = CORR;
+ digdepth--;
+ }
+ mnewsym(zx,zy);
+ zx += u.dx;
+ zy += u.dy;
+ }
+ mnewsym(zx,zy); /* not always necessary */
+ Tmp_at(-1,-1); /* closing call */
+ break;
+ }
+ default:
+ buzz((int) obj->otyp - WAN_MAGIC_MISSILE,
+ u.ux, u.uy, u.dx, u.dy);
+ break;
+ }
+ if(!objects[obj->otyp].oc_name_known) {
+ objects[obj->otyp].oc_name_known = 1;
+ more_experienced(0,10);
+ }
+ }
+ return(1);
+}
+
+char *
+exclam(force)
+register int force;
+{
+ /* force == 0 occurs e.g. with sleep ray */
+ /* note that large force is usual with wands so that !! would
+ require information about hand/weapon/wand */
+ return( (force < 0) ? "?" : (force <= 4) ? "." : "!" );
+}
+
+hit(str,mtmp,force)
+register char *str;
+register struct monst *mtmp;
+register char *force; /* usually either "." or "!" */
+{
+ if(!cansee(mtmp->mx,mtmp->my)) pline("The %s hits it.", str);
+ else pline("The %s hits %s%s", str, monnam(mtmp), force);
+}
+
+miss(str,mtmp)
+register char *str;
+register struct monst *mtmp;
+{
+ if(!cansee(mtmp->mx,mtmp->my)) pline("The %s misses it.",str);
+ else pline("The %s misses %s.",str,monnam(mtmp));
+}
+
+/* bhit: called when a weapon is thrown (sym = obj->olet) or when an
+ IMMEDIATE wand is zapped (sym = 0); the weapon falls down at end of
+ range or when a monster is hit; the monster is returned, and bhitpos
+ is set to the final position of the weapon thrown; the ray of a wand
+ may affect several objects and monsters on its path - for each of
+ these an argument function is called. */
+/* check !u.uswallow before calling bhit() */
+
+struct monst *
+bhit(ddx,ddy,range,sym,fhitm,fhito,obj)
+register int ddx,ddy,range; /* direction and range */
+char sym; /* symbol displayed on path */
+int (*fhitm)(), (*fhito)(); /* fns called when mon/obj hit */
+struct obj *obj; /* 2nd arg to fhitm/fhito */
+{
+ register struct monst *mtmp;
+ register struct obj *otmp;
+ register int typ;
+
+ bhitpos.x = u.ux;
+ bhitpos.y = u.uy;
+
+ if(sym) tmp_at(-1, sym); /* open call */
+ while(range-- > 0) {
+ bhitpos.x += ddx;
+ bhitpos.y += ddy;
+ typ = levl[bhitpos.x][bhitpos.y].typ;
+ if(mtmp = m_at(bhitpos.x,bhitpos.y)){
+ if(sym) {
+ tmp_at(-1, -1); /* close call */
+ return(mtmp);
+ }
+ (*fhitm)(mtmp, obj);
+ range -= 3;
+ }
+ if(fhito && (otmp = o_at(bhitpos.x,bhitpos.y))){
+ if((*fhito)(otmp, obj))
+ range--;
+ }
+ if(!ZAP_POS(typ)) {
+ bhitpos.x -= ddx;
+ bhitpos.y -= ddy;
+ break;
+ }
+ if(sym) tmp_at(bhitpos.x, bhitpos.y);
+ }
+
+ /* leave last symbol unless in a pool */
+ if(sym)
+ tmp_at(-1, (levl[bhitpos.x][bhitpos.y].typ == POOL) ? -1 : 0);
+ return(0);
+}
+
+struct monst *
+boomhit(dx,dy) {
+ register int i, ct;
+ register struct monst *mtmp;
+ char sym = ')';
+ extern schar xdir[], ydir[];
+
+ bhitpos.x = u.ux;
+ bhitpos.y = u.uy;
+
+ for(i=0; i<8; i++) if(xdir[i] == dx && ydir[i] == dy) break;
+ tmp_at(-1, sym); /* open call */
+ for(ct=0; ct<10; ct++) {
+ if(i == 8) i = 0;
+ sym = ')' + '(' - sym;
+ tmp_at(-2, sym); /* change let call */
+ dx = xdir[i];
+ dy = ydir[i];
+ bhitpos.x += dx;
+ bhitpos.y += dy;
+ if(mtmp = m_at(bhitpos.x, bhitpos.y)){
+ tmp_at(-1,-1);
+ return(mtmp);
+ }
+ if(!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ)) {
+ bhitpos.x -= dx;
+ bhitpos.y -= dy;
+ break;
+ }
+ if(bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */
+ if(rn2(20) >= 10+u.ulevel){ /* we hit ourselves */
+ (void) thitu(10, rnd(10), "boomerang");
+ break;
+ } else { /* we catch it */
+ tmp_at(-1,-1);
+ pline("Skillfully, you catch the boomerang.");
+ return(&youmonst);
+ }
+ }
+ tmp_at(bhitpos.x, bhitpos.y);
+ if(ct % 5 != 0) i++;
+ }
+ tmp_at(-1, -1); /* do not leave last symbol */
+ return(0);
+}
+
+char
+dirlet(dx,dy) register dx,dy; {
+ return
+ (dx == dy) ? '\\' : (dx && dy) ? '/' : dx ? '-' : '|';
+}
+
+/* type == -1: monster spitting fire at you */
+/* type == -1,-2,-3: bolts sent out by wizard */
+/* called with dx = dy = 0 with vertical bolts */
+buzz(type,sx,sy,dx,dy)
+register int type;
+register xchar sx,sy;
+register int dx,dy;
+{
+ int abstype = abs(type);
+ register char *fltxt = (type == -1) ? "blaze of fire" : fl[abstype];
+ struct rm *lev;
+ xchar range;
+ struct monst *mon;
+
+ if(u.uswallow) {
+ register int tmp;
+
+ if(type < 0) return;
+ tmp = zhit(u.ustuck, type);
+ pline("The %s rips into %s%s",
+ fltxt, monnam(u.ustuck), exclam(tmp));
+ return;
+ }
+ if(type < 0) pru();
+ range = rn1(7,7);
+ Tmp_at(-1, dirlet(dx,dy)); /* open call */
+ while(range-- > 0) {
+ sx += dx;
+ sy += dy;
+ if((lev = &levl[sx][sy])->typ) Tmp_at(sx,sy);
+ else {
+ int bounce = 0;
+ if(cansee(sx-dx,sy-dy))
+ pline("The %s bounces!", fltxt);
+ if(ZAP_POS(levl[sx][sy-dy].typ))
+ bounce = 1;
+ if(ZAP_POS(levl[sx-dx][sy].typ)) {
+ if(!bounce || rn2(2)) bounce = 2;
+ }
+ switch(bounce){
+ case 0:
+ dx = -dx;
+ dy = -dy;
+ continue;
+ case 1:
+ dy = -dy;
+ sx -= dx;
+ break;
+ case 2:
+ dx = -dx;
+ sy -= dy;
+ break;
+ }
+ Tmp_at(-2,dirlet(dx,dy));
+ continue;
+ }
+ if(lev->typ == POOL && abstype == 1 /* fire */) {
+ range -= 3;
+ lev->typ = ROOM;
+ if(cansee(sx,sy)) {
+ mnewsym(sx,sy);
+ pline("The water evaporates.");
+ } else
+ pline("You hear a hissing sound.");
+ }
+ if((mon = m_at(sx,sy)) &&
+ (type != -1 || mon->data->mlet != 'D')) {
+ wakeup(mon);
+ if(rnd(20) < 18 + mon->data->ac) {
+ register int tmp = zhit(mon,abstype);
+ if(mon->mhp < 1) {
+ if(type < 0) {
+ if(cansee(mon->mx,mon->my))
+ pline("%s is killed by the %s!",
+ Monnam(mon), fltxt);
+ mondied(mon);
+ } else
+ killed(mon);
+ } else
+ hit(fltxt, mon, exclam(tmp));
+ range -= 2;
+ } else
+ miss(fltxt,mon);
+ } else if(sx == u.ux && sy == u.uy) {
+ nomul(0);
+ if(rnd(20) < 18+u.uac) {
+ register int dam = 0;
+ range -= 2;
+ pline("The %s hits you!",fltxt);
+ switch(abstype) {
+ case 0:
+ dam = d(2,6);
+ break;
+ case 1:
+ if(Fire_resistance)
+ pline("You don't feel hot!");
+ else dam = d(6,6);
+ if(!rn2(3))
+ burn_scrolls();
+ break;
+ case 2:
+ nomul(-rnd(25)); /* sleep ray */
+ break;
+ case 3:
+ if(Cold_resistance)
+ pline("You don't feel cold!");
+ else dam = d(6,6);
+ break;
+ case 4:
+ u.uhp = -1;
+ }
+ losehp(dam,fltxt);
+ } else pline("The %s whizzes by you!",fltxt);
+ stop_occupation();
+ }
+ if(!ZAP_POS(lev->typ)) {
+ int bounce = 0, rmn;
+ if(cansee(sx,sy)) pline("The %s bounces!",fltxt);
+ range--;
+ if(!dx || !dy || !rn2(20)){
+ dx = -dx;
+ dy = -dy;
+ } else {
+ if(ZAP_POS(rmn = levl[sx][sy-dy].typ) &&
+ (IS_ROOM(rmn) || ZAP_POS(levl[sx+dx][sy-dy].typ)))
+ bounce = 1;
+ if(ZAP_POS(rmn = levl[sx-dx][sy].typ) &&
+ (IS_ROOM(rmn) || ZAP_POS(levl[sx-dx][sy+dy].typ)))
+ if(!bounce || rn2(2))
+ bounce = 2;
+
+ switch(bounce){
+ case 0:
+ dy = -dy;
+ dx = -dx;
+ break;
+ case 1:
+ dy = -dy;
+ break;
+ case 2:
+ dx = -dx;
+ break;
+ }
+ Tmp_at(-2, dirlet(dx,dy));
+ }
+ }
+ }
+ Tmp_at(-1,-1);
+}
+
+zhit(mon,type) /* returns damage to mon */
+register struct monst *mon;
+register type;
+{
+ register int tmp = 0;
+
+ switch(type) {
+ case 0: /* magic missile */
+ tmp = d(2,6);
+ break;
+ case -1: /* Dragon blazing fire */
+ case 1: /* fire */
+ if(index("Dg", mon->data->mlet)) break;
+ tmp = d(6,6);
+ if(index("YF", mon->data->mlet)) tmp += 7;
+ break;
+ case 2: /* sleep*/
+ mon->mfroz = 1;
+ break;
+ case 3: /* cold */
+ if(index("YFgf", mon->data->mlet)) break;
+ tmp = d(6,6);
+ if(mon->data->mlet == 'D') tmp += 7;
+ break;
+ case 4: /* death*/
+ if(index(UNDEAD, mon->data->mlet)) break;
+ tmp = mon->mhp+1;
+ break;
+ }
+ mon->mhp -= tmp;
+ return(tmp);
+}
+
+#define CORPSE_I_TO_C(otyp) (char) ((otyp >= DEAD_ACID_BLOB)\
+ ? 'a' + (otyp - DEAD_ACID_BLOB)\
+ : '@' + (otyp - DEAD_HUMAN))
+revive(obj)
+register struct obj *obj;
+{
+ register struct monst *mtmp;
+
+ if(obj->olet == FOOD_SYM && obj->otyp > CORPSE) {
+ /* do not (yet) revive shopkeepers */
+ /* Note: this might conceivably produce two monsters
+ at the same position - strange, but harmless */
+ mtmp = mkmon_at(CORPSE_I_TO_C(obj->otyp),obj->ox,obj->oy);
+ delobj(obj);
+ }
+ return(!!mtmp); /* TRUE if some monster created */
+}
+
+rloco(obj)
+register struct obj *obj;
+{
+ register tx,ty,otx,oty;
+
+ otx = obj->ox;
+ oty = obj->oy;
+ do {
+ tx = rn1(COLNO-3,2);
+ ty = rn2(ROWNO);
+ } while(!goodpos(tx,ty));
+ obj->ox = tx;
+ obj->oy = ty;
+ if(cansee(otx,oty))
+ newsym(otx,oty);
+}
+
+fracture_rock(obj) /* fractured by pick-axe or wand of striking */
+register struct obj *obj; /* no texts here! */
+{
+ /* unpobj(obj); */
+ obj->otyp = ROCK;
+ obj->quan = 7 + rn2(60);
+ obj->owt = weight(obj);
+ obj->olet = WEAPON_SYM;
+ if(cansee(obj->ox,obj->oy))
+ prl(obj->ox,obj->oy);
+}
+
+burn_scrolls()
+{
+ register struct obj *obj, *obj2;
+ register int cnt = 0;
+
+ for(obj = invent; obj; obj = obj2) {
+ obj2 = obj->nobj;
+ if(obj->olet == SCROLL_SYM) {
+ cnt++;
+ useup(obj);
+ }
+ }
+ if(cnt > 1) {
+ pline("Your scrolls catch fire!");
+ losehp(cnt, "burning scrolls");
+ } else if(cnt) {
+ pline("Your scroll catches fire!");
+ losehp(1, "burning scroll");
+ }
+}
diff --git a/games/hack/help b/games/hack/help
new file mode 100644
index 000000000000..24b22a5d410f
--- /dev/null
+++ b/games/hack/help
@@ -0,0 +1,132 @@
+ Welcome to HACK! ( description of version 1.0.3 )
+
+ Hack is a Dungeons and Dragons like game where you (the adventurer)
+descend into the depths of the dungeon in search of the Amulet of Yendor
+(reputed to be hidden on the twentieth level). You are accompanied by a
+little dog that can help you in many ways and can be trained to do all
+sorts of things. On the way you will find useful (or useless) items, (quite
+possibly with magic properties) and assorted monsters. You attack a monster
+by trying to move into the space a monster is in (but often it is much
+wiser to leave it alone).
+
+ Unlike most adventure games, which give you a verbal description of
+your location, hack gives you a visual image of the dungeon level you are on.
+
+ Hack uses the following symbols:
+ A to Z and a to z: monsters. You can find out what a letter
+represents by saying "/ (letter)", as in "/A", which will tell you that 'A'
+is a giant ant.
+ - and | These form the walls of a room (or maze).
+ . this is the floor of a room.
+ # this is a corridor.
+ > this is the staircase to the next level.
+ < the staircase to the previous level.
+ ` A large boulder.
+ @ You (usually).
+ ^ A trap.
+ ) A weapon of some sort.
+ ( Some other useful object (key, rope, dynamite, camera, ...)
+ [ A suit of armor.
+ % A piece of food (not necessarily healthy ...).
+ / A wand.
+ = A ring.
+ ? A scroll.
+ ! A magic potion.
+ $ A pile or pot of gold.
+
+Commands:
+ Hack knows the following commands:
+ ? help: print this list.
+ Q Quit the game.
+ S Save the game.
+ ! Escape to a shell.
+ ^Z Suspend the game.
+ < up: go up the staircase (if you are standing on it).
+ > down: go down (just like up).
+ kjhlyubn - go one step in the direction indicated.
+ k: north (i.e., to the top of the screen),
+ j: south, h: west, l: east, y: ne, u: nw, b: se, n: sw.
+ KJHLYUBN - Go in that direction until you hit a wall or run
+ into something.
+ m (followed by one of kjhlyubn): move without picking up
+ any objects.
+ M (followed by one of KJHLYUBN): Move far, no pickup.
+ f (followed by one of kjhlyubn): move until something
+ interesting is found.
+ F (followed by one of KJHLYUBN): as previous, but forking
+ of corridors is not considered interesting.
+ i print your inventory.
+ I print selected parts of your inventory, like in
+ I* - print all gems in inventory;
+ IU - print all unpaid items;
+ IX - print all used up items that are on your shopping bill;
+ I$ - count your money.
+ s search for secret doors and traps around you.
+ ^ ask for the type of a trap you found earlier.
+ ) ask for current wielded weapon.
+ [ ask for current armor.
+ = ask for current rings.
+ $ count how many gold pieces you are carrying.
+ . rest, do nothing.
+ , pick up some things.
+ : look at what is here.
+ ^T teleport.
+ ^R redraw the screen.
+ ^P repeat last message
+ (subsequent ^P's repeat earlier messages).
+ / (followed by any symbol): tell what this symbol represents.
+ \ tell what has been discovered.
+ e eat food.
+ w wield weapon. w- means: wield nothing, use bare hands.
+ q drink (quaff) a potion.
+ r read a scroll.
+ T Takeoff armor.
+ R Remove Ring.
+ W Wear armor.
+ P Put on a ring.
+ z zap a wand.
+ t throw an object or shoot an arrow.
+ p pay your shopping bill.
+ d drop something. d7a: drop seven items of object a.
+ D Drop several things.
+ In answer to the question "What kinds of things do you
+ want to drop? [!%= au]" you should give zero or more
+ object symbols possibly followed by 'a' and/or 'u'.
+ 'a' means: drop all such objects, without asking for
+ confirmation.
+ 'u' means: drop only unpaid objects (when in a shop).
+ a use, apply - Generic command for using a key to lock
+ or unlock a door, using a camera, using a rope, etc.
+ c call: name a certain object or class of objects.
+ C Call: Name an individual monster.
+ E Engrave: Write a message in the dust on the floor.
+ E- means: use fingers for writing.
+ O Set options. You will be asked to enter an option line.
+ If this is empty, the current options are reported.
+ Otherwise it should be a list of options separated by commas.
+ Possible boolean options are: oneline, time, news, tombstone,
+ rest_on_space, fixinvlet, beginner, male, female.
+ They can be negated by prefixing them with '!' or "no".
+ A string option is name; it supplies the answer to the question
+ "Who are you?"; it may have a suffix.
+ A compound option is endgame; it is followed by a description
+ of what parts of the list of topscorers should be printed
+ when the game is finished.
+ Usually one will not want to use the 'O' command, but instead
+ put a HACKOPTIONS="...." line in one's environment.
+ v print version number.
+
+ You can put a number before a command to repeat it that many times,
+ as in "20s" or "40.".
+
+ At present, some information is displayed on the bottom line.
+ (It is expected that this information will go away in future versions.)
+ You see on what dungeon level you are, how many hit points you have
+ now (and will have when fully recovered), what your armor class is
+ (the lower the better), your strength, experience level and the
+ state of your stomach.
+
+ Have Fun, and Good Hacking!
+
+
+
diff --git a/games/hack/hh b/games/hack/hh
new file mode 100644
index 000000000000..d777102dae64
--- /dev/null
+++ b/games/hack/hh
@@ -0,0 +1,55 @@
+y k u Move commands:
+ \|/ hykulnjb: single move in specified direction
+h-+-l HYKULNJB: repeated move in specified direction
+ /|\ (until stopped by e.g. a wall)
+b j n f<dir>: fast movement in direction <dir>
+ (until something interesting is seen)
+ m<dir>: move without picking up objects
+
+Meta commands:
+Q quit leave the game
+S save save the game (to be continued later)
+! sh escape to some SHELL
+^Z suspend suspend the game (independent of your current suspend char)
+O set set options
+? help print information
+/ whatis give name (and sometimes more info) of specified monster
+\ known print list of what's been discovered
+v version print version number
+^R redraw redraw the screen (^R denotes the symbol CTRL/R)
+^P print repeat last message (subsequent ^P's repeat earlier messages)
+# introduces a long command; not really implemented
+
+Game commands:
+^T teleport teleport
+a apply, use use something (a key, camera, etc.)
+c call give a name to a class of objects
+d drop drop an object. d7a: drop seven items of object a.
+e eat eat something
+i