aboutsummaryrefslogtreecommitdiff
path: root/hack
diff options
context:
space:
mode:
Diffstat (limited to 'hack')
-rw-r--r--hack/COPYRIGHT6
-rw-r--r--hack/Makefile52
-rw-r--r--hack/Makequest196
-rw-r--r--hack/OWNER2
-rw-r--r--hack/Original_READ_ME61
-rw-r--r--hack/READ_ME92
-rw-r--r--hack/alloc.c38
-rw-r--r--hack/config.h124
-rw-r--r--hack/data232
-rw-r--r--hack/date.h2
-rw-r--r--hack/def.edog.h12
-rw-r--r--hack/def.eshk.h24
-rw-r--r--hack/def.flag.h42
-rw-r--r--hack/def.func_tab.h13
-rw-r--r--hack/def.gold.h12
-rw-r--r--hack/def.mkroom.h26
-rw-r--r--hack/def.monst.h60
-rw-r--r--hack/def.obj.h48
-rw-r--r--hack/def.objclass.h62
-rw-r--r--hack/def.objects.h290
-rw-r--r--hack/def.permonst.h27
-rw-r--r--hack/def.rm.h53
-rw-r--r--hack/def.trap.h27
-rw-r--r--hack/def.wseg.h14
-rw-r--r--hack/hack.6160
-rw-r--r--hack/hack.Decl.c42
-rw-r--r--hack/hack.apply.c467
-rw-r--r--hack/hack.bones.c108
-rw-r--r--hack/hack.c904
-rw-r--r--hack/hack.cmd.c332
-rw-r--r--hack/hack.do.c512
-rw-r--r--hack/hack.do_name.c316
-rw-r--r--hack/hack.do_wear.c395
-rw-r--r--hack/hack.dog.c459
-rw-r--r--hack/hack.eat.c492
-rw-r--r--hack/hack.end.c725
-rw-r--r--hack/hack.engrave.c337
-rw-r--r--hack/hack.fight.c404
-rw-r--r--hack/hack.fix113
-rw-r--r--hack/hack.h707
-rw-r--r--hack/hack.invent.c964
-rw-r--r--hack/hack.ioctl.c47
-rw-r--r--hack/hack.lev.c276
-rw-r--r--hack/hack.main.c507
-rw-r--r--hack/hack.makemon.c212
-rw-r--r--hack/hack.mfndpos.h12
-rw-r--r--hack/hack.mhitu.c385
-rw-r--r--hack/hack.mklev.c795
-rw-r--r--hack/hack.mkmaze.c157
-rw-r--r--hack/hack.mkobj.c160
-rw-r--r--hack/hack.mkshop.c304
-rw-r--r--hack/hack.mon.c961
-rw-r--r--hack/hack.monst.c79
-rw-r--r--hack/hack.o_init.c184
-rw-r--r--hack/hack.objnam.c584
-rw-r--r--hack/hack.options.c217
-rw-r--r--hack/hack.pager.c418
-rw-r--r--hack/hack.potion.c407
-rw-r--r--hack/hack.pri.c721
-rw-r--r--hack/hack.read.c572
-rw-r--r--hack/hack.rip.c89
-rw-r--r--hack/hack.rumors.c90
-rw-r--r--hack/hack.save.c240
-rw-r--r--hack/hack.search.c147
-rw-r--r--hack/hack.sh14
-rw-r--r--hack/hack.shk.c1085
-rw-r--r--hack/hack.shknam.c149
-rw-r--r--hack/hack.steal.c210
-rw-r--r--hack/hack.termcap.c268
-rw-r--r--hack/hack.timeout.c72
-rw-r--r--hack/hack.topl.c236
-rw-r--r--hack/hack.track.c49
-rw-r--r--hack/hack.trap.c488
-rw-r--r--hack/hack.tty.c313
-rw-r--r--hack/hack.u_init.c385
-rw-r--r--hack/hack.unix.c421
-rw-r--r--hack/hack.vault.c310
-rw-r--r--hack/hack.version.c20
-rw-r--r--hack/hack.wield.c111
-rw-r--r--hack/hack.wizard.c198
-rw-r--r--hack/hack.worm.c223
-rw-r--r--hack/hack.worn.c70
-rw-r--r--hack/hack.zap.c688
-rw-r--r--hack/help132
-rw-r--r--hack/hh55
-rw-r--r--hack/makedefs.c266
-rw-r--r--hack/pathnames.h35
-rw-r--r--hack/rnd.c35
-rw-r--r--hack/rumors505
89 files changed, 22854 insertions, 0 deletions
diff --git a/hack/COPYRIGHT b/hack/COPYRIGHT
new file mode 100644
index 0000000..71a9449
--- /dev/null
+++ b/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/hack/Makefile b/hack/Makefile
new file mode 100644
index 0000000..53b4975
--- /dev/null
+++ b/hack/Makefile
@@ -0,0 +1,52 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $FreeBSD: src/games/hack/Makefile,v 1.20.2.4 2002/08/07 16:31:41 ru Exp $
+# $DragonFly: src/games/hack/Makefile,v 1.6 2006/10/08 16:22:35 pavalos Exp $
+
+# 20150209: hackery with makedefs and hack.onames.h to
+# make Slackware's old pmake do this right.
+
+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 \
+ hack.onames.h
+MAN= hack.6
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap -lbsd
+CFLAGS+= -I${.CURDIR} -I.
+FILES= rumors help hh data
+FILESMODE_rumors= 440
+FILESGRP= ${BINGRP}
+FILESDIR= /var/games/hackdir
+HIDEGAME=hidegame
+CLEANFILES=hack.onames.h makedefs makedefs.no
+
+build-tools: makedefs
+
+alloc.c: hack.onames.h
+
+hack.onames.h: makedefs def.objects.h
+ ./makedefs ${.CURDIR}/def.objects.h > hack.onames.h
+
+makedefs: makedefs.c
+
+beforeinstall:
+ mkdir -p ${DESTDIR}/var/games/hackdir
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
+ ${DESTDIR}/var/games/hackdir/perm
+.if !exists(${DESTDIR}/var/games/hackdir/record)
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 460 /dev/null \
+ ${DESTDIR}/var/games/hackdir/record
+.endif
+# rm -f ${DESTDIR}/var/games/hackdir/bones* \
+# ${DESTDIR}/var/games/hackdir/save/*
+
+.include <bsd.prog.mk>
diff --git a/hack/Makequest b/hack/Makequest
new file mode 100644
index 0000000..9271c28
--- /dev/null
+++ b/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/hack/OWNER b/hack/OWNER
new file mode 100644
index 0000000..be2d1e5
--- /dev/null
+++ b/hack/OWNER
@@ -0,0 +1,2 @@
+Andries Brouwer
+mcvax!aeb
diff --git a/hack/Original_READ_ME b/hack/Original_READ_ME
new file mode 100644
index 0000000..81ca07a
--- /dev/null
+++ b/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 undoubtedly 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
+ independent 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/hack/READ_ME b/hack/READ_ME
new file mode 100644
index 0000000..cfe6ca2
--- /dev/null
+++ b/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/hack/alloc.c b/hack/alloc.c
new file mode 100644
index 0000000..5d72997
--- /dev/null
+++ b/hack/alloc.c
@@ -0,0 +1,38 @@
+/* alloc.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/alloc.c,v 1.4 1999/11/16 02:57:01 billf Exp $ */
+/* $DragonFly: src/games/hack/alloc.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+#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
+*/
+long *
+alloc(size_t n)
+{
+ long dummy = ftell(stderr);
+
+ if (n)
+ dummy = 0; /* make sure arg is used */
+ return (&dummy);
+}
+
+#else
+
+void *
+alloc(size_t lth)
+{
+ void *ptr;
+
+ if ((ptr = malloc(lth)) == NULL)
+ panic("Cannot get %zd bytes", lth);
+ return (ptr);
+}
+
+#endif /* LINT */
diff --git a/hack/config.h b/hack/config.h
new file mode 100644
index 0000000..2173dcd
--- /dev/null
+++ b/hack/config.h
@@ -0,0 +1,124 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* config.h - version 1.0.3 */
+/* $DragonFly: src/games/hack/config.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include <stdbool.h>
+#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 */
+
+/* #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 */
+#define SUSPEND /* let ^Z suspend the game */
+#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 short 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 bool 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/hack/data b/hack/data
new file mode 100644
index 0000000..5faa384
--- /dev/null
+++ b/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 venomous 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/hack/date.h b/hack/date.h
new file mode 100644
index 0000000..9a7ef76
--- /dev/null
+++ b/hack/date.h
@@ -0,0 +1,2 @@
+
+char datestring[] = "Tue Jul 23 1985";
diff --git a/hack/def.edog.h b/hack/def.edog.h
new file mode 100644
index 0000000..a5c2b46
--- /dev/null
+++ b/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/hack/def.eshk.h b/hack/def.eshk.h
new file mode 100644
index 0000000..2ebf280
--- /dev/null
+++ b/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/hack/def.flag.h b/hack/def.flag.h
new file mode 100644
index 0000000..221f33d
--- /dev/null
+++ b/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/hack/def.func_tab.h b/hack/def.func_tab.h
new file mode 100644
index 0000000..ceb952c
--- /dev/null
+++ b/hack/def.func_tab.h
@@ -0,0 +1,13 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.func_tab.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.func_tab.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+struct func_tab {
+ char f_char;
+ int (*f_funct)(void);
+};
+
+struct ext_func_tab {
+ const char *ef_txt;
+ int (*ef_funct)(void);
+};
diff --git a/hack/def.gold.h b/hack/def.gold.h
new file mode 100644
index 0000000..c279a45
--- /dev/null
+++ b/hack/def.gold.h
@@ -0,0 +1,12 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.gold.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.gold.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */
+
+struct gold {
+ struct gold *ngold;
+ xchar gx,gy;
+ long amount;
+};
+
+extern struct gold *fgold;
+#define newgold() alloc(sizeof(struct gold))
diff --git a/hack/def.mkroom.h b/hack/def.mkroom.h
new file mode 100644
index 0000000..f57f34c
--- /dev/null
+++ b/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/hack/def.monst.h b/hack/def.monst.h
new file mode 100644
index 0000000..8424396
--- /dev/null
+++ b/hack/def.monst.h
@@ -0,0 +1,60 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.monst.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.monst.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+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) alloc((unsigned)(xl) + sizeof(struct monst))
+
+extern struct monst *fmon;
+extern struct monst *fallen_down;
+
+/* 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/hack/def.obj.h b/hack/def.obj.h
new file mode 100644
index 0000000..cbdf091
--- /dev/null
+++ b/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) alloc((unsigned)(xl) + sizeof(struct obj))
+#define ONAME(otmp) ((char *)otmp->oextra)
+#define OGOLD(otmp) (otmp->oextra[0])
diff --git a/hack/def.objclass.h b/hack/def.objclass.h
new file mode 100644
index 0000000..e388554
--- /dev/null
+++ b/hack/def.objclass.h
@@ -0,0 +1,62 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.objclass.h - version 1.0.3 */
+/* $DragonFly: src/games/hack/def.objclass.h,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#ifndef _DEF_OBJCLASS_H_
+#define _DEF_OBJCLASS_H_
+/* definition of a class of objects */
+
+struct objclass {
+ const char *oc_name; /* actual name */
+ const 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:
+ * hack.mklev.c: "=/)%?![<>"[rn2(9)];
+ * hack.mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%";
+ * hack.apply.c: otmp = getobj("0#%", "put in");
+ * hack.eat.c: otmp = getobj("%", "eat");
+ * hack.invent.c: else if (strchr("!%?[()= /\"0", sym)) {
+ */
+#endif /* _DEF_OBJCLASS_H_ */
diff --git a/hack/def.objects.h b/hack/def.objects.h
new file mode 100644
index 0000000..a2416fb
--- /dev/null
+++ b/hack/def.objects.h
@@ -0,0 +1,290 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.objects.h - version 1.0.3 */
+/* $DragonFly: src/games/hack/def.objects.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */
+
+/* objects have letter " % ) ( 0 _ ` [ ! ? / = * */
+#include <string.h>
+#include "config.h"
+#include "def.objclass.h"
+
+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/hack/def.permonst.h b/hack/def.permonst.h
new file mode 100644
index 0000000..591a6c6
--- /dev/null
+++ b/hack/def.permonst.h
@@ -0,0 +1,27 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.permonst.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.permonst.h,v 1.2 2005/05/22 03:37:05 y0netan1 Exp $ */
+
+struct permonst {
+ const char *mname;
+ char 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/hack/def.rm.h b/hack/def.rm.h
new file mode 100644
index 0000000..46f3975
--- /dev/null
+++ b/hack/def.rm.h
@@ -0,0 +1,53 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.rm.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.rm.h,v 1.2 2004/11/06 12:29:17 eirikn Exp $ */
+
+/* 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/hack/def.trap.h b/hack/def.trap.h
new file mode 100644
index 0000000..2d25ec1
--- /dev/null
+++ b/hack/def.trap.h
@@ -0,0 +1,27 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.trap.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.trap.h,v 1.2 2006/08/21 19:45:32 pavalos Exp $ */
+
+struct trap {
+ struct trap *ntrap;
+ xchar tx, ty;
+ unsigned ttyp:5;
+ unsigned tseen:1;
+ unsigned once:1;
+};
+
+extern struct trap *ftrap;
+#define newtrap() 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/hack/def.wseg.h b/hack/def.wseg.h
new file mode 100644
index 0000000..161ead9
--- /dev/null
+++ b/hack/def.wseg.h
@@ -0,0 +1,14 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* def.wseg.h - version 1.0.2 */
+/* $DragonFly: src/games/hack/def.wseg.h,v 1.2 2004/11/06 12:29:17 eirikn Exp $ */
+
+#ifndef NOWORM
+/* worm structure */
+struct wseg {
+ struct wseg *nseg;
+ xchar wx, wy;
+ unsigned wdispl:1;
+};
+
+#define newseg() alloc(sizeof(struct wseg))
+#endif /* NOWORM */
diff --git a/hack/hack.6 b/hack/hack.6
new file mode 100644
index 0000000..239a4ce
--- /dev/null
+++ b/hack/hack.6
@@ -0,0 +1,160 @@
+.\" $FreeBSD: src/games/hack/hack.6,v 1.2.8.1 2001/07/22 11:01:22 dd Exp $
+.\" $DragonFly: src/games/hack/hack.6,v 1.5 2007/10/23 07:51:09 swildner Exp $
+.Dd March 31, 1985
+.Dt HACK 6
+.Os
+.Sh NAME
+.Nm hack
+.Nd exploring The Dungeons of Doom
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar directory
+.Op Fl n
+.Op Fl u Ar playername
+.Nm
+.Op Fl d Ar directory
+.Op Fl s
+.Op Fl X
+.Op Ar playername ...
+.Sh DESCRIPTION
+.Nm
+is a display oriented dungeons \*[Am] 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
+.Ic \&?
+will give you a list of the available commands and the command
+.Ic /
+will identify the things you see on the screen.
+.Pp
+To win the game (as opposed to merely playing to beat other people's 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 heroes.
+.Pp
+When the game ends, either by your death, when you quit, or if you escape
+from the caves,
+.Nm
+will give you (a fragment of) the list of top scorers.
+The scoring is based on many aspects of your behavior 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
+.Fl d
+option, or, if no such option is given, in the directory specified by
+the environment variable
+.Ev 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
+.Pa save
+where games are saved.
+The game administrator may however choose to install
+.Nm
+with a fixed playing ground, usually
+.Pa /var/games/hackdir .
+.Pp
+The
+.Fl n
+option suppresses printing of the news.
+.Pp
+The
+.Fl u Ar playername
+option supplies the answer to the question "Who are you?".
+When
+.Ar playername
+has as suffix one of
+.Em -T ,
+.Em -S ,
+.Em -K ,
+.Em -F ,
+.Em -C ,
+or
+.Em -W ,
+then this supplies the answer to the question "What kind of character ... ?".
+.Pp
+The
+.Fl s
+option will print out the list of your scores.
+It may be followed by arguments
+.Fl 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 ENVIRONMENT
+.Bl -tag -width 24n -compact
+.It Ev USER No or Ev LOGNAME
+Your login name.
+.It Ev HOME
+Your home directory.
+.It Ev SHELL
+Your shell.
+.It Ev TERM
+The type of your terminal.
+.It Ev HACKPAGER, PAGER
+Pager used instead of default pager.
+.It Ev MAIL
+Mailbox file.
+.It Ev MAILREADER
+Reader used instead of default (probably
+.Pa /usr/bin/mail ) .
+.It Ev HACKDIR
+Playground.
+.It Ev HACKOPTIONS
+String predefining several
+.Nm
+options (see help file).
+.El
+.Pp
+Several other environment variables are used in debugging (wizard) mode,
+like
+.Ev GENOCIDED ,
+.Ev INVENT ,
+.Ev MAGIC
+and
+.Ev SHOPTYPE .
+.Sh FILES
+.Bl -tag -width 24n -compact
+.It Pa hack
+The
+.Nm
+program.
+.It Pa data, rumors
+Data files used by
+.Nm .
+.It Pa help, hh
+Help data files.
+.It Pa record
+The list of topscorers.
+.It Pa save
+A subdirectory containing the saved games.
+.It Pa bones_dd
+Descriptions of the ghost and belongings of a deceased adventurer.
+.It Pa xlock.dd
+Description of a dungeon level.
+.It Pa safelock
+Lock file for xlock.
+.It Pa record_lock
+Lock file for record.
+.El
+.Sh AUTHORS
+Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the
+original
+.Nm ,
+very much like
+.Xr rogue 6
+(but full of bugs).
+.Pp
+Andries Brouwer continuously deformed their sources into the current
+version - in fact an entirely different game.
+.Sh BUGS
+Probably infinite.
+Mail complaints to mcvax!aeb .
diff --git a/hack/hack.Decl.c b/hack/hack.Decl.c
new file mode 100644
index 0000000..15f807e
--- /dev/null
+++ b/hack/hack.Decl.c
@@ -0,0 +1,42 @@
+/* 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
+struct mkroom rooms[MAXNROFROOMS + 1];
+coord doors[DOORMAX];
+#endif /* QUEST */
+struct monst *fmon = NULL;
+struct trap *ftrap = NULL;
+struct gold *fgold = NULL;
+struct obj *fobj = NULL, *fcobj = NULL, *invent = NULL, *uwep = NULL, *uarm = NULL,
+ *uarm2 = NULL, *uarmh = NULL, *uarms = NULL, *uarmg = NULL, *uright = NULL,
+ *uleft = NULL, *uchain = NULL, *uball = NULL;
+struct flag flags;
+struct you u;
+struct monst youmonst; /* dummy; used as return value for boomhit */
+
+xchar dlevel = 1;
+xchar xupstair, yupstair, xdnstair, ydnstair;
+const char *save_cm, *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/hack/hack.apply.c b/hack/hack.apply.c
new file mode 100644
index 0000000..7944991
--- /dev/null
+++ b/hack/hack.apply.c
@@ -0,0 +1,467 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.apply.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.apply.c,v 1.4.2.1 2001/02/18 02:20:07 kris Exp $ */
+/* $DragonFly: src/games/hack/hack.apply.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+#include "def.edog.h"
+extern char quitchars[];
+
+static void use_camera(struct obj *);
+static bool in_ice_box(struct obj *);
+static bool ck_ice_box(struct obj *);
+static int out_ice_box(struct obj *);
+static void use_ice_box(struct obj *);
+static struct monst *bchit(int, int, int, char);
+static void use_whistle(struct obj *);
+static void use_magic_whistle(struct obj *);
+static bool dig(void);
+static int use_pick_axe(struct obj *);
+
+int
+doapply(void)
+{
+ struct obj *obj;
+ 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);
+}
+
+static void
+use_camera(struct obj *obj __attribute__((unused)))
+{
+ 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, '!')) != NULL) {
+ 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) {
+ int tmp = dist(mtmp->mx, mtmp->my);
+ 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 bool
+in_ice_box(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(NULL);
+ }
+ 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 bool
+ck_ice_box(struct obj *obj)
+{
+ return (obj->o_cnt_id == current_ice_box->o_id);
+}
+
+static int
+out_ice_box(struct obj *obj)
+{
+ 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 */
+ addinv(obj);
+ return (0);
+}
+
+static void
+use_ice_box(struct obj *obj)
+{
+ int cnt = 0;
+ 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, NULL, 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(int ddx, int ddy, int range, char sym)
+{
+ struct monst *mtmp = NULL;
+ 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);
+}
+
+static void
+use_whistle(struct obj *obj __attribute__((unused)))
+{
+ 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;
+ }
+}
+
+static void
+use_magic_whistle(struct obj *obj __attribute__((unused)))
+{
+ 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
+bool
+dig(void)
+{
+ struct rm *lev;
+ int 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) {
+ 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) {
+ const char *digtxt;
+ struct obj *obj;
+
+ lev = &levl[dpx][dpy];
+ if ((obj = sobj_at(ENORMOUS_ROCK, dpx, dpy)) != NULL) {
+ 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("%s", digtxt); /* after mnewsym & prl */
+ return (0);
+ } else {
+ if (IS_WALL(levl[dpx][dpy].typ)) {
+ 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. */
+int
+holetime(void)
+{
+ return ((occupation == dig) ? (250 - dig_effort) / 20 : -1);
+}
+
+void
+dighole(void)
+{
+ 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 int
+use_pick_axe(struct obj *obj)
+{
+ char dirsyms[12];
+ char *dsp = dirsyms, *sdp = sdir;
+ struct monst *mtmp;
+ struct rm *lev;
+ 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) {
+ 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, NULL));
+ } 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/hack/hack.bones.c b/hack/hack.bones.c
new file mode 100644
index 0000000..97ecc0f
--- /dev/null
+++ b/hack/hack.bones.c
@@ -0,0 +1,108 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.bones.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.bones.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.bones.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+char bones[] = "bones_xx";
+
+/* save bones and possessions of a deceased adventurer */
+void
+savebones(void)
+{
+ int fd;
+ struct obj *otmp;
+ struct trap *ttmp;
+ struct monst *mtmp;
+
+ if (dlevel <= 0 || dlevel > MAXLEVEL)
+ return;
+ if (!rn2(1 + dlevel / 2)) /* not so many ghosts on low levels */
+ return;
+ bones[6] = '0' + (dlevel / 10);
+ bones[7] = '0' + (dlevel % 10);
+ if ((fd = open(bones, O_RDONLY)) >= 0) {
+ 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;
+ 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);
+ close(fd);
+}
+
+int
+getbones(void)
+{
+ int fd, x, y, ok;
+
+ if (rn2(3)) /* only once in three times do we find bones */
+ return (0);
+ bones[6] = '0' + dlevel / 10;
+ bones[7] = '0' + dlevel % 10;
+ if ((fd = open(bones, O_RDONLY)) < 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;
+ }
+ 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/hack/hack.c b/hack/hack.c
new file mode 100644
index 0000000..56aa11f
--- /dev/null
+++ b/hack/hack.c
@@ -0,0 +1,904 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */
+
+#include "hack.h"
+
+static void movobj(struct obj *, int, int);
+#ifdef QUEST
+static bool rroom(int, int);
+#endif
+static int inv_cnt(void);
+
+/* called on movement:
+ * 1. when throwing ball+chain far away
+ * 2. when teleporting
+ * 3. when walking out of a lit room
+ */
+void
+unsee(void)
+{
+ int x, y;
+ struct rm *lev;
+
+#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
+ * mode:
+ * 1 to redo @, 0 to leave them *//* 1 means
+ * misc movement, 0 means blindness
+ */
+void
+seeoff(bool mode)
+{
+ int x, y;
+ 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;
+ }
+ }
+}
+
+void
+domove(void)
+{
+ xchar oldx, oldy;
+ struct monst *mtmp = NULL;
+ struct rm *tmpr, *ust;
+ struct trap *trap = NULL;
+ 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) /* we just fainted */
+ return;
+
+ /* 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)) != NULL) {
+ struct trap *ttmp;
+ xchar rx = u.ux + 2 * u.dx, ry = u.uy + 2 * u.dy;
+ 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)) != NULL)
+ 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;
+ 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 (!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. */
+ inshop();
+ if (!Blind)
+ read_engr_at(u.ux, u.uy);
+}
+
+static void
+movobj(struct obj *obj, int ox, int oy)
+{
+ /* Some dirty programming to get display right */
+ freeobj(obj);
+ unpobj(obj);
+ obj->nobj = fobj;
+ fobj = obj;
+ obj->ox = ox;
+ obj->oy = oy;
+}
+
+int
+dopickup(void)
+{
+ 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);
+}
+
+void
+pickup(int all)
+{
+ struct gold *gold;
+ struct obj *obj, *obj2;
+ 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) {
+ 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 (!strchr("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 */
+ 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));
+ 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 accommodate 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) /* this is done by prinv(), */
+ obj->dknown = 1;/* 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 */
+void
+lookaround(void)
+{
+ int x, y, i, x0, y0, m0, i0 = 9;
+ int corrct = 0, noturn = 0;
+ struct monst *mtmp;
+
+ /* suppress "used before set" message */
+ x0 = y0 = m0 = 0;
+ 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 /* invisible M cannot influence us */
+ mtmp = NULL;
+ 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) /* if you must */
+ goto corr;
+ 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) /* d */
+ break;
+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 */
+bool
+monster_nearby(void)
+{
+ int x, y;
+ 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 &&
+ !strchr("Ea", mtmp->data->mlet) &&
+ !mtmp->mfroz && !mtmp->msleep && /* aplvax!jcn */
+ (!mtmp->minvis || See_invisible))
+ return (1);
+ }
+ return (0);
+}
+
+#ifdef QUEST
+bool
+cansee(xchar x, xchar y)
+{
+ 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);
+ }
+}
+
+static bool
+rroom(int x, int y)
+{
+ return (IS_ROOM(levl[u.ux + x][u.uy + y].typ));
+}
+
+#else
+
+bool
+cansee(xchar x, xchar 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 */
+
+int
+sgn(int a)
+{
+ return ((a > 0) ? 1 : (a == 0) ? 0 : -1);
+}
+
+#ifdef QUEST
+void
+setsee(void)
+{
+ int 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
+
+void
+setsee(void)
+{
+ int 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) /* seems necessary elsewhere */
+ seehx = 0;
+ 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 */
+
+void
+nomul(int nval)
+{
+ if (multi < 0)
+ return;
+ multi = nval;
+ flags.mv = flags.run = 0;
+}
+
+int
+abon(void)
+{
+ 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) /* up to 18/50 */
+ return (1);
+ else if (u.ustr < 118)
+ return (2);
+ else
+ return (3);
+}
+
+int
+dbon(void)
+{
+ 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) /* up to 18 */
+ return (2);
+ else if (u.ustr < 94) /* up to 18/75 */
+ return (3);
+ else if (u.ustr < 109) /* up to 18/90 */
+ return (4);
+ else if (u.ustr < 118) /* up to 18/99 */
+ return (5);
+ else
+ return (6);
+}
+
+/* may kill you; cause may be poison or monster like 'A' */
+void
+losestr(int num)
+{
+ u.ustr -= num;
+ while (u.ustr < 3) {
+ u.ustr++;
+ u.uhp -= 6;
+ u.uhpmax -= 6;
+ }
+ flags.botl = 1;
+}
+
+void
+losehp(int n, const 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");
+ }
+}
+
+void
+losehp_m(int n, struct monst *mtmp)
+{
+ u.uhp -= n;
+ flags.botl = 1;
+ if (u.uhp < 1)
+ done_in_by(mtmp);
+}
+
+void
+losexp(void) /* hit by V or W */
+{
+ int num;
+
+ 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;
+}
+
+int
+inv_weight(void)
+{
+ struct obj *otmp = invent;
+ int wt = (u.ugold + 500) / 1000;
+ 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);
+}
+
+static int
+inv_cnt(void)
+{
+ struct obj *otmp = invent;
+ int ct = 0;
+
+ while (otmp) {
+ ct++;
+ otmp = otmp->nobj;
+ }
+ return (ct);
+}
+
+long
+newuexp(void)
+{
+ return (10 * (1L << (u.ulevel - 1)));
+}
diff --git a/hack/hack.cmd.c b/hack/hack.cmd.c
new file mode 100644
index 0000000..d2aaed3
--- /dev/null
+++ b/hack/hack.cmd.c
@@ -0,0 +1,332 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.cmd.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.cmd.c,v 1.4 1999/11/16 10:26:35 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.cmd.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+#include "def.func_tab.h"
+
+static int doextcmd(void);
+static char lowc(char);
+static char unctrl(char);
+#ifdef QUEST
+static bool isroom(int, int);
+#endif
+static int done2(void);
+
+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', done2 },
+ { '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 }
+};
+
+struct ext_func_tab extcmdlist[] = {
+ { "dip", dodip },
+ { "pray", dopray },
+ { NULL, donull }
+};
+
+extern char quitchars[];
+
+void
+rhack(const char *cmd)
+{
+ struct func_tab *tlist = cmdlist;
+ boolean firsttime = FALSE;
+ int 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];
+ char *cp = expcmd;
+ while (*cmd && cp - expcmd < (int)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;
+}
+
+static int
+doextcmd(void) /* here after # - now read a full-word command */
+{
+ char buf[BUFSZ];
+ 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);
+}
+
+static char
+lowc(char sym)
+{
+ return ((sym >= 'A' && sym <= 'Z') ? sym + 'a' - 'A' : sym);
+}
+
+static char
+unctrl(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 };
+
+bool
+movecmd(char sym) /* also sets u.dz, but returns false for <> */
+{
+ char *dp;
+
+ u.dz = 0;
+ if (!(dp = strchr(sdir, sym)))
+ return (0);
+ u.dx = xdir[dp - sdir];
+ u.dy = ydir[dp - sdir];
+ u.dz = zdir[dp - sdir];
+ return (!u.dz);
+}
+
+bool
+getdir(bool s)
+{
+ char dirsym;
+
+ if (s)
+ pline("In what direction?");
+ dirsym = readchar();
+ if (!movecmd(dirsym) && !u.dz) {
+ if (!strchr(quitchars, dirsym))
+ pline("What a strange direction!");
+ return (0);
+ }
+ if (Confusion && !u.dz)
+ confdir();
+ return (1);
+}
+
+void
+confdir(void)
+{
+ int x = rn2(8);
+
+ u.dx = xdir[x];
+ u.dy = ydir[x];
+}
+
+#ifdef QUEST
+void
+finddir(void)
+{
+ 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;
+ }
+ 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;
+ }
+ if (isroom(u.ux + xdir[ui], u.uy + ydir[ui]))
+ break;
+ }
+ u.di = ui;
+ u.dx = xdir[ui];
+ u.dy = ydir[ui];
+}
+
+static bool
+isroom(int x, int y)
+{ /* what about POOL? */
+ return (isok(x, y) && (levl[x][y].typ == ROOM ||
+ (levl[x][y].typ >= LDOOR && flags.run >= 6)));
+}
+#endif /* QUEST */
+
+bool
+isok(int x, int y)
+{
+ /* x corresponds to curx, so x==1 is the first column. Ach. %% */
+ return (x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1);
+}
+
+/*
+ * done2 is a function that fits into cmdlist[] (int func(void))
+ * and calls done1 which discards its argument.
+ */
+static int
+done2(void)
+{
+ done1(0);
+ return (0);
+}
diff --git a/hack/hack.do.c b/hack/hack.do.c
new file mode 100644
index 0000000..8d6277d
--- /dev/null
+++ b/hack/hack.do.c
@@ -0,0 +1,512 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.do.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.do.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.do.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
+
+/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) and 't' (throw) */
+
+#include "hack.h"
+
+extern struct monst youmonst;
+
+static int drop(struct obj *);
+static void dropy(struct obj *);
+
+int
+dodrop(void)
+{
+ return (drop(getobj("0$#", "drop")));
+}
+
+static int
+drop(struct obj *obj)
+{
+ if (!obj)
+ return (0);
+ if (obj->olet == '$') { /* pseudo object */
+ 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(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(NULL);
+ }
+ pline("You dropped %s.", doname(obj));
+ dropx(obj);
+ return (1);
+}
+
+/* Called in several places - should not produce texts */
+void
+dropx(struct obj *obj)
+{
+ freeinv(obj);
+ dropy(obj);
+}
+
+static void
+dropy(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 */
+int
+doddrop(void)
+{
+ return (ggetobj("drop", drop, 0));
+}
+
+int
+dodown(void)
+{
+ 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);
+}
+
+int
+doup(void)
+{
+ 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);
+}
+
+void
+goto_level(int newlevel, boolean at_stairs)
+{
+ int fd;
+ boolean up = (newlevel < dlevel);
+
+ if (newlevel <= 0) /* in fact < 0 is impossible */
+ done("escaped");
+ if (newlevel > MAXLEVEL) /* strange ... */
+ newlevel = MAXLEVEL;
+ if (newlevel == dlevel) /* this can happen */
+ return;
+
+ 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 */
+ inshop(); /* probably was a trapdoor */
+
+ savelev(fd, dlevel);
+ close(fd);
+
+ dlevel = newlevel;
+ if (maxdlevel < dlevel)
+ maxdlevel = dlevel;
+ glo(dlevel);
+
+ if (!level_exists[dlevel])
+ mklev();
+ else {
+ if ((fd = open(lock, O_RDONLY)) < 0) {
+ pline("Cannot open %s .", lock);
+ pline("Probably someone removed it.");
+ done("tricked");
+ }
+ getlev(fd, hackpid, dlevel);
+ 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");
+ }
+ }
+ {
+ 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");
+ }
+ inshop();
+ initrack();
+
+ losedogs();
+ {
+ struct monst *mtmp;
+ if ((mtmp = m_at(u.ux, u.uy))) /* riv05!a3 */
+ mnexto(mtmp);
+ }
+ flags.nscrinh = 0;
+ setsee();
+ seeobjs(); /* make old cadavers disappear - riv05!a3 */
+ docrt();
+ pickup(1);
+ read_engr_at(u.ux, u.uy);
+}
+
+int
+donull(void)
+{
+ return (1); /* Do nothing, but let other things happen */
+}
+
+int
+dopray(void)
+{
+ nomovemsg = "You finished your prayer.";
+ nomul(-3);
+ return (1);
+}
+
+int
+dothrow(void)
+{
+ struct obj *obj;
+ struct monst *mon;
+ int 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(NULL);
+ } else if (obj->quan > 1)
+ 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);
+ } else if (obj->otyp == EGG) {
+ pline("\"Splash!\"");
+ obfree(obj, NULL);
+ } else if (obj->olet == POTION_SYM) {
+ pline("The flask breaks, and you smell a peculiar odor ...");
+ potionbreathe(obj);
+ obfree(obj, NULL);
+ } else
+ dropy(obj);
+ }
+ return (1);
+ } else if (obj->otyp == BOOMERANG) {
+ mon = boomhit(u.dx, u.dy);
+ if (mon == &youmonst) { /* the thing was caught */
+ 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, (void (*)(struct monst *, struct obj *)) 0,
+ (bool (*)(struct obj *, struct obj *)) 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, NULL);
+ 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 {
+ 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();
+ 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(struct obj *obj, int num)
+{
+ 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);
+}
+
+void
+more_experienced(int exp, int rexp)
+{
+ 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;
+}
+
+void
+set_wounded_legs(long side, int timex)
+{
+ if (!Wounded_legs || (Wounded_legs & TIMEOUT))
+ Wounded_legs |= side + timex;
+ else
+ Wounded_legs |= side;
+}
+
+void
+heal_legs(void)
+{
+ 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/hack/hack.do_name.c b/hack/hack.do_name.c
new file mode 100644
index 0000000..38ef837
--- /dev/null
+++ b/hack/hack.do_name.c
@@ -0,0 +1,316 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.do_name.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.do_name.c,v 1.5 1999/11/16 10:26:36 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.do_name.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+static void do_oname(struct obj *);
+static char *xmonnam(struct monst *, int);
+static char *lmonnam(struct monst *);
+static char *visctrl(char);
+
+coord
+getpos(int force, const char *goal)
+{
+ int cx, cy, i, c;
+ 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);
+}
+
+int
+do_mname(void)
+{
+ char buf[BUFSZ];
+ coord cc;
+ int cx, cy, lth, i;
+ struct monst *mtmp, *mtmp2;
+
+ 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; (unsigned)i < mtmp->mxlth; i++)
+ ((char *)mtmp2->mextra)[i] = ((char *)mtmp->mextra)[i];
+ mtmp2->mnamelth = lth;
+ 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.
+ */
+static void
+do_oname(struct obj *obj)
+{
+ struct obj *otmp, *otmp2;
+ int 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;
+ strcpy(ONAME(otmp2), buf);
+
+ setworn(NULL, 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(obj); /* let us hope nobody else saved a pointer */
+}
+
+int
+ddocall(void)
+{
+ 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);
+}
+
+void
+docall(struct obj *obj)
+{
+ char buf[BUFSZ];
+ struct obj otemp;
+ char **str1;
+ char *str;
+
+ otemp = *obj;
+ otemp.quan = 1;
+ otemp.onamelth = 0;
+ str = xname(&otemp);
+ pline("Call %s %s: ", strchr(vowels, *str) ? "an" : "a", str);
+ getlin(buf);
+ clrlin();
+ if (!*buf || *buf == '\033')
+ return;
+ str = newstring(strlen(buf) + 1);
+ strcpy(str, buf);
+ str1 = &(objects[obj->otyp].oc_uname);
+ if (*str1)
+ free(*str1);
+ *str1 = str;
+}
+
+/* these names should have length < PL_NSIZ */
+const char *ghostnames[] = {
+ "adri", "andries", "andreas", "bert", "david", "dirk", "emile",
+ "frans", "fred", "greg", "hether", "jay", "john", "jon", "kay",
+ "kenny", "maud", "michiel", "mike", "peter", "robert", "ron",
+ "tom", "wilmar"
+};
+
+static char *
+xmonnam(struct monst *mtmp, int vb)
+{
+ static char buf[BUFSZ]; /* %% */
+
+ if (mtmp->mnamelth && !vb) {
+ strcpy(buf, NAME(mtmp));
+ return (buf);
+ }
+ switch (mtmp->data->mlet) {
+ case ' ':
+ {
+ const char *gn = (const char *)mtmp->mextra;
+ if (!*gn) { /* might also look in scorefile */
+ gn = ghostnames[rn2(SIZE(ghostnames))];
+ if (!rn2(2))
+ strcpy((char *)mtmp->mextra,
+ !rn2(5) ? plname : gn);
+ }
+ sprintf(buf, "%s's ghost", gn);
+ }
+ break;
+ case '@':
+ if (mtmp->isshk) {
+ strcpy(buf, shkname(mtmp));
+ break;
+ }
+ /* fall into next case */
+ default:
+ sprintf(buf, "the %s%s",
+ mtmp->minvis ? "invisible " : "",
+ mtmp->data->mname);
+ }
+ if (vb && mtmp->mnamelth) {
+ strcat(buf, " called ");
+ strcat(buf, NAME(mtmp));
+ }
+ return (buf);
+}
+
+static char *
+lmonnam(struct monst *mtmp)
+{
+ return (xmonnam(mtmp, 1));
+}
+
+char *
+monnam(struct monst *mtmp)
+{
+ return (xmonnam(mtmp, 0));
+}
+
+char *
+Monnam(struct monst *mtmp)
+{
+ char *bp = monnam(mtmp);
+
+ if ('a' <= *bp && *bp <= 'z')
+ *bp += ('A' - 'a');
+ return (bp);
+}
+
+char *
+amonnam(struct monst *mtmp, const char *adj)
+{
+ char *bp = monnam(mtmp);
+ static char buf[BUFSZ]; /* %% */
+
+ if (!strncmp(bp, "the ", 4))
+ bp += 4;
+ sprintf(buf, "the %s %s", adj, bp);
+ return (buf);
+}
+
+char *
+Amonnam(struct monst *mtmp, const char *adj)
+{
+ char *bp = amonnam(mtmp, adj);
+
+ *bp = 'T';
+ return (bp);
+}
+
+char *
+Xmonnam(struct monst *mtmp)
+{
+ char *bp = Monnam(mtmp);
+
+ if (!strncmp(bp, "The ", 4)) {
+ bp += 2;
+ *bp = 'A';
+ }
+ return (bp);
+}
+
+static char *
+visctrl(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/hack/hack.do_wear.c b/hack/hack.do_wear.c
new file mode 100644
index 0000000..c7f7802
--- /dev/null
+++ b/hack/hack.do_wear.c
@@ -0,0 +1,395 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.do_wear.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.do_wear.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */
+/* $DragonFly: src/games/hack/hack.do_wear.c,v 1.6 2008/04/20 13:44:24 swildner Exp $ */
+
+#include "hack.h"
+extern char quitchars[];
+
+static void off_msg(struct obj *);
+static int dorr(struct obj *);
+static bool cursed(struct obj *);
+
+static void
+off_msg(struct obj *otmp)
+{
+ pline("You were wearing %s.", doname(otmp));
+}
+
+int
+doremarm(void)
+{
+ 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);
+ }
+ armoroff(otmp);
+ return (1);
+}
+
+int
+doremring(void)
+{
+ if (!uleft && !uright) {
+ pline("Not wearing any ring.");
+ return (0);
+ }
+ if (!uleft)
+ return (dorr(uright));
+ if (!uright)
+ return (dorr(uleft));
+ if (uleft && uright)
+ for (;;) {
+ char answer;
+
+ pline("What ring, Right or Left? [ rl?]");
+ if (strchr(quitchars, (answer = readchar())))
+ return (0);
+ switch (answer) {
+ case 'l':
+ case 'L':
+ return (dorr(uleft));
+ case 'r':
+ case 'R':
+ return (dorr(uright));
+ case '?':
+ doprring();
+ /* might look at morc here %% */
+ }
+ }
+ /* NOTREACHED */
+ return (0);
+}
+
+static int
+dorr(struct obj *otmp)
+{
+ if (cursed(otmp))
+ return (0);
+ ringoff(otmp);
+ off_msg(otmp);
+ return (1);
+}
+
+static bool
+cursed(struct obj *otmp)
+{
+ if (otmp->cursed) {
+ pline("You can't. It appears to be cursed.");
+ return (1);
+ }
+ return (0);
+}
+
+bool
+armoroff(struct obj *otmp)
+{
+ int delay = -objects[otmp->otyp].oc_delay;
+
+ if (cursed(otmp))
+ return (0);
+ setworn(NULL, 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);
+}
+
+int
+doweararm(void)
+{
+ struct obj *otmp;
+ int delay;
+ 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(NULL);
+ delay = -objects[otmp->otyp].oc_delay;
+ if (delay) {
+ nomul(delay);
+ nomovemsg = "You finished your dressing manoeuvre.";
+ }
+ otmp->known = 1;
+ return (1);
+}
+
+int
+dowearring(void)
+{
+ 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 (strchr(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(NULL);
+ 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);
+}
+
+void
+ringoff(struct obj *obj)
+{
+ long mask;
+
+ mask = obj->owornmask & W_RING;
+ setworn(NULL, obj->owornmask);
+ if (!(u.uprops[PROP(obj->otyp)].p_flgs & mask))
+ impossible("Strange... I didn't 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;
+ }
+}
+
+void
+find_ac(void)
+{
+ 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;
+ }
+}
+
+void
+glibr(void)
+{
+ 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) {
+ ringoff(uleft);
+ dropx(otmp);
+ }
+ if ((otmp = uright) != NULL) {
+ ringoff(uright);
+ dropx(otmp);
+ }
+ }
+ if ((otmp = uwep) != NULL) {
+ /* Note: at present also cursed weapons fall */
+ setuwep(NULL);
+ dropx(otmp);
+ pline("Your weapon %sslips from your hands.",
+ xfl ? "also " : "");
+ }
+}
+
+struct obj *
+some_armor(void)
+{
+ 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);
+}
+
+void
+corrode_armor(void)
+{
+ 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/hack/hack.dog.c b/hack/hack.dog.c
new file mode 100644
index 0000000..531a3a4
--- /dev/null
+++ b/hack/hack.dog.c
@@ -0,0 +1,459 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.dog.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.dog.c,v 1.3 1999/11/16 02:57:03 billf Exp $ */
+
+#include "hack.h"
+#include "hack.mfndpos.h"
+#include "def.edog.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) };
+
+static void initedog(struct monst *);
+static xchar dogfood(struct obj *);
+
+void
+makedog(void)
+{
+ struct monst *mtmp = makemon(&li_dog, u.ux, u.uy);
+
+ if (!mtmp)
+ return; /* dogs were genocided */
+ initedog(mtmp);
+}
+
+static void
+initedog(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 = NULL;
+struct monst *fallen_down = NULL; /* monsters that fell through a trapdoor */
+/* they will appear on the next level @ goes to, even if he goes up! */
+
+void
+losedogs(void)
+{
+ 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);
+ }
+}
+
+void
+keepdogs(void)
+{
+ 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) */
+ }
+}
+
+void
+fall_down(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
+
+static xchar
+dogfood(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) */
+int
+dog_move(struct monst *mtmp, int after)
+{
+ int nx, ny, omx, omy, appr, nearer, j;
+ int udist, chi = 0, i, whappr;
+ struct monst *mtmp2;
+ struct permonst *mdat = mtmp->data;
+ 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) /* dog is still eating */
+ return (0);
+ 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) < (int)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 (!strchr("0_", obj->olet)) {
+ if ((otyp = dogfood(obj)) <= CADAVER) {
+ nix = omx;
+ niy = omy;
+ goto eatobj;
+ }
+ if (obj->owt < 10 * mtmp->data->mlevel) {
+ if (rn2(20) < (int)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 */
+ gx = gy = 0; /* suppress 'used before set' message */
+ 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 && (int)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 /* gtyp != UNDEF */
+ appr = 1;
+ if (mtmp->mconf)
+ appr = 0;
+
+ if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) {
+ 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) /* hit only once each move */
+ return (0);
+
+ 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) {
+ 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 */
+int
+inroom(xchar x, xchar y)
+{
+#ifndef QUEST
+ 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 */
+}
+
+bool
+tamedog(struct monst *mtmp, struct obj *obj)
+{
+ 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 || strchr(" &@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, NULL);
+ }
+ mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
+ *mtmp2 = *mtmp;
+ mtmp2->mxlth = sizeof(struct edog);
+ if (mtmp->mnamelth)
+ strcpy(NAME(mtmp2), NAME(mtmp));
+ initedog(mtmp2);
+ replmon(mtmp, mtmp2);
+ return (1);
+}
diff --git a/hack/hack.eat.c b/hack/hack.eat.c
new file mode 100644
index 0000000..6373bc5
--- /dev/null
+++ b/hack/hack.eat.c
@@ -0,0 +1,492 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.eat.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.eat.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+
+#include "hack.h"
+char POISONOUS[] = "ADKSVabhks";
+
+static bool opentin(void);
+static void Meatdone(void);
+static void unfaint(void);
+static void newuhs(bool);
+static int eatcorpse(struct obj *);
+
+/* 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
+
+const char *hu_stat[] = {
+ "Satiated",
+ " ",
+ "Hungry ",
+ "Weak ",
+ "Fainting",
+ "Fainted ",
+ "Starved "
+};
+
+void
+init_uhunger(void)
+{
+ u.uhunger = 900;
+ u.uhs = NOT_HUNGRY;
+}
+
+#define TTSZ SIZE(tintxts)
+struct { const 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;
+
+static bool
+opentin(void)
+{
+ 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("%s", 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);
+}
+
+static void
+Meatdone(void)
+{
+ u.usym = '@';
+ prme();
+}
+
+int
+doeat(void)
+{
+ struct obj *otmp;
+ struct objclass *ftmp;
+ int 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)
+ 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, NULL));
+ } 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) {
+ struct obj *obj;
+
+ 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];
+ sprintf(msgbuf, "You finished eating the %s.", ftmp->oc_name);
+ nomovemsg = msgbuf;
+ }
+ useup(otmp);
+ return (1);
+}
+
+/* called in hack.main.c */
+void
+gethungry(void)
+{
+ --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 */
+void
+morehungry(int num)
+{
+ u.uhunger -= num;
+ newuhs(TRUE);
+}
+
+/* called after eating something (and after drinking fruit juice) */
+void
+lesshungry(int num)
+{
+ u.uhunger += num;
+ newuhs(FALSE);
+}
+
+static void
+unfaint(void)
+{
+ u.uhs = FAINTING;
+ flags.botl = 1;
+}
+
+static void
+newuhs(bool incr)
+{
+ 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))
+bool
+poisonous(struct obj *otmp)
+{
+ return (strchr(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0);
+}
+
+/* returns 1 if some text was printed */
+static int
+eatcorpse(struct obj *otmp)
+{
+ char let = CORPSE_I_TO_C(otmp->otyp);
+ int 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 (strchr(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 (strchr("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/hack/hack.end.c b/hack/hack.end.c
new file mode 100644
index 0000000..46b3002
--- /dev/null
+++ b/hack/hack.end.c
@@ -0,0 +1,725 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.end.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.end.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+
+#include "hack.h"
+#define Sprintf (void) sprintf
+
+#define newttentry() 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;
+
+static void done_intr(int);
+static void done_hangup(int);
+static void topten(void);
+static void outheader(void);
+static int outentry(int, struct toptenentry *, int);
+static char *itoa(int);
+static const char *ordin(int);
+
+xchar maxdlevel = 1;
+
+void
+done1(int unused __attribute__((unused)))
+{
+ signal(SIGINT, SIG_IGN);
+ pline("Really quit?");
+ if (readchar() != 'y') {
+ signal(SIGINT, done1);
+ clrlin();
+ fflush(stdout);
+ if (multi > 0)
+ nomul(0);
+ return;
+ }
+ done("quit");
+ /* NOTREACHED */
+}
+
+int done_stopprint;
+int done_hup;
+
+static void
+done_intr(int unused __attribute__((unused)))
+{
+ done_stopprint++;
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+}
+
+static void
+done_hangup(int unused __attribute__((unused)))
+{
+ done_hup++;
+ signal(SIGHUP, SIG_IGN);
+ done_intr(0);
+}
+
+void
+done_in_by(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! */
+void
+done(const char *st1)
+{
+#ifdef WIZARD
+ if (wizard && *st1 == 'd') {
+ u.uswldtim = 0;
+ if (u.uhpmax < 0) /* arbitrary */
+ u.uhpmax = 100;
+ 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 */
+ signal(SIGINT, done_intr);
+ signal(SIGQUIT, done_intr);
+ 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 (!strchr("bcd", *st1))
+ killer = st1;
+ paybill();
+ clearlocks();
+ if (flags.toplin == 1)
+ more();
+ if (strchr("bcds", *st1)) {
+#ifdef WIZARD
+ if (!wizard)
+#endif /* WIZARD */
+ savebones();
+ if (!flags.notombstone)
+ outrip();
+ }
+ if (*st1 == 'c') /* after outrip() */
+ killer = st1;
+ settty(NULL); /* 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') {
+ struct monst *mtmp;
+ struct obj *otmp;
+ int i;
+ 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);
+}
+
+static void
+topten(void)
+{
+ int uid = getuid();
+ int rank, rank0 = -1, rank1 = 0;
+ int occ_cnt = PERSMAX;
+ struct toptenentry *t0, *t1, *tprev;
+ const char *recfile = RECORD;
+ const char *reclock = "record_lock";
+ int sleepct = 300;
+ FILE *rfile;
+ int flg = 0;
+
+#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 fflush(stdout);
+ sleep(1);
+ }
+ if (!(rfile = fopen(recfile, "r"))) {
+ HUP puts("Cannot open record file!");
+ goto unlock;
+ }
+ HUP 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;
+ strncpy(t0->name, plname, NAMSZ);
+ (t0->name)[NAMSZ] = 0;
+ strncpy(t0->death, killer, DTHSZ);
+ (t0->death)[DTHSZ] = 0;
+ strcpy(t0->date, getdate());
+
+ /* assure minimum number of points */
+ if (t0->points < POINTSMIN)
+ t0->points = 0;
+
+ t1 = tt_head = newttentry();
+ tprev = NULL;
+ /* 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 == NULL)
+ 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 */
+ 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 > (int)flags.end_top &&
+ (rank < rank0 - (int)flags.end_around || rank > rank0 +
+ (int)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 - (int)flags.end_around &&
+ rank0 > (int)flags.end_top + (int)flags.end_around + 1 &&
+ !flags.end_own)
+ putchar('\n');
+ if (rank != rank0)
+ outentry(rank, t1, 0);
+ else if (!rank1)
+ outentry(rank, t1, 1);
+ else {
+ int t0lth = outentry(0, t0, -1);
+ int t1lth = outentry(rank, t1, t0lth);
+ if (t1lth > t0lth)
+ t0lth = t1lth;
+ outentry(0, t0, t0lth);
+ }
+ }
+ if (rank0 >= rank)
+ if (!done_stopprint)
+ outentry(0, t0, 1);
+ fclose(rfile);
+unlock:
+ unlink(reclock);
+}
+
+static void
+outheader(void)
+{
+ char linebuf[BUFSZ];
+ char *bp;
+
+ strcpy(linebuf, "Number Points Name");
+ bp = eos(linebuf);
+ while (bp < linebuf + COLNO - 9)
+ *bp++ = ' ';
+ strcpy(bp, "Hp [max]");
+ puts(linebuf);
+}
+
+/* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */
+static int
+outentry(int rank, struct toptenentry *t1, int so)
+{
+ boolean quit = FALSE, dead = 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");
+ dead = TRUE;
+ }
+ Sprintf(eos(linebuf), " on%s level %d",
+ (dead || starv) ? "" : " dungeon", t1->level);
+ if (t1->maxlvl != t1->level)
+ Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
+ if (quit && t1->death[4])
+ Sprintf(eos(linebuf), "%s", t1->death + 4);
+ }
+ if (dead) {
+ Sprintf(eos(linebuf), " by %s%s",
+ (!strncmp(t1->death, "trick", 5) ||
+ !strncmp(t1->death, "the ", 4))
+ ? "" :
+ strchr(vowels, *t1->death) ? "an " : "a ",
+ t1->death);
+ }
+ Sprintf(eos(linebuf), ".");
+ if (t1->maxhp) {
+ char *bp = eos(linebuf);
+ char hpbuf[10];
+ int hppos;
+
+ Sprintf(hpbuf, "%s", (t1->hp > 0) ? itoa(t1->hp) : "-");
+ hppos = COLNO - 7 - strlen(hpbuf);
+ if (bp <= linebuf + hppos) {
+ while (bp < linebuf + hppos)
+ *bp++ = ' ';
+ strcpy(bp, hpbuf);
+ Sprintf(eos(bp), " [%d]", t1->maxhp);
+ }
+ }
+ if (so == 0)
+ puts(linebuf);
+ else if (so > 0) {
+ char *bp = eos(linebuf);
+ if (so >= COLNO)
+ so = COLNO - 1;
+ while (bp < linebuf + so)
+ *bp++ = ' ';
+ *bp = 0;
+ standoutbeg();
+ fputs(linebuf, stdout);
+ standoutend();
+ putchar('\n');
+ }
+ return (strlen(linebuf));
+}
+
+static char *
+itoa(int a)
+{
+ static char buf[12];
+
+ Sprintf(buf, "%d", a);
+ return (buf);
+}
+
+static const char *
+ordin(int n)
+{
+ int d1 = n % 10;
+
+ return ((d1 == 0 || d1 > 3 || n / 10 == 1) ? "th" : (d1 == 1) ? "st" :
+ (d1 == 2) ? "nd" : "rd");
+}
+
+void
+clearlocks(void)
+{
+ int x;
+
+ signal(SIGHUP, SIG_IGN);
+ for (x = maxdlevel; x >= 0; x--) {
+ glo(x);
+ unlink(lock); /* not all levels need be present */
+ }
+}
+
+#ifdef NOSAVEONHANGUP
+void
+hangup(int unused __attribute__((unused)))
+{
+ signal(SIGINT, SIG_IGN);
+ clearlocks();
+ exit(1);
+}
+#endif /* NOSAVEONHANGUP */
+
+char *
+eos(char *s)
+{
+ while (*s)
+ s++;
+ return (s);
+}
+
+/* it is the callers responsibility to check that there is room for c */
+void
+charcat(char *s, char 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).
+ */
+void
+prscore(int argc, char **argv)
+{
+ char **players = NULL;
+ int playerct;
+ int rank;
+ struct toptenentry *t1, *t2;
+ const char *recfile = RECORD;
+ FILE *rfile;
+ int flg = 0;
+ 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] && strchr("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();
+ }
+ 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)
+ outentry(rank, t1, 0);
+#ifdef nonsense
+ total_score += t1->points;
+ if (totcharct < sizeof(totchars) - 1)
+ totchars[totcharct++] = t1->plchar;
+#endif /* nonsense */
+ break;
+ }
+ }
+ free(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 (!strchr(totchars, "CFKSTWX"[i])) {
+ flags.beginner = 1;
+ if (!pl_character[0])
+ pl_character[0] = "CFKSTWX"[i];
+ break;
+ }
+#endif /* nonsense */
+}
diff --git a/hack/hack.engrave.c b/hack/hack.engrave.c
new file mode 100644
index 0000000..a319fa9
--- /dev/null
+++ b/hack/hack.engrave.c
@@ -0,0 +1,337 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.engrave.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.engrave.c,v 1.4 1999/11/16 02:57:04 billf Exp $ */
+
+#include "hack.h"
+
+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;
+
+static struct engr *engr_at(xchar, xchar);
+static void del_engr(struct engr *);
+
+static struct engr *
+engr_at(xchar x, xchar y)
+{
+ struct engr *ep = head_engr;
+
+ while (ep) {
+ if (x == ep->engr_x && y == ep->engr_y)
+ return(ep);
+ ep = ep->nxt_engr;
+ }
+ return (NULL);
+}
+
+bool
+sengr_at(const char *s, xchar x, xchar y)
+{
+ struct engr *ep = engr_at(x, y);
+ char *t;
+ int n;
+
+ if (ep && ep->engr_time <= moves) {
+ t = ep->engr_txt;
+ n = strlen(s);
+ while (*t) {
+ if (!strncmp(s, t, n))
+ return (1);
+ t++;
+ }
+ }
+ return (0);
+}
+
+void
+u_wipe_engr(int cnt)
+{
+ if (!u.uswallow && !Levitation)
+ wipe_engr_at(u.ux, u.uy, cnt);
+}
+
+void
+wipe_engr_at(xchar x, xchar y, xchar cnt)
+{
+ struct engr *ep = engr_at(x, y);
+ 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);
+ }
+}
+
+void
+read_engr_at(int x, int y)
+{
+ 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);
+ }
+}
+
+void
+make_engr_at(int x, int y, const char *s)
+{
+ struct engr *ep;
+
+ if ((ep = engr_at(x, y)))
+ del_engr(ep);
+ ep = 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);
+ strcpy(ep->engr_txt, s);
+ ep->engr_time = 0;
+ ep->engr_type = DUST;
+ ep->engr_lth = strlen(s) + 1;
+}
+
+int
+doengrave(void)
+{
+ int len;
+ char *sp;
+ struct engr *ep, *oep = engr_at(u.ux, u.uy);
+ char buf[BUFSZ];
+ xchar type;
+ int spct; /* number of leading spaces */
+ 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 = NULL;
+ 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, NULL));
+ 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 = NULL;
+ }
+ 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 = 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) {
+ strcpy(sp, oep->engr_txt);
+ strcat(sp, buf);
+ del_engr(oep);
+ } else
+ 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);
+}
+
+void
+save_engravings(int fd)
+{
+ 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 = NULL;
+}
+
+void
+rest_engravings(int fd)
+{
+ struct engr *ep;
+ unsigned lth;
+
+ head_engr = NULL;
+ for (;;) {
+ mread(fd, (char *)&lth, sizeof(unsigned));
+ if (lth == 0)
+ return;
+ ep = 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;
+ }
+}
+
+static void
+del_engr(struct engr *ep)
+{
+ 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(ep);
+}
diff --git a/hack/hack.fight.c b/hack/hack.fight.c
new file mode 100644
index 0000000..66fb57f
--- /dev/null
+++ b/hack/hack.fight.c
@@ -0,0 +1,404 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.fight.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.fight.c,v 1.5 1999/11/16 10:26:36 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.fight.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+extern struct permonst li_dog, dog, la_dog;
+
+static boolean far_noise;
+static long noisetime;
+
+static void monstone(struct monst *);
+
+/* hitmm returns 0 (miss), 1 (hit), or 2 (kill) */
+int
+hitmm(struct monst *magr, struct monst *mdef)
+{
+ struct permonst *pa = magr->data, *pd = mdef->data;
+ int ht;
+ schar tmp;
+ boolean vis;
+
+ if (strchr("Eauy", pa->mlet))
+ return (0);
+ if (magr->mfroz) /* riv05!a3 */
+ return (0);
+ tmp = pd->ac + pa->mlevel;
+ if (mdef->mconf || mdef->mfroz || mdef->msleep) {
+ tmp += 4;
+ if (mdef->msleep)
+ mdef->msleep = 0;
+ }
+ ht = (tmp > rnd(20));
+ if (ht)
+ 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);
+ sprintf(buf, "%s %s", Monnam(magr),
+ ht ? "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 (ht) {
+ 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);
+ ht = 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);
+ ht = 2;
+ }
+ }
+ return (ht);
+}
+
+/* drop (perhaps) a cadaver and remove monster */
+void
+mondied(struct monst *mdef)
+{
+ struct permonst *pd = mdef->data;
+
+ if (letter(pd->mlet) && rn2(3)) {
+ 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 */
+static void
+monstone(struct monst *mdef)
+{
+ if (strchr(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);
+}
+
+int
+fightm(struct monst *mtmp)
+{
+ 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 */
+bool
+thitu(int tlev, int dam, const 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',&";
+
+/* return TRUE if mon still alive */
+bool
+hmon(struct monst *mon, struct obj *obj, int thrown)
+{
+ int tmp;
+ bool 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 (strchr(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(NULL, obj->owornmask);
+ obfree(obj, NULL);
+ 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(NULL, obj->owornmask);
+ obfree(obj, NULL);
+ 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 (strchr(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 */
+bool
+attack(struct monst *mtmp)
+{
+ schar tmp;
+ boolean malive = TRUE;
+ 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) {
+ 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 &&
+ strchr("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/hack/hack.fix b/hack/hack.fix
new file mode 100644
index 0000000..9e41c32
--- /dev/null
+++ b/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 occurred in the onbill-routine. After investigating the core
+dump I found that the shopkeeper's bill was still to be paid. Normally
+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/hack/hack.h b/hack/hack.h
new file mode 100644
index 0000000..f875387
--- /dev/null
+++ b/hack/hack.h
@@ -0,0 +1,707 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.h - version 1.0.3 */
+/* $DragonFly: src/games/hack/hack.h,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "config.h"
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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"
+#include "def.mkroom.h"
+#include "def.wseg.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 xchar xdnstair, ydnstair, xupstair, yupstair; /* stairs up and down. */
+
+#define newstring(x) 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 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;
+ void (*p_tofn)(void); /* 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;
+ const 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 const char *traps[];
+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 const char *save_cm, *killer, *nomovemsg;
+
+extern xchar dlevel, maxdlevel; /* dungeon level */
+
+extern long moves;
+
+extern int multi;
+
+extern char lock[];
+
+extern const char *occtxt;
+extern const char *hu_stat[];
+
+#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 */
+
+extern schar xdir[], ydir[];
+extern int hackpid, locknum, doorindex, done_stopprint;
+extern char mlarge[], pl_character[PL_CSIZ], genocided[60], fut_geno[60];
+extern char *hname, morc, plname[PL_NSIZ], sdir[];
+extern boolean level_exists[], restoring, in_mklev;
+extern struct permonst pm_eel, pm_ghost;
+extern void (*afternmv)(void);
+extern struct monst *mydogs;
+extern bool (*occupation)(void);
+
+/* Non-static function prototypes */
+
+/* alloc.c */
+void *alloc(size_t);
+
+/* hack.apply.c */
+int doapply(void);
+int holetime(void);
+void dighole(void);
+
+/* hack.bones.c */
+void savebones(void);
+int getbones(void);
+
+/* hack.c */
+void unsee(void);
+void seeoff(bool);
+void domove(void);
+int dopickup(void);
+void pickup(int);
+void lookaround(void);
+bool monster_nearby(void);
+bool cansee(xchar, xchar);
+int sgn(int);
+void setsee(void);
+void nomul(int);
+int abon(void);
+int dbon(void);
+void losestr(int);
+void losehp(int, const char *);
+void losehp_m(int, struct monst *);
+void losexp(void);
+int inv_weight(void);
+long newuexp(void);
+
+/* hack.cmd.c */
+void rhack(const char *);
+bool movecmd(char);
+bool getdir(bool);
+void confdir(void);
+#ifdef QUEST
+void finddir(void);
+#endif
+bool isok(int, int);
+
+/* hack.do.c */
+int dodrop(void);
+void dropx(struct obj *);
+int doddrop(void);
+int dodown(void);
+int doup(void);
+void goto_level(int, bool);
+int donull(void);
+int dopray(void);
+int dothrow(void);
+struct obj *splitobj(struct obj *, int);
+void more_experienced(int, int);
+void set_wounded_legs(long, int);
+void heal_legs(void);
+
+/* hack.do_name.c */
+coord getpos(int, const char *);
+int do_mname(void);
+int ddocall(void);
+void docall(struct obj *);
+char *monnam(struct monst *);
+char *Monnam(struct monst *);
+char *amonnam(struct monst *, const char *);
+char *Amonnam(struct monst *, const char *);
+char *Xmonnam(struct monst *);
+
+/* hack.do_wear.c */
+int doremarm(void);
+int doremring(void);
+bool armoroff(struct obj *);
+int doweararm(void);
+int dowearring(void);
+void ringoff(struct obj *);
+void find_ac(void);
+void glibr(void);
+struct obj *some_armor(void);
+void corrode_armor(void);
+
+/* hack.dog.c */
+void makedog(void);
+void losedogs(void);
+void keepdogs(void);
+void fall_down(struct monst *);
+int dog_move(struct monst *, int);
+int inroom(xchar, xchar);
+bool tamedog(struct monst *, struct obj *);
+
+/* hack.eat.c */
+void init_uhunger(void);
+int doeat(void);
+void gethungry(void);
+void morehungry(int);
+void lesshungry(int);
+bool poisonous(struct obj *);
+
+/* hack.end.c */
+void done1(int);
+void done_in_by(struct monst *);
+void done(const char *);
+void clearlocks(void);
+#ifdef NOSAVEONHANGUP
+void hangup(int);
+#endif
+char *eos(char *);
+void charcat(char *, char);
+void prscore(int, char **);
+
+/* hack.engrave.c */
+bool sengr_at(const char *, xchar, xchar);
+void u_wipe_engr(int);
+void wipe_engr_at(xchar, xchar, xchar);
+void read_engr_at(int, int);
+void make_engr_at(int, int, const char *);
+int doengrave(void);
+void save_engravings(int);
+void rest_engravings(int);
+
+/* hack.fight.c */
+int hitmm(struct monst *, struct monst *);
+void mondied(struct monst *);
+int fightm(struct monst *);
+bool thitu(int, int, const char *);
+bool hmon(struct monst *, struct obj *, int);
+bool attack(struct monst *);
+
+/* hack.invent.c */
+struct obj *addinv(struct obj *);
+void useup(struct obj *);
+void freeinv(struct obj *);
+void delobj(struct obj *);
+void freeobj(struct obj *);
+void freegold(struct gold *);
+void deltrap(struct trap *);
+struct monst *m_at(int, int);
+struct obj *o_at(int, int);
+struct obj *sobj_at(int, int, int);
+bool carried(struct obj *);
+bool carrying(int);
+struct obj *o_on(unsigned int, struct obj *);
+struct trap *t_at(int, int);
+struct gold *g_at(int, int);
+struct obj *getobj(const char *, const char *);
+int ggetobj(const char *, int (*)(struct obj *), int);
+int askchain(struct obj *, char *, int, int (*)(struct obj *),
+ bool (*)(struct obj *), int);
+void prinv(struct obj *);
+int ddoinv(void);
+int dotypeinv(void);
+int dolook(void);
+void stackobj(struct obj *);
+int doprgold(void);
+int doprwep(void);
+int doprarm(void);
+int doprring(void);
+bool digit(char);
+
+/* hack.ioctl.c */
+void getioctls(void);
+void setioctls(void);
+#ifdef SUSPEND
+int dosuspend(void);
+#endif
+
+/* hack.lev.c */
+void savelev(int, xchar);
+void bwrite(int, char *, unsigned int);
+void saveobjchn(int, struct obj *);
+void savemonchn(int, struct monst *);
+void getlev(int, int, xchar);
+void mread(int, char *, unsigned int);
+void mklev(void);
+
+/* hack.main.c */
+void glo(int);
+void askname(void);
+void impossible(const char *, ...) __printflike(1, 2);
+void stop_occupation(void);
+
+/* hack.makemon.c */
+struct monst *makemon(struct permonst *, int, int);
+coord enexto(xchar, xchar);
+bool goodpos(int, int);
+void rloc(struct monst *);
+struct monst *mkmon_at(char, int, int);
+
+/* hack.mhitu.c */
+bool mhitu(struct monst *);
+bool hitu(struct monst *, int);
+
+/* hack.mklev.c */
+void makelevel(void);
+void mktrap(int, int, struct mkroom *);
+
+/* hack.mkmaze.c */
+void makemaz(void);
+coord mazexy(void);
+
+/* hack.mkobj.c */
+struct obj *mkobj_at(int, int, int);
+void mksobj_at(int, int, int);
+struct obj *mkobj(int);
+struct obj *mksobj(int);
+bool letter(char);
+int weight(struct obj *);
+void mkgold(long, int, int);
+
+/* hack.mkshop.c */
+#ifndef QUEST
+void mkshop(void);
+void mkzoo(int);
+void mkswamp(void);
+#endif
+
+/* hack.mon.c */
+void movemon(void);
+void justswld(struct monst *, const char *);
+void youswld(struct monst *, int, int, const char *);
+bool dochug(struct monst *);
+int m_move(struct monst *, int);
+int mfndpos(struct monst *, coord *, int *, int);
+int dist(int, int);
+void poisoned(const char *, const char *);
+void mondead(struct monst *);
+void replmon(struct monst *, struct monst *);
+void relmon(struct monst *);
+void monfree(struct monst *);
+void unstuck(struct monst *);
+void killed(struct monst *);
+void kludge(const char *, const char *);
+void rescham(void);
+bool newcham(struct monst *, struct permonst *);
+void mnexto(struct monst *);
+void setmangry(struct monst *);
+bool canseemon(struct monst *);
+
+/* hack.o_init.c */
+int letindex(char);
+void init_objects(void);
+int probtype(char);
+void oinit(void);
+void savenames(int);
+void restnames(int);
+int dodiscovered(void);
+
+/* hack.objnam.c */
+char *typename(int);
+char *xname(struct obj *);
+char *doname(struct obj *);
+void setan(const char *, char *);
+char *aobjnam(struct obj *, const char *);
+char *Doname(struct obj *);
+struct obj *readobjnam(char *);
+
+/* hack.options.c */
+void initoptions(void);
+int doset(void);
+
+/* hack.pager.c */
+int dowhatis(void);
+void set_whole_screen(void);
+#ifdef NEWS
+bool readnews(void);
+#endif
+void set_pager(int);
+bool page_line(const char *);
+void cornline(int, const char *);
+int dohelp(void);
+bool page_file(const char *, bool);
+#ifdef UNIX
+#ifdef SHELL
+int dosh(void);
+#endif /* SHELL */
+bool child(bool);
+#endif /* UNIX */
+
+/* hack.potion.c */
+int dodrink(void);
+void pluslvl(void);
+void strange_feeling(struct obj *, const char *);
+void potionhit(struct monst *, struct obj *);
+void potionbreathe(struct obj *);
+int dodip(void);
+
+/* hack.pri.c */
+void swallowed(void);
+void panic(const char *, ...) __printflike(1, 2);
+void atl(int, int, char);
+void on_scr(int, int);
+void tmp_at(schar, schar);
+void Tmp_at(schar, schar);
+void setclipped(void);
+void at(xchar, xchar, char);
+void prme(void);
+int doredraw(void);
+void docrt(void);
+void docorner(int, int);
+void curs_on_u(void);
+void pru(void);
+void prl(int, int);
+char news0(xchar, xchar);
+void newsym(int, int);
+void mnewsym(int, int);
+void nosee(int, int);
+#ifndef QUEST
+void prl1(int, int);
+void nose1(int, int);
+#endif
+bool vism_at(int, int);
+void unpobj(struct obj *);
+void seeobjs(void);
+void seemons(void);
+void pmon(struct monst *);
+void unpmon(struct monst *);
+void nscr(void);
+void bot(void);
+#ifdef WAN_PROBING
+void mstatusline(struct monst *);
+#endif
+void cls(void);
+
+/* hack.read.c */
+int doread(void);
+int identify(struct obj *);
+void litroom(bool);
+
+/* hack.rip.c */
+void outrip(void);
+
+/* hack.rumors.c */
+void outrumor(void);
+
+/* hack.save.c */
+int dosave(void);
+#ifndef NOSAVEONHANGUP
+void hangup(int);
+#endif
+bool dorecover(int);
+struct obj *restobjchn(int);
+struct monst *restmonchn(int);
+
+/* hack.search.c */
+int findit(void);
+int dosearch(void);
+int doidtrap(void);
+void wakeup(struct monst *);
+void seemimic(struct monst *);
+
+/* hack.shk.c */
+#ifdef QUEST
+void obfree(struct obj *, struct obj *);
+int inshop(void);
+void shopdig(void);
+void addtobill(void);
+void subfrombill(void);
+void splitbill(void);
+int dopay(void);
+void paybill(void);
+int doinvbill(void);
+void shkdead(void);
+int shkcatch(void);
+int shk_move(void);
+void replshk(struct monst *, struct monst *);
+const char *shkname(void);
+#else
+char *shkname(struct monst *);
+void shkdead(struct monst *);
+void replshk(struct monst *, struct monst *);
+int inshop(void);
+void obfree(struct obj *, struct obj *);
+int dopay(void);
+void paybill(void);
+void addtobill(struct obj *);
+void splitbill(struct obj *, struct obj *);
+void subfrombill(struct obj *);
+int doinvbill(int);
+bool shkcatch(struct obj *);
+int shk_move(struct monst *);
+void shopdig(int);
+#endif
+bool online(int, int);
+bool follower(struct monst *);
+
+/* hack.shknam.c */
+void findname(char *, char);
+
+/* hack.steal.c */
+long somegold(void);
+void stealgold(struct monst *);
+bool steal(struct monst *);
+void mpickobj(struct monst *, struct obj *);
+bool stealamulet(struct monst *);
+void relobj(struct monst *, int);
+
+/* hack.termcap.c */
+void startup(void);
+void start_screen(void);
+void end_screen(void);
+void curs(int, int);
+void cl_end(void);
+void clear_screen(void);
+void home(void);
+void standoutbeg(void);
+void standoutend(void);
+void backsp(void);
+void bell(void);
+void cl_eos(void);
+
+/* hack.timeout.c */
+void p_timeout(void);
+
+/* hack.topl.c */
+int doredotopl(void);
+void remember_topl(void);
+void addtopl(const char *);
+void more(void);
+void cmore(const char *);
+void clrlin(void);
+void pline(const char *, ...) __printflike(1, 2);
+void vpline(const char *, va_list) __printflike(1, 0);
+void putsym(char);
+void putstr(const char *);
+
+/* hack.track.c */
+void initrack(void);
+void settrack(void);
+coord *gettrack(int, int);
+
+/* hack.trap.c */
+struct trap *maketrap(int, int, int);
+void dotrap(struct trap *);
+int mintrap(struct monst *);
+void selftouch(const char *);
+void float_up(void);
+void float_down(void);
+void tele(void);
+int dotele(void);
+void placebc(int);
+void unplacebc(void);
+void level_tele(void);
+void drown(void);
+
+/* hack.tty.c */
+void gettty(void);
+void settty(const char *);
+void setftty(void);
+void error(const char *, ...) __printflike(1, 2);
+void getlin(char *);
+void getret(void);
+void cgetret(const char *);
+void xwaitforspace(const char *);
+char *parse(void);
+char readchar(void);
+void end_of_input(void);
+
+/* hack.u_init.c */
+void u_init(void);
+void plnamesuffix(void);
+
+/* hack.unix.c */
+void setrandom(void);
+int getyear(void);
+char *getdate(void);
+int phase_of_the_moon(void);
+bool night(void);
+bool midnight(void);
+void gethdate(const char *);
+bool uptodate(int);
+void getlock(void);
+#ifdef MAIL
+void getmailstatus(void);
+void ckmailstatus(void);
+void readmail(void);
+#endif
+void regularize(char *);
+
+/* hack.vault.c */
+void setgd(void);
+int gd_move(void);
+void replgd(struct monst *, struct monst *);
+void invault(void);
+#ifdef QUEST
+void gddead(struct monst *);
+#else
+void gddead(void);
+#endif
+
+/* hack.version.c */
+int doversion(void);
+
+/* hack.wield.c */
+void setuwep(struct obj *);
+int dowield(void);
+void corrode_weapon(void);
+bool chwepon(struct obj *, int);
+
+/* hack.wizard.c */
+void amulet(void);
+bool wiz_hit(struct monst *);
+void inrange(struct monst *);
+
+/* hack.worm.c */
+#ifndef NOWORM
+bool getwn(struct monst *);
+void initworm(struct monst *);
+void worm_move(struct monst *);
+void worm_nomove(struct monst *);
+void wormdead(struct monst *);
+void wormhit(struct monst *);
+void wormsee(unsigned int);
+void pwseg(struct wseg *);
+void cutworm(struct monst *, xchar, xchar, uchar);
+#endif
+
+/* hack.worn.c */
+void setworn(struct obj *, long);
+void setnotworn(struct obj *);
+
+/* hack.zap.c */
+int dozap(void);
+const char *exclam(int);
+void hit(const char *, struct monst *, const char *);
+void miss(const char *, struct monst *);
+struct monst *bhit(int, int, int, char,
+ void (*)(struct monst *, struct obj *),
+ bool (*)(struct obj *, struct obj *), struct obj *);
+struct monst *boomhit(int, int);
+void buzz(int, xchar, xchar, int, int);
+void fracture_rock(struct obj *);
+
+/* rnd.c */
+int rn1(int, int);
+int rn2(int);
+int rnd(int);
+int d(int, int);
diff --git a/hack/hack.invent.c b/hack/hack.invent.c
new file mode 100644
index 0000000..bae4b75
--- /dev/null
+++ b/hack/hack.invent.c
@@ -0,0 +1,964 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.invent.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.invent.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+
+#include "hack.h"
+extern struct obj zeroobj;
+extern char quitchars[];
+
+static void assigninvlet(struct obj *);
+static struct obj *mkgoldobj(long);
+static bool ckunpaid(struct obj *);
+static char obj_to_let(struct obj *);
+static char *xprname(struct obj *, char);
+static void doinv(char *);
+static bool merged(struct obj *, struct obj *, bool);
+static bool countgold(void);
+
+#ifndef NOWORM
+extern struct wseg *wsegs[32];
+#endif /* NOWORM */
+
+#define NOINVSYM '#'
+
+static int lastinvnr = 51; /* 0 ... 51 */
+
+static void
+assigninvlet(struct obj *otmp)
+{
+ boolean inuse[52];
+ int i;
+ 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(struct obj *obj)
+{
+ struct obj *otmp;
+
+ /* merge or attach to end of chain */
+ if (!invent) {
+ invent = obj;
+ otmp = NULL;
+ } 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);
+}
+
+void
+useup(struct obj *obj)
+{
+ if (obj->quan > 1) {
+ obj->quan--;
+ obj->owt = weight(obj);
+ } else {
+ setnotworn(obj);
+ freeinv(obj);
+ obfree(obj, NULL);
+ }
+}
+
+void
+freeinv(struct obj *obj)
+{
+ 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) */
+void
+delobj(struct obj *obj)
+{
+ freeobj(obj);
+ unpobj(obj);
+ obfree(obj, NULL);
+}
+
+/* unlink obj from chain starting with fobj */
+void
+freeobj(struct obj *obj)
+{
+ 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! */
+void
+freegold(struct gold *gold)
+{
+ 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(gold);
+}
+
+void
+deltrap(struct trap *trap)
+{
+ struct trap *ttmp;
+
+ if (trap == ftrap)
+ ftrap = ftrap->ntrap;
+ else {
+ for (ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap)
+ ; /* nothing */
+ ttmp->ntrap = trap->ntrap;
+ }
+ free(trap);
+}
+
+struct wseg *m_atseg;
+
+struct monst *
+m_at(int x, int y)
+{
+ struct monst *mtmp;
+#ifndef NOWORM
+ struct wseg *wtmp;
+#endif /* NOWORM */
+
+ m_atseg = NULL;
+ 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(int x, int y)
+{
+ 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(int n, int x, int y)
+{
+ struct obj *otmp;
+
+ for (otmp = fobj; otmp; otmp = otmp->nobj)
+ if (otmp->ox == x && otmp->oy == y && otmp->otyp == n)
+ return (otmp);
+ return (0);
+}
+
+bool
+carried(struct obj *obj)
+{
+ struct obj *otmp;
+
+ for (otmp = invent; otmp; otmp = otmp->nobj)
+ if (otmp == obj)
+ return (1);
+ return (0);
+}
+
+bool
+carrying(int type)
+{
+ struct obj *otmp;
+
+ for (otmp = invent; otmp; otmp = otmp->nobj)
+ if (otmp->otyp == type)
+ return (TRUE);
+ return (FALSE);
+}
+
+struct obj *
+o_on(unsigned int id, struct obj *objchn)
+{
+ while (objchn) {
+ if (objchn->o_id == id)
+ return (objchn);
+ objchn = objchn->nobj;
+ }
+ return (NULL);
+}
+
+struct trap *
+t_at(int x, int y)
+{
+ struct trap *trap = ftrap;
+
+ while (trap) {
+ if (trap->tx == x && trap->ty == y)
+ return (trap);
+ trap = trap->ntrap;
+ }
+ return (0);
+}
+
+struct gold *
+g_at(int x, int y)
+{
+ 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 */
+static struct obj *
+mkgoldobj(long q)
+{
+ 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.
+ * NULL error return: no object.
+ * &zeroobj explicitly no object (as in w-).
+ */
+struct obj *
+getobj(const char *let, const char *word)
+{
+ struct obj *otmp;
+ char ilet, ilet1, ilet2;
+ char buf[BUFSZ];
+ char lets[BUFSZ];
+ int foo = 0, foo2;
+ 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 || strchr(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;
+ 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 (strchr(quitchars, ilet))
+ return (NULL);
+ if (ilet == '-')
+ return (allownone ? &zeroobj : NULL);
+ 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(NULL);
+ 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)
+ ; /* nothing */
+ }
+ 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 && !strchr(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) {
+ struct obj *obj;
+ obj = splitobj(otmp, (int)cnt);
+ if (otmp == uwep)
+ setuwep(obj);
+ }
+ }
+ return (otmp);
+}
+
+static bool
+ckunpaid(struct obj *otmp)
+{
+ return (otmp->unpaid);
+}
+
+/* interactive version of getobj - used for Drop and Identify */
+/* return the number of times fn was called successfully */
+int
+ggetobj(const char *word, int (*fn)(struct obj *), int max)
+{
+ char buf[BUFSZ];
+ char *ip;
+ char sym;
+ int oletct = 0, iletct = 0;
+ boolean allflag = FALSE;
+ char olets[20], ilets[20];
+ bool (*ckfn)(struct obj *) = (bool (*)()) 0;
+ xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */
+
+ if (!invent && !allowgold) {
+ pline("You have nothing to %s.", word);
+ return (0);
+ } else {
+ struct obj *otmp = invent;
+ int uflg = 0;
+
+ if (allowgold)
+ ilets[iletct++] = '$';
+ ilets[iletct] = 0;
+ while (otmp) {
+ if (!strchr(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 (strchr("!%?[()=*/\"0", sym)) {
+ if (!strchr(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.
+ */
+int
+askchain(struct obj *objchn, char *olets, int allflag,
+ int (*fn)(struct obj *), bool (*ckfn)(struct obj *), int max)
+{
+ struct obj *otmp, *otmp2;
+ char sym, ilet;
+ 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 && !strchr(olets, otmp->olet))
+ continue;
+ if (ckfn && !(*ckfn)(otmp))
+ continue;
+ if (!allflag) {
+ pline("%s", 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);
+}
+
+/* should of course only be called for things in invent */
+static char
+obj_to_let(struct obj *obj)
+{
+ struct obj *otmp;
+ 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);
+}
+
+void
+prinv(struct obj *obj)
+{
+ pline("%s", xprname(obj, obj_to_let(obj)));
+}
+
+static char *
+xprname(struct obj *obj, char let)
+{
+ static char li[BUFSZ];
+
+ sprintf(li, "%c - %s.",
+ flags.invlet_constant ? obj->invlet : let,
+ doname(obj));
+ return (li);
+}
+
+int
+ddoinv(void)
+{
+ doinv(NULL);
+ return (0);
+}
+
+/* called with 0 or "": all objects in inventory */
+/* otherwise: all objects with (serial) letter in lets */
+static void
+doinv(char *lets)
+{
+ struct obj *otmp;
+ char ilet;
+ int ct = 0;
+ char any[BUFSZ];
+
+ morc = 0; /* just to be sure */
+ if (!invent) {
+ pline("Not carrying anything.");
+ return;
+ }
+
+ cornline(0, NULL);
+ ilet = 'a';
+ for (otmp = invent; otmp; otmp = otmp->nobj) {
+ if (flags.invlet_constant)
+ ilet = otmp->invlet;
+ if (!lets || !*lets || strchr(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);
+}
+
+int
+dotypeinv(void) /* free after Robert Viduya */
+{
+ /* Changed to one type only, so he doesnt have to type cr */
+ char c, ilet;
+ char stuff[BUFSZ];
+ int stct;
+ 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 (!strchr(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 (strchr(quitchars, c))
+ return (0);
+ } else
+ c = stuff[0];
+
+ if (c == '$')
+ return (doprgold());
+
+ if (c == 'x' || c == 'X') {
+ if (billx)
+ 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 */
+int
+dolook(void)
+{
+ struct obj *otmp, *otmp0 = NULL;
+ struct gold *gold = NULL;
+ const 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];
+
+ 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, NULL);
+ }
+ if (ct > 1)
+ cornline(2, NULL);
+ return (!!Blind);
+}
+
+void
+stackobj(struct obj *obj)
+{
+ 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 */
+static bool
+merged(struct obj *otmp, struct obj *obj, bool lose)
+{
+ if (obj->otyp == otmp->otyp &&
+ obj->unpaid == otmp->unpaid &&
+ obj->spe == otmp->spe &&
+ obj->dknown == otmp->dknown &&
+ obj->cursed == otmp->cursed &&
+ (strchr("%*?!", 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.]
+ */
+static long goldcounted;
+
+static bool
+countgold(void)
+{
+ 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 */
+}
+
+int
+doprgold(void)
+{
+ 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 --- */
+
+int
+doprwep(void)
+{
+ if (!uwep)
+ pline("You are empty handed.");
+ else
+ prinv(uwep);
+ return (0);
+}
+
+int
+doprarm(void)
+{
+ if (!uarm && !uarmg && !uarms && !uarmh)
+ pline("You are not wearing any armor.");
+ else {
+ char lets[6];
+ 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);
+}
+
+int
+doprring(void)
+{
+ if (!uleft && !uright)
+ pline("You are not wearing any rings.");
+ else {
+ char lets[3];
+ 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);
+}
+
+bool
+digit(char c)
+{
+ return (c >= '0' && c <= '9');
+}
diff --git a/hack/hack.ioctl.c b/hack/hack.ioctl.c
new file mode 100644
index 0000000..980755a
--- /dev/null
+++ b/hack/hack.ioctl.c
@@ -0,0 +1,47 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.ioctl.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.ioctl.c,v 1.2 1999/09/12 07:01:23 marcel Exp $
+ * $DragonFly: src/games/hack/hack.ioctl.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $
+ *
+ * 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 "hack.h"
+#include <termios.h>
+struct termios termio;
+
+void
+getioctls(void)
+{
+ tcgetattr(fileno(stdin), &termio);
+}
+
+void
+setioctls(void)
+{
+ tcsetattr(fileno(stdin), TCSANOW, &termio);
+}
+
+#ifdef SUSPEND
+#include <signal.h>
+int
+dosuspend(void)
+{
+#ifdef SIGTSTP
+ if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
+ settty(NULL);
+ signal(SIGTSTP, SIG_DFL);
+ 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/hack/hack.lev.c b/hack/hack.lev.c
new file mode 100644
index 0000000..d47b873
--- /dev/null
+++ b/hack/hack.lev.c
@@ -0,0 +1,276 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.lev.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.lev.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+
+#include "hack.h"
+extern struct obj *billobjs;
+extern char SAVEF[];
+extern char nul[];
+
+#ifndef NOWORM
+extern struct wseg *wsegs[32], *wheads[32];
+extern long wgrowtime[32];
+#endif /* NOWORM */
+
+boolean level_exists[MAXLEVEL+1];
+
+static void savegoldchn(int, struct gold *);
+static void savetrapchn(int, struct trap *);
+
+void
+savelev(int fd, xchar lev)
+{
+#ifndef NOWORM
+ struct wseg *wtmp, *wtmp2;
+ int 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 = NULL;
+ 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] = NULL;
+ }
+ bwrite(fd, (char *)wgrowtime, sizeof(wgrowtime));
+#endif /* NOWORM */
+}
+
+void
+bwrite(int fd, char *loc, unsigned int num)
+{
+ /* lint wants the 3rd arg of write to be an int; lint -p an unsigned */
+ if (write(fd, loc, (int)num) != (int)num)
+ panic("cannot write %u bytes to file #%d", num, fd);
+}
+
+void
+saveobjchn(int fd, struct obj *otmp)
+{
+ 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(otmp);
+ otmp = otmp2;
+ }
+ bwrite(fd, (char *)&minusone, sizeof(int));
+}
+
+void
+savemonchn(int fd, struct monst *mtmp)
+{
+ 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(mtmp);
+ mtmp = mtmp2;
+ }
+ bwrite(fd, (char *)&minusone, sizeof(int));
+}
+
+static void
+savegoldchn(int fd, struct gold *gold)
+{
+ struct gold *gold2;
+
+ while (gold) {
+ gold2 = gold->ngold;
+ bwrite(fd, (char *)gold, sizeof(struct gold));
+ free(gold);
+ gold = gold2;
+ }
+ bwrite(fd, nul, sizeof(struct gold));
+}
+
+static void
+savetrapchn(int fd, struct trap *trap)
+{
+ struct trap *trap2;
+
+ while (trap) {
+ trap2 = trap->ntrap;
+ bwrite(fd, (char *)trap, sizeof(struct trap));
+ free(trap);
+ trap = trap2;
+ }
+ bwrite(fd, nul, sizeof(struct trap));
+}
+
+void
+getlev(int fd, int pid, xchar lev)
+{
+ struct gold *gold;
+ struct trap *trap;
+#ifndef NOWORM
+ struct wseg *wtmp;
+#endif /* NOWORM */
+ int 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;
+ struct monst *mtmp, *mtmp2;
+
+ for (mtmp = fmon; mtmp; mtmp = mtmp2) {
+ long newhp; /* tmoves may be very large */
+
+ mtmp2 = mtmp->nmon;
+ if (strchr(genocided, mtmp->data->mlet)) {
+ mondead(mtmp);
+ continue;
+ }
+
+ if (mtmp->mtame && tmoves > 250) {
+ mtmp->mtame = 0;
+ mtmp->mpeaceful = 0;
+ }
+
+ newhp = mtmp->mhp +
+ (strchr(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(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(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();
+ for (;;) {
+ 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 */
+}
+
+void
+mread(int fd, char *buf, unsigned int len)
+{
+ int rlen;
+
+ rlen = read(fd, buf, (int)len);
+ if (rlen != (int)len) {
+ pline("Read %d instead of %u bytes.\n", rlen, len);
+ if (restoring) {
+ unlink(SAVEF);
+ error("Error restoring old game.");
+ }
+ panic("Error reading level file.");
+ }
+}
+
+void
+mklev(void)
+{
+ if (getbones())
+ return;
+
+ in_mklev = TRUE;
+ makelevel();
+ in_mklev = FALSE;
+}
diff --git a/hack/hack.main.c b/hack/hack.main.c
new file mode 100644
index 0000000..d17bdd9
--- /dev/null
+++ b/hack/hack.main.c
@@ -0,0 +1,507 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.main.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.main.c,v 1.9 1999/11/16 10:26:36 marcel Exp $ */
+
+#include <sys/stat.h>
+#include "hack.h"
+
+#ifdef QUEST
+#define gamename "quest"
+#else
+#define gamename "hack"
+#endif
+
+void (*afternmv)(void);
+bool (*occupation)(void);
+const char *occtxt;
+
+
+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 long wailmsg;
+
+#ifdef CHDIR
+static void chdirx(const char *, bool);
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+#ifdef CHDIR
+ 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.
+ */
+ {
+ char *s;
+
+ initoptions();
+ if (!*plname && (s = getenv("USER")))
+ strncpy(plname, s, sizeof(plname) - 1);
+ if (!*plname && (s = getenv("LOGNAME")))
+ strncpy(plname, s, sizeof(plname) - 1);
+ if (!*plname && (s = getlogin()))
+ 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);
+ umask(007);
+ setrandom();
+ startup();
+ cls();
+ u.uhp = 1; /* prevent RIP on early quits */
+ u.ux = FAR; /* prevent nscr() */
+ 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':
+ wizard = TRUE;
+ break;
+#endif
+#ifdef NEWS
+ case 'n':
+ flags.nonews = TRUE;
+ break;
+#endif
+ case 'u':
+ if (argv[0][2])
+ strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
+ else if (argc > 1) {
+ argc--;
+ argv++;
+ strncpy(plname, argv[0], sizeof(plname) - 1);
+ } else
+ printf("Player name expected after -u\n");
+ break;
+ default:
+ /* allow -T for Tourist, etc. */
+ strncpy(pl_character, argv[0] + 1,
+ sizeof(pl_character) - 1);
+ }
+ }
+
+ 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)
+ 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)
+ */
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ if (!locknum)
+ strcpy(lock, plname);
+ getlock(); /* sets lock if locknum != 0 */
+#ifdef WIZARD
+ } else {
+ char *sfoo;
+ strcpy(lock, plname);
+ if ((sfoo = getenv("MAGIC")))
+ while (*sfoo) {
+ switch (*sfoo++) {
+ case 'n':
+ srandom(*sfoo++);
+ break;
+ }
+ }
+ if ((sfoo = getenv("GENOCIDED")) != NULL) {
+ if (*sfoo == '!') {
+ struct permonst *pm = mons;
+ char *gp = genocided;
+
+ while (pm < mons + CMNUM + 2) {
+ if (!strchr(sfoo, pm->mlet))
+ *gp++ = pm->mlet;
+ pm++;
+ }
+ *gp = 0;
+ } else
+ strncpy(genocided, sfoo, sizeof(genocided) - 1);
+ strcpy(fut_geno, genocided);
+ }
+ }
+#endif
+ setftty();
+ sprintf(SAVEF, "save/%d%s", getuid(), plname);
+ regularize(SAVEF + 5); /* avoid . or / in name */
+ if ((fd = open(SAVEF, O_RDONLY)) >= 0 &&
+ (uptodate(fd) || unlink(SAVEF) == 666)) {
+ signal(SIGINT, done1);
+ pline("Restoring old save file...");
+ 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();
+
+ signal(SIGINT, done1);
+ mklev();
+ u.ux = xupstair;
+ u.uy = yupstair;
+ inshop();
+ setsee();
+ flags.botlx = 1;
+ makedog();
+ {
+ struct monst *mtmp;
+ if ((mtmp = m_at(u.ux, u.uy)) != NULL)
+ 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)))) {
+ movemon();
+ if (!rn2(70))
+ makemon(NULL, 0, 0);
+ }
+ if (Glib)
+ glibr();
+ p_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)
+ dosearch();
+ gethungry();
+ invault();
+ amulet();
+ }
+ if (multi < 0) {
+ if (!++multi) {
+ pline("%s", nomovemsg ? nomovemsg :
+ "You can move again.");
+ nomovemsg = 0;
+ if (afternmv)
+ (*afternmv)();
+ afternmv = NULL;
+ }
+ }
+ 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 = NULL;
+ 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(NULL);
+ }
+ if (multi && multi % 7 == 0)
+ fflush(stdout);
+ }
+}
+
+void
+glo(int foo)
+{
+ /* construct the string xlock.n */
+ 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.
+ */
+void
+askname(void)
+{
+ int c, ct;
+
+ printf("\nWho are you? ");
+ 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 < (int)sizeof(plname) - 1)
+ plname[ct++] = c;
+ }
+ plname[ct] = 0;
+ if (ct == 0)
+ askname();
+}
+
+/* VARARGS1 */
+void
+impossible(const char *s, ...)
+{
+ va_list ap;
+
+ va_start(ap, s);
+ vpline(s, ap);
+ va_end(ap);
+ pline("Program in disorder - perhaps you'd better Quit.");
+}
+
+#ifdef CHDIR
+static void
+chdirx(const char *dir, bool wr)
+{
+#ifdef SECURE
+ if (dir /* User specified directory? */
+#ifdef HACKDIR
+ && strcmp(dir, HACKDIR) /* and not the default? */
+#endif
+ ) {
+ /* revoke */
+ 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) {
+ int fd;
+
+ if (dir == NULL)
+ dir = ".";
+ if ((fd = open(RECORD, O_RDWR)) < 0) {
+ printf("Warning: cannot write %s/%s", dir, RECORD);
+ getret();
+ } else
+ close(fd);
+ }
+}
+#endif
+
+void
+stop_occupation(void)
+{
+ if (occupation) {
+ pline("You stop %s.", occtxt);
+ occupation = NULL;
+ }
+}
diff --git a/hack/hack.makemon.c b/hack/hack.makemon.c
new file mode 100644
index 0000000..4c9bb07
--- /dev/null
+++ b/hack/hack.makemon.c
@@ -0,0 +1,212 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.makemon.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.makemon.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.makemon.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+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(struct permonst *ptr, int x, int y)
+{
+ struct monst *mtmp;
+ int tmp, ct;
+ boolean anything = (!ptr);
+
+ if (x != 0 || y != 0)
+ if (m_at(x, y))
+ return (NULL);
+ if (ptr) {
+ if (strchr(fut_geno, ptr->mlet))
+ return (NULL);
+ } else {
+ ct = CMNUM - strlen(fut_geno);
+ if (strchr(fut_geno, 'm')) /* make only 1 minotaur */
+ ct++;
+ if (strchr(fut_geno, '@'))
+ ct++;
+ if (ct <= 0) /* no more monsters! */
+ return (0);
+ 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 (strchr(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; (unsigned)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)
+ mkobj_at(0, mtmp->mx, mtmp->my);
+ }
+ if (ptr->mlet == ':') {
+ mtmp->cham = 1;
+ 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 && strchr("&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 mm;
+ int cnt = rnd(10);
+ mm.x = x;
+ mm.y = y;
+ while (cnt--) {
+ mm = enexto(mm.x, mm.y);
+ makemon(ptr, mm.x, mm.y);
+ }
+ }
+
+ return (mtmp);
+}
+
+coord
+enexto(xchar xx, xchar yy)
+{
+ 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)]);
+}
+
+bool
+goodpos(int x, int 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)
+ ));
+}
+
+void
+rloc(struct monst *mtmp)
+{
+ int tx, ty;
+ char ch = mtmp->data->mlet;
+
+#ifndef NOWORM
+ if (ch == 'w' && mtmp->mx) /* do not relocate worms */
+ return;
+#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(char let, int x, int y)
+{
+ int ct;
+ 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/hack/hack.mfndpos.h b/hack/hack.mfndpos.h
new file mode 100644
index 0000000..f4da529
--- /dev/null
+++ b/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/hack/hack.mhitu.c b/hack/hack.mhitu.c
new file mode 100644
index 0000000..b9b410d
--- /dev/null
+++ b/hack/hack.mhitu.c
@@ -0,0 +1,385 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mhitu.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.mhitu.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.mhitu.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+/*
+ * mhitu: monster hits you
+ * returns 1 if monster dies (e.g. 'y', 'F'), 0 otherwise
+ */
+bool
+mhitu(struct monst *mtmp)
+{
+ struct permonst *mdat = mtmp->data;
+ 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 (!strchr("1&DuxynNF", mdat->mlet))
+ tmp = hitu(mtmp, d(mdat->damn, mdat->damd));
+ else
+ tmp = 0;
+ if (strchr(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)) /* he disappeared */
+ return (1);
+ break;
+ case '&':
+ if (!mtmp->cham && !mtmp->mcan && !rn2(13)) {
+ makemon(PM_DEMON, u.ux, u.uy);
+ } else {
+ hitu(mtmp, d(2, 6));
+ hitu(mtmp, d(2, 6));
+ hitu(mtmp, rnd(3));
+ hitu(mtmp, rnd(3));
+ 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':
+ 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;
+ }
+ break;
+ case 'D':
+ if (rn2(6) || mtmp->mcan) {
+ hitu(mtmp, d(3, 10));
+ hitu(mtmp, rnd(8));
+ 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':
+ hitu(mtmp, d(2, (flags.moonphase == FULL_MOON) ? 3 : 4));
+ break;
+ case 'e':
+ 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) {
+ hitu(mtmp, rnd(4));
+ 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 {
+ hitu(mtmp, d(2, 6));
+ 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
+ hitu(mtmp, d(2, 4));
+ break;
+ case 'Q':
+ hitu(mtmp, rnd(2));
+ 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);
+ hitu(mtmp, rnd(8));
+ hitu(mtmp, rnd(8));
+ break;
+ case 'T':
+ hitu(mtmp, rnd(6));
+ hitu(mtmp, rnd(6));
+ break;
+ case 't':
+ if (!rn2(5))
+ rloc(mtmp);
+ break;
+ case 'u':
+ mtmp->mflee = 1;
+ break;
+ case 'U':
+ hitu(mtmp, d(3, 4));
+ 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':
+ hitu(mtmp, rnd(5));
+ hitu(mtmp, rnd(5));
+ hitu(mtmp, rnd(5));
+ break;
+ case 'x':
+ {
+ 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':
+ hitu(mtmp, rnd(6));
+ break;
+ }
+ if (u.uhp < 1)
+ done_in_by(mtmp);
+ return (0);
+}
+
+bool
+hitu(struct monst *mtmp, int dam)
+{
+ bool res;
+ int tmp;
+
+ nomul(0);
+ if (u.uswallow)
+ return (0);
+
+ if (mtmp->mhide && mtmp->mundetected) {
+ mtmp->mundetected = 0;
+ if (!Blind) {
+ struct obj *obj;
+ if ((obj = o_at(mtmp->mx, mtmp->my)) != NULL)
+ 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/hack/hack.mklev.c b/hack/hack.mklev.c
new file mode 100644
index 0000000..ad51f4b
--- /dev/null
+++ b/hack/hack.mklev.c
@@ -0,0 +1,795 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mklev.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.mklev.c,v 1.6 1999/11/16 10:26:36 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.mklev.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+#define somex() ((random()%(croom->hx-croom->lx+1))+croom->lx)
+#define somey() ((random()%(croom->hy-croom->ly+1))+croom->ly)
+
+#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;
+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 */
+
+static bool makerooms(void);
+static void addrs(int, int, int, int);
+static void addrsx(int, int, int, int, bool);
+static int comp(const void *, const void *);
+static coord finddpos(int, int, int, int);
+static bool okdoor(int, int);
+static void dodoor(int, int, struct mkroom *);
+static void dosdoor(int, int, struct mkroom *, int);
+static bool maker(schar, schar, schar, schar);
+static void makecorridors(void);
+static void join(int, int);
+static void make_niches(void);
+static void makevtele(void);
+static void makeniche(bool);
+
+void
+makelevel(void)
+{
+ struct mkroom *croom, *troom;
+ unsigned tryct;
+ int 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;
+ 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))
+ makemon(NULL, 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)) {
+ mkobj_at(0, somex(), somey());
+ tryct = 0;
+ while (!rn2(5)) {
+ if (++tryct > 100) {
+ printf("tryct overflow4\n");
+ break;
+ }
+ 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 */
+}
+
+static bool
+makerooms(void)
+{
+ struct rectangle *rsp;
+ 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 */
+}
+
+static void
+addrs(int lowx, int lowy, int hix, int hiy)
+{
+ struct rectangle *rsp;
+ 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);
+ }
+}
+
+/* discarded: piece of a discarded area */
+static void
+addrsx(int lx, int ly, int hx, int hy, bool discarded)
+{
+ 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;
+}
+
+static int
+comp(const void *vx, const void *vy)
+{
+ const struct mkroom *x, *y;
+
+ x = vx;
+ y = vy;
+ if (x->lx < y->lx)
+ return (-1);
+ return (x->lx > y->lx);
+}
+
+static coord
+finddpos(int xl, int yl, int xh, int yh)
+{
+ coord ff;
+ int 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] */
+static bool
+okdoor(int x, int 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);
+}
+
+static void
+dodoor(int x, int y, struct mkroom *aroom)
+{
+ if (doorindex >= DOORMAX) {
+ impossible("DOORMAX exceeded?");
+ return;
+ }
+ if (!okdoor(x, y) && nxcor)
+ return;
+ dosdoor(x, y, aroom, rn2(8) ? DOOR : SDOOR);
+}
+
+static void
+dosdoor(int x, int y, struct mkroom *aroom, int type)
+{
+ struct mkroom *broom;
+ int 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() */
+static bool
+maker(schar lowx, schar ddx, schar lowy, schar ddy)
+{
+ struct mkroom *croom;
+ int x, y, hix = lowx + ddx, hiy = lowy + ddy;
+ int 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);
+}
+
+static void
+makecorridors(void)
+{
+ int 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);
+ }
+}
+
+static void
+join(int a, int b)
+{
+ coord cc, tt;
+ int tx, ty, xx, yy;
+ struct rm *crm;
+ struct mkroom *croom, *troom;
+ int 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))
+ 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) {
+ int 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) {
+ int 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];
+}
+
+static void
+make_niches(void)
+{
+ int ct = rnd(nroom / 2 + 1);
+ while (ct--)
+ makeniche(FALSE);
+}
+
+static void
+makevtele(void)
+{
+ makeniche(TRUE);
+}
+
+static void
+makeniche(bool with_trap)
+{
+ struct mkroom *aroom;
+ struct rm *rm;
+ int vct = 8;
+ coord dd;
+ int dy, xx, yy;
+ struct trap *ttmp;
+
+ if (doorindex < DOORMAX)
+ while (vct--) {
+ aroom = &rooms[rn2(nroom - 1)];
+ if (aroom->rtype != 0) /* not an ordinary room */
+ continue;
+ 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))
+ mkobj_at(0, xx, yy + dy);
+ }
+ }
+ return;
+ }
+}
+
+/* make a trap somewhere (in croom if mazeflag = 0) */
+void
+mktrap(int num, int mazeflag, struct mkroom *croom)
+{
+ struct trap *ttmp;
+ int kind, nopierc, nomimic, fakedoor, fakegold, tryct = 0;
+ xchar mx, my;
+
+ if (!num || num >= TRAPNUM) {
+ nopierc = (dlevel < 4) ? 1 : 0;
+ nomimic = (dlevel < 9 || goldseen) ? 1 : 0;
+ if (strchr(fut_geno, 'M'))
+ nomimic = 1;
+ kind = rn2(TRAPNUM - nopierc - nomimic);
+ /* note: PIERC = 7, MIMIC = 8, TRAPNUM = 9 */
+ } else
+ kind = num;
+
+ if (kind == MIMIC) {
+ 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) {
+ 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)) != NULL) {
+ mtmp->mimic = 1;
+ mtmp->mappearance =
+ fakegold ? '$' : fakedoor ? '+' :
+ (mazeflag && rn2(2)) ? AMULET_SYM :
+ "=/)%?![<>"[rn2(9)];
+ }
+ return;
+ }
+
+ do {
+ if (++tryct > 200)
+ return;
+ if (mazeflag) {
+ 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/hack/hack.mkmaze.c b/hack/hack.mkmaze.c
new file mode 100644
index 0000000..29800c0
--- /dev/null
+++ b/hack/hack.mkmaze.c
@@ -0,0 +1,157 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mkmaze.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.mkmaze.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.mkmaze.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+extern struct permonst pm_wizard;
+struct permonst hell_hound =
+ { "hell hound", 'd', 12, 14, 2, 3, 6, 0 };
+
+static void walkfrom(int, int);
+static void move(int *, int *, int);
+static bool okay(int, int, int);
+
+void
+makemaz(void)
+{
+ int x, y;
+ int 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) {
+ 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;
+ }
+
+ mkobj_at(AMULET_SYM, zx, zy);
+ flags.made_amulet = 1;
+ walkfrom(zx + 4, zy);
+ if ((mtmp = makemon(&hell_hound, zx, zy)) != NULL)
+ mtmp->msleep = 1;
+ if ((mtmp = makemon(PM_WIZARD, zx + 1, zy)) != NULL) {
+ mtmp->msleep = 1;
+ flags.no_of_wizards = 1;
+ }
+ } else {
+ mm = mazexy();
+ zx = mm.x;
+ zy = mm.y;
+ walkfrom(zx, zy);
+ mksobj_at(WAN_WISHING, zx, zy);
+ 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();
+ mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y);
+ }
+ for (x = rn1(10, 2); x; x--) {
+ mm = mazexy();
+ mkobj_at(ROCK_SYM, mm.x, mm.y);
+ }
+ mm = mazexy();
+ makemon(PM_MINOTAUR, mm.x, mm.y);
+ for (x = rn1(5, 7); x; x--) {
+ mm = mazexy();
+ makemon(NULL, 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, NULL);
+ mm = mazexy();
+ levl[(xupstair = mm.x)][(yupstair = mm.y)].scrsym = '<';
+ levl[xupstair][yupstair].typ = STAIRS;
+ xdnstair = ydnstair = 0;
+}
+
+static void
+walkfrom(int x, int y)
+{
+ int q, a, dir;
+ int dirs[4];
+
+ levl[x][y].typ = ROOM;
+ for (;;) {
+ 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);
+ }
+}
+
+static void
+move(int *x, int *y, int dir)
+{
+ switch (dir) {
+ case 0:
+ --(*y);
+ break;
+ case 1:
+ (*x)++;
+ break;
+ case 2:
+ (*y)++;
+ break;
+ case 3:
+ --(*x);
+ break;
+ }
+}
+
+static bool
+okay(int x, int y, 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(void)
+{
+ coord mm;
+
+ mm.x = 3 + 2 * rn2(COLNO / 2 - 2);
+ mm.y = 3 + 2 * rn2(ROWNO / 2 - 2);
+ return (mm);
+}
diff --git a/hack/hack.mkobj.c b/hack/hack.mkobj.c
new file mode 100644
index 0000000..cf44d1a
--- /dev/null
+++ b/hack/hack.mkobj.c
@@ -0,0 +1,160 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mkobj.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.mkobj.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.mkobj.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%";
+
+struct obj *
+mkobj_at(int let, int x, int y)
+{
+ struct obj *otmp = mkobj(let);
+
+ otmp->ox = x;
+ otmp->oy = y;
+ otmp->nobj = fobj;
+ fobj = otmp;
+ return (otmp);
+}
+
+void
+mksobj_at(int otyp, int x, int y)
+{
+ struct obj *otmp = mksobj(otyp);
+
+ otmp->ox = x;
+ otmp->oy = y;
+ otmp->nobj = fobj;
+ fobj = otmp;
+}
+
+struct obj *
+mkobj(int 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(int otyp)
+{
+ 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 = strchr("/=!?*", 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);
+}
+
+bool
+letter(char c)
+{
+ return (('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z'));
+}
+
+int
+weight(struct obj *obj)
+{
+ int wt = objects[obj->otyp].oc_weight;
+ return (wt ? wt * obj->quan : (obj->quan + 1) / 2);
+}
+
+void
+mkgold(long num, int x, int y)
+{
+ struct gold *gold;
+ long amount = (num ? num : 1 + (rnd(dlevel + 2) * rnd(30)));
+
+ if ((gold = g_at(x, y)) != NULL)
+ 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/hack/hack.mkshop.c b/hack/hack.mkshop.c
new file mode 100644
index 0000000..48c217e
--- /dev/null
+++ b/hack/hack.mkshop.c
@@ -0,0 +1,304 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mkshop.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.mkshop.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.mkshop.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#ifndef QUEST
+#include "hack.h"
+#include "def.eshk.h"
+#define ESHK ((struct eshk *)(&(shk->mextra[0])))
+extern int nroom;
+extern char shtypes[]; /* = "=/)%?!["; 8 types: 7 specialized, 1 mixed */
+schar shprobs[] = { 3,3,5,5,10,10,14,50 }; /* their probabilities */
+
+static struct permonst *morguemon(void);
+static bool nexttodoor(int, int);
+static bool has_dnstairs(struct mkroom *);
+static bool has_upstairs(struct mkroom *);
+static bool isbig(struct mkroom *);
+static int dist2(int, int, int, int);
+static int sq(int);
+
+void
+mkshop(void)
+{
+ struct mkroom *sroom;
+ int sh, sx, sy, i = -1;
+ char let;
+ int roomno;
+ struct monst *shk;
+
+#ifdef WIZARD
+ /* first determine shoptype */
+ if (wizard) {
+ 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 */
+ int j;
+
+ for (j = rn2(100), i = 0; (j -= shprobs[i]) >= 0; i++)
+ if (!shtypes[i]) /* superfluous */
+ break;
+ 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) {
+ int j = sroom->doorct;
+
+ 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++) {
+ 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;
+ }
+ mkobj_at(let, sx, sy);
+ }
+}
+
+void
+mkzoo(int type)
+{
+ struct mkroom *sroom;
+ struct monst *mon;
+ int sh, sx, sy, i;
+ int goldlim = 500 * dlevel;
+ int moct = 0;
+
+ 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 : NULL,
+ 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;
+ }
+ }
+}
+
+static struct permonst *
+morguemon(void)
+{
+ 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);
+}
+
+void
+mkswamp(void) /* Michiel Huisjes & Fred de Wilde */
+{
+ struct mkroom *sroom;
+ int sx, sy, i, eelct = 0;
+
+ 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)) {
+ makemon(PM_EEL, sx, sy);
+ eelct++;
+ }
+ }
+ }
+}
+
+static bool
+nexttodoor(int sx, int sy)
+{
+ int dx, dy;
+ 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);
+}
+
+static bool
+has_dnstairs(struct mkroom *sroom)
+{
+ return (sroom->lx <= xdnstair && xdnstair <= sroom->hx &&
+ sroom->ly <= ydnstair && ydnstair <= sroom->hy);
+}
+
+static bool
+has_upstairs(struct mkroom *sroom)
+{
+ return (sroom->lx <= xupstair && xupstair <= sroom->hx &&
+ sroom->ly <= yupstair && yupstair <= sroom->hy);
+}
+
+static bool
+isbig(struct mkroom *sroom)
+{
+ int area = (sroom->hx - sroom->lx) * (sroom->hy - sroom->ly);
+ return (area > 20);
+}
+
+static int
+dist2(int x0, int y0, int x1, int y1)
+{
+ return ((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1));
+}
+
+static int
+sq(int a)
+{
+ return (a * a);
+}
+#endif /* QUEST */
diff --git a/hack/hack.mon.c b/hack/hack.mon.c
new file mode 100644
index 0000000..2554926
--- /dev/null
+++ b/hack/hack.mon.c
@@ -0,0 +1,961 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.mon.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.mon.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.mon.c,v 1.6 2008/06/05 18:01:49 swildner Exp $ */
+
+#include "hack.h"
+#include "hack.mfndpos.h"
+
+int warnlevel; /* used by movemon and dochugw */
+long lastwarntime;
+int lastwarnlev;
+static const char *warnings[] = {
+ "white", "pink", "red", "ruby", "purple", "black"
+};
+
+static int dochugw(struct monst *);
+static void mpickgold(struct monst *);
+static void mpickgems(struct monst *);
+static void dmonsfree(void);
+static bool ishuman(struct monst *);
+
+void
+movemon(void)
+{
+ struct monst *mtmp;
+ int fr;
+
+ warnlevel = 0;
+
+ for (;;) {
+ /* 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) {
+ const 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 */
+}
+
+void
+justswld(struct monst *mtmp, const 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();
+}
+
+void
+youswld(struct monst *mtmp, int dam, int die, const char *name)
+{
+ if (mtmp != u.ustuck)
+ return;
+ kludge("%s digests you!", name);
+ u.uhp -= dam;
+ if ((int)u.uswldtim++ >= die) { /* a3 */
+ pline("It totally digests you!");
+ u.uhp = -1;
+ }
+ if (u.uhp < 1)
+ done_in_by(mtmp);
+#if 0
+ flags.botlx = 1; /* should we show status line ? */
+#endif
+}
+
+static int
+dochugw(struct monst *mtmp)
+{
+ int x = mtmp->mx;
+ int y = mtmp->my;
+ int d1 = dochug(mtmp);
+ int dd;
+
+ if (!d1) /* 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 (d1);
+}
+
+/* returns 1 if monster died moving, 0 otherwise */
+bool
+dochug(struct monst *mtmp)
+{
+ struct permonst *mdat;
+ int tmp = 0, nearby, scared;
+
+ if (mtmp->cham && !rn2(6))
+ 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) || strchr(MREGEN, mdat->mlet)) &&
+ mtmp->mhp < mtmp->mhpmax)
+ mtmp->mhp++;
+
+ if (mtmp->mfroz) /* frozen monsters don't do anything */
+ return (0);
+
+ 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))) &&
+ (!strchr("NL", mdat->mlet) || !rn2(50)) &&
+ (Aggravate_monster || strchr("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 && strchr("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)) ||
+ (strchr("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 (!strchr("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);
+}
+
+int
+m_move(struct monst *mtmp, int after)
+{
+ struct monst *mtmp2;
+ int nx, ny, omx, omy, appr, nearer, cnt, i, j;
+ xchar gx, gy, nix, niy, chcnt;
+ schar chi;
+ boolean likegold = 0, likegems = 0, 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) /* he died */
+ return (2);
+ if (i == 1) /* still in trap, so didnt move */
+ return (0);
+ }
+ 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 (strchr("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 ||
+ (strchr("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')) {
+ 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 = (strchr("LOD", msym) != NULL);
+ likegems = (strchr("ODu", msym) != NULL);
+ likeobjs = mtmp->mhide;
+#define SRCHRADIUS 25
+ {
+ xchar mind = SRCHRADIUS; /* not too far away */
+ int dd;
+ if (likegold) {
+ 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) {
+ 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) :
+ strchr(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) {
+ 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);
+}
+
+static void
+mpickgold(struct monst *mtmp)
+{
+ struct gold *gold;
+
+ while ((gold = g_at(mtmp->mx, mtmp->my)) != NULL) {
+ mtmp->mgold += gold->amount;
+ freegold(gold);
+ if (levl[mtmp->mx][mtmp->my].scrsym == '$')
+ newsym(mtmp->mx, mtmp->my);
+ }
+}
+
+static void
+mpickgems(struct monst *mtmp)
+{
+ 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 */
+int
+mfndpos(struct monst *mon, coord poss[9], int info[9], int flag)
+{
+ int x, y, nx, ny, cnt = 0, ntyp;
+ 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)) != NULL) {
+ 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 */
+ {
+ struct trap *ttmp = t_at(nx, ny);
+ 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);
+}
+
+int
+dist(int x, int y)
+{
+ return ((x - u.ux) * (x - u.ux) + (y - u.uy) * (y - u.uy));
+}
+
+void
+poisoned(const char *string, const char *pname)
+{
+ 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");
+ }
+}
+
+void
+mondead(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 */
+void
+replmon(struct monst *mtmp, struct monst *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);
+}
+
+void
+relmon(struct monst *mon)
+{
+ struct monst *mtmp;
+
+ if (mon == fmon)
+ fmon = fmon->nmon;
+ else {
+ for (mtmp = fmon; mtmp->nmon != mon; mtmp = mtmp->nmon)
+ ; /* nothing */
+ 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 */
+
+void
+monfree(struct monst *mtmp)
+{
+ mtmp->nmon = fdmon;
+ fdmon = mtmp;
+}
+
+static void
+dmonsfree(void)
+{
+ struct monst *mtmp;
+
+ while ((mtmp = fdmon) != NULL) {
+ fdmon = mtmp->nmon;
+ free(mtmp);
+ }
+}
+
+void
+unstuck(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;
+ }
+}
+
+void
+killed(struct monst *mtmp)
+{
+#ifdef lint
+#define NEW_SCORING
+ int tmp2;
+#endif /* lint */
+ int tmp, nk, x, y;
+ struct permonst *mdat;
+
+ 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) {
+ u.nr_killed[tmp]++;
+ if ((nk = u.nr_killed[tmp]) > MAXMONNO &&
+ !strchr(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 (strchr("AcsSDXaeRTVWU&In:P", mdat->mlet))
+ tmp += 2 * mdat->mlevel;
+ if (strchr("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) || (!strchr("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 (strchr("NTVm&", mdat->mlet) || rn2(5)) {
+ struct obj *obj2 = mkobj_at(tmp, x, y);
+ if (cansee(x, y))
+ atl(x, y, obj2->olet);
+ stackobj(obj2);
+ }
+}
+
+void
+kludge(const char *str, const char *arg)
+{
+ if (Blind) {
+ if (*str == '%')
+ pline(str, "It");
+ else
+ pline(str, "it");
+ } else
+ pline(str, arg);
+}
+
+void
+rescham(void) /* force all chameleons to become normal */
+{
+ struct monst *mtmp;
+
+ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if (mtmp->cham) {
+ mtmp->cham = 0;
+ newcham(mtmp, PM_CHAMELEON);
+ }
+}
+
+/* make a chameleon look like a new monster */
+/* returns 1 if the monster actually changed */
+bool
+newcham(struct monst *mtmp, struct permonst *mdat)
+{
+ int mhp, hpn, hpd;
+
+ if (mdat == mtmp->data) /* still the same monster */
+ return (0);
+#ifndef NOWORM
+ if (mtmp->wormno) /* throw tail away */
+ wormdead(mtmp);
+#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);
+}
+
+/* Make monster mtmp next to you (if possible) */
+void
+mnexto(struct monst *mtmp)
+{
+ coord mm;
+ mm = enexto(u.ux, u.uy);
+ mtmp->mx = mm.x;
+ mtmp->my = mm.y;
+ pmon(mtmp);
+}
+
+static bool
+ishuman(struct monst *mtmp)
+{
+ return (mtmp->data->mlet == '@');
+}
+
+void
+setmangry(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 percent correct: now a snake may hide under an invisible
+ * object
+ */
+bool
+canseemon(struct monst *mtmp)
+{
+ return ((!mtmp->minvis || See_invisible)
+ && (!mtmp->mhide || !o_at(mtmp->mx, mtmp->my))
+ && cansee(mtmp->mx, mtmp->my));
+}
diff --git a/hack/hack.monst.c b/hack/hack.monst.c
new file mode 100644
index 0000000..5b743ac
--- /dev/null
+++ b/hack/hack.monst.c
@@ -0,0 +1,79 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.monst.c - version 1.0.2 */
+/* $DragonFly: src/games/hack/hack.monst.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+#include "def.eshk.h"
+
+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/hack/hack.o_init.c b/hack/hack.o_init.c
new file mode 100644
index 0000000..dc17896
--- /dev/null
+++ b/hack/hack.o_init.c
@@ -0,0 +1,184 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.o_init.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.o_init.c,v 1.6 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.o_init.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "def.objects.h"
+#include "hack.h"
+
+static void setgemprobs(void);
+static bool interesting_to_discover(int);
+
+int
+letindex(char let)
+{
+ int i = 0;
+ char ch;
+
+ while ((ch = obj_symbols[i++]) != 0)
+ if (ch == let)
+ return (i);
+ return (0);
+}
+
+void
+init_objects(void)
+{
+ int i, j, first, last, sum, end;
+ char let;
+ const char *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;
+ }
+}
+
+int
+probtype(char let)
+{
+ int i = bases[letindex(let)];
+ 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);
+}
+
+static void
+setgemprobs(void)
+{
+ int j, first;
+
+ 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);
+}
+
+void
+oinit(void) /* level dependent initialization */
+{
+ setgemprobs();
+}
+
+void
+savenames(int fd)
+{
+ 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);
+ }
+ }
+}
+
+void
+restnames(int fd)
+{
+ 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 = alloc(len);
+ mread(fd, objects[i].oc_uname, len);
+ }
+}
+
+int
+dodiscovered(void) /* free after Robert Viduya */
+{
+ 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, NULL);
+ } else
+ cornline(2, NULL);
+
+ return (0);
+}
+
+static bool
+interesting_to_discover(int i)
+{
+ return (
+ objects[i].oc_uname != NULL ||
+ (objects[i].oc_name_known && objects[i].oc_descr != NULL)
+ );
+}
diff --git a/hack/hack.objnam.c b/hack/hack.objnam.c
new file mode 100644
index 0000000..2dc2397
--- /dev/null
+++ b/hack/hack.objnam.c
@@ -0,0 +1,584 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.objnam.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.objnam.c,v 1.3 1999/11/16 02:57:08 billf Exp $ */
+
+#include "hack.h"
+#define Sprintf (void) sprintf
+#define Strcat (void) strcat
+#define Strcpy (void) strcpy
+#define PREFIX 15
+extern int bases[];
+
+static char *strprepend(char *, char *);
+static char *sitoa(int);
+
+static char *
+strprepend(char *s, char *pref)
+{
+ int i = strlen(pref);
+
+ if (i > PREFIX) {
+ pline("WARNING: prefix too short.");
+ return (s);
+ }
+ s -= i;
+ strncpy(s, pref, i); /* do not copy trailing 0 */
+ return (s);
+}
+
+static char *
+sitoa(int a)
+{
+ static char buf[13];
+
+ Sprintf(buf, (a < 0) ? "%d" : "+%d", a);
+ return (buf);
+}
+
+char *
+typename(int otyp)
+{
+ static char buf[BUFSZ];
+ struct objclass *ocl = &objects[otyp];
+ const char *an = ocl->oc_name;
+ const char *dn = ocl->oc_descr;
+ char *un = ocl->oc_uname;
+ 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(struct obj *obj)
+{
+ static char bufr[BUFSZ];
+ /* caution: doname() and aobjnam() below "know" these sizes */
+ char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */
+ int nn = objects[obj->otyp].oc_name_known;
+ const char *an = objects[obj->otyp].oc_name;
+ const char *dn = objects[obj->otyp].oc_descr;
+ char *un = objects[obj->otyp].oc_uname;
+ int pl = (obj->quan != 1);
+
+ if (!obj->dknown && !Blind) /* %% doesnt belong here */
+ obj->dknown = 1;
+ 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) {
+ char *p;
+
+ for (p = buf; *p; p++)
+ if (!strncmp(" of ", p, 4)) {
+ /* pieces of, cloves of, lumps of */
+ 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' && !strchr(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(struct obj *obj)
+{
+ char prefix[PREFIX];
+ 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 ") && strchr(vowels, *bp))
+ Strcpy(prefix, "an ");
+ bp = strprepend(bp, prefix);
+ return (bp);
+}
+
+/* used only in hack.fight.c (thitu) */
+void
+setan(const char *str, char *buf)
+{
+ if (strchr(vowels, *str))
+ Sprintf(buf, "an %s", str);
+ else
+ Sprintf(buf, "a %s", str);
+}
+
+char *
+aobjnam(struct obj *otmp, const char *verb)
+{
+ 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(struct obj *obj)
+{
+ char *s = doname(obj);
+
+ if ('a' <= *s && *s <= 'z')
+ *s -= ('a' - 'A');
+ return (s);
+}
+
+static const char *wrp[] = { "wand", "ring", "potion", "scroll", "gem" };
+char wrpsym[] = { WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM };
+
+struct obj *
+readobjnam(char *bp)
+{
+ char *p;
+ int i;
+ int cnt, spe, spesgn, typ, heavy;
+ char let;
+ char *un, *dn, *an;
+
+ cnt = spe = spesgn = typ = heavy = 0;
+ let = 0;
+ an = dn = un = NULL;
+ for (p = bp; *p; p++)
+ if ('A' <= *p && *p <= 'Z')
+ *p += 'a' - 'A';
+ if (!strncmp(bp, "the ", 4))
+ 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) /* %% what with "gems" etc. ? */
+ cnt = 1;
+
+ if (*bp == '+' || *bp == '-') {
+ spesgn = (*bp++ == '+') ? 1 : -1;
+ spe = atoi(bp);
+ while (digit(*bp))
+ bp++;
+ while (*bp == ' ')
+ bp++;
+ } else {
+ p = strrchr(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;
+
+ 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; (unsigned)i < sizeof(wrpsym); i++) {
+ 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)) {
+ const 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:
+ {
+ struct obj *otmp;
+ let = objects[typ].oc_olet;
+ otmp = mksobj(typ);
+ if (heavy)
+ otmp->owt += 15;
+ if (cnt > 0 && strchr("%?!*)", 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/hack/hack.options.c b/hack/hack.options.c
new file mode 100644
index 0000000..63a8c2a
--- /dev/null
+++ b/hack/hack.options.c
@@ -0,0 +1,217 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.options.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.options.c,v 1.5 1999/11/16 02:57:08 billf Exp $ */
+
+#include "hack.h"
+
+static void parseoptions(char *, bool);
+
+void
+initoptions(void)
+{
+ char *opts;
+
+ 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);
+}
+
+static void
+parseoptions(char *opts, bool from_env)
+{
+ char *op, *op2;
+ unsigned num;
+ boolean negated;
+
+ if ((op = strchr(opts, ',')) != NULL) {
+ *op++ = 0;
+ parseoptions(op, from_env);
+ }
+ if ((op = strchr(opts, ' ')) != NULL) {
+ 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)) {
+ if (!from_env) {
+ pline("The playername can be set only from HACKOPTIONS.");
+ return;
+ }
+ op = strchr(opts, ':');
+ if (!op)
+ goto bad;
+ strncpy(plname, op + 1, sizeof(plname) - 1);
+ return;
+ }
+
+ /* endgame:5t[op] 5a[round] o[wn] */
+ if (!strncmp(opts, "endgame", 3)) {
+ op = strchr(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))
+ ; /* nothing */
+ 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();
+}
+
+int
+doset(void)
+{
+ char buf[BUFSZ];
+
+ pline("What options do you want to set? ");
+ getlin(buf);
+ if (!buf[0] || buf[0] == '\033') {
+ strcpy(buf, "HACKOPTIONS=");
+ strcat(buf, flags.female ? "female," : "male,");
+ if (flags.standout)
+ strcat(buf, "standout,");
+ if (flags.nonull)
+ strcat(buf, "nonull,");
+ if (flags.nonews)
+ strcat(buf, "nonews,");
+ if (flags.time)
+ strcat(buf, "time,");
+ if (flags.notombstone)
+ strcat(buf, "notombstone,");
+ if (flags.no_rest_on_space)
+ strcat(buf, "!rest_on_space,");
+ if (flags.end_top != 5 || flags.end_around != 4 || flags.end_own) {
+ sprintf(eos(buf), "endgame: %u topscores/%u around me",
+ flags.end_top, flags.end_around);
+ if (flags.end_own)
+ strcat(buf, "/own scores");
+ } else {
+ char *eop = eos(buf);
+ if (*--eop == ',')
+ *eop = 0;
+ }
+ pline("%s", buf);
+ } else
+ parseoptions(buf, FALSE);
+
+ return (0);
+}
diff --git a/hack/hack.pager.c b/hack/hack.pager.c
new file mode 100644
index 0000000..0705233
--- /dev/null
+++ b/hack/hack.pager.c
@@ -0,0 +1,418 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.pager.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.pager.c,v 1.7 1999/11/16 02:57:09 billf Exp $ */
+
+/* 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 <signal.h>
+#include "hack.h"
+extern int CO, LI; /* usually COLNO and ROWNO+2 */
+extern char *CD;
+extern char quitchars[];
+
+static void intruph(int);
+static void page_more(FILE *, int);
+
+int
+dowhatis(void)
+{
+ FILE *fp;
+ char bufr[BUFSZ + 6];
+ char *buf = &bufr[6], *ep, q;
+
+ 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 = strchr(buf, '\n');
+ if (ep)
+ *ep = 0;
+ /* else: bad data file */
+ /* Expand tab 'by hand' */
+ if (buf[1] == '\t') {
+ buf = bufr;
+ buf[0] = q;
+ strncpy(buf + 1, " ", 7);
+ }
+ pline("%s", buf);
+ if (ep[-1] == ';') {
+ pline("More info? ");
+ if (readchar() == 'y') {
+ page_more(fp, 1); /* does fclose() */
+ return (0);
+ }
+ }
+ fclose(fp); /* kopper@psuvax1 */
+ return (0);
+ }
+ pline("I've never heard of such things.");
+ fclose(fp);
+ }
+ return (0);
+}
+
+/* make the paging of a file interruptible */
+static int got_intrup;
+
+static void
+intruph(int unused __attribute__((unused)))
+{
+ got_intrup++;
+}
+
+/* simple pager, also used from dohelp() */
+/* strip: nr of chars to be stripped from each line (0 or 1) */
+static void
+page_more(FILE *fp, int strip)
+{
+ char *bufr, *ep;
+ sig_t prevsig = signal(SIGINT, intruph);
+
+ set_pager(0);
+ bufr = alloc((unsigned)CO);
+ bufr[CO - 1] = 0;
+ while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) {
+ ep = strchr(bufr, '\n');
+ if (ep)
+ *ep = 0;
+ if (page_line(bufr + strip)) {
+ set_pager(2);
+ goto ret;
+ }
+ }
+ set_pager(1);
+ret:
+ free(bufr);
+ fclose(fp);
+ signal(SIGINT, prevsig);
+ got_intrup = 0;
+}
+
+static boolean whole_screen = TRUE;
+#define PAGMIN 12 /* minimum # of lines for page below level map */
+
+void
+set_whole_screen(void) /* called in termcap as soon as LI is known */
+{
+ whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD);
+}
+
+#ifdef NEWS
+bool
+readnews(void)
+{
+ 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 */
+
+void
+set_pager(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();
+ }
+ }
+}
+
+bool
+page_line(const char *s) /* returns 1 if we should quit */
+{
+ 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
+ */
+
+void
+cornline(int mode, const char *text)
+{
+ static struct line {
+ struct line *next_line;
+ char *line_text;
+ } *texthead, *texttail;
+ static int maxlen;
+ static int linect;
+ struct line *tl;
+
+ if (mode == 0) {
+ texthead = NULL;
+ maxlen = 0;
+ linect = 0;
+ if (text) {
+ cornline(1, text); /* title */
+ cornline(1, ""); /* blank line */
+ }
+ return;
+ }
+
+ if (mode == 1) {
+ int len;
+
+ if (!text) /* superfluous, just to be sure */
+ return;
+ linect++;
+ len = strlen(text);
+ if (len > maxlen)
+ maxlen = len;
+ tl = alloc((unsigned)(len + sizeof(struct line) + 1));
+ tl->next_line = NULL;
+ tl->line_text = (char *)(tl + 1);
+ 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("%s", texthead->line_text);
+ else if (mode == 2) {
+ int curline, lth;
+
+ if (flags.toplin == 1) /* ab@unido */
+ more();
+ 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) != NULL) {
+ texthead = tl->next_line;
+ free(tl);
+ }
+}
+
+int
+dohelp(void)
+{
+ char c;
+
+ pline("Long or short help? ");
+ while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c))
+ bell();
+ if (!strchr(quitchars, c))
+ page_file((c == 'l') ? HELP : SHELP, FALSE);
+ return (0);
+}
+
+/* return: 0 - cannot open fnam; 1 - otherwise */
+bool
+page_file(const char *fnam, bool silent)
+{
+#ifdef DEF_PAGER /* this implies that UNIX is defined */
+ /* use external pager; this may give security problems */
+ int fd = open(fnam, O_RDONLY);
+
+ 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.
+ */
+ close(0);
+ if (dup(fd)) {
+ if (!silent)
+ printf("Cannot open %s as stdin.\n", fnam);
+ } else {
+ execl(catmore, "page", NULL);
+ if (!silent)
+ printf("Cannot exec %s.\n", catmore);
+ }
+ exit(1);
+ }
+ close(fd);
+#else /* DEF_PAGER */
+ FILE *f; /* free after Robert Viduya */
+
+ if ((f = fopen(fnam, "r")) == NULL) {
+ 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
+int
+dosh(void)
+{
+ char *str;
+
+ if (child(0)) {
+ if ((str = getenv("SHELL")) != NULL)
+ execl(str, str, NULL);
+ else
+ execl("/bin/sh", "sh", NULL);
+ 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
+#include <sys/wait.h>
+#endif /* NOWAITINCLUDE */
+
+bool
+child(bool wt)
+{
+ int status;
+ int f;
+
+ f = fork();
+ if (f == 0) { /* child */
+ settty(NULL); /* also calls end_screen() */
+ /* revoke */
+ setgid(getgid());
+#ifdef CHDIR
+ 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 */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ wait(&status);
+ gettty();
+ setftty();
+ signal(SIGINT, done1);
+#ifdef WIZARD
+ if (wizard)
+ signal(SIGQUIT, SIG_DFL);
+#endif /* WIZARD */
+ if (wt)
+ getret();
+ docrt();
+ return (0);
+}
+#endif /* UNIX */
diff --git a/hack/hack.potion.c b/hack/hack.potion.c
new file mode 100644
index 0000000..e7879db
--- /dev/null
+++ b/hack/hack.potion.c
@@ -0,0 +1,407 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.potion.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.potion.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */
+
+#include "hack.h"
+extern struct monst youmonst;
+
+static void ghost_from_bottle(void);
+
+int
+dodrink(void)
+{
+ struct obj *otmp, *objs;
+ struct monst *mtmp;
+ 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) /* see on next move */
+ Blind = 1;
+ 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) /* > 118 is impossible */
+ break;
+ 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);
+}
+
+void
+pluslvl(void)
+{
+ int num;
+
+ pline("You feel more experienced.");
+ num = rnd(10);
+ u.uhpmax += num;
+ u.uhp += num;
+ if (u.ulevel < 14) {
+ u.uexp = newuexp() + 1;
+ pline("Welcome to experience level %u.", ++u.ulevel);
+ }
+ flags.botl = 1;
+}
+
+void
+strange_feeling(struct obj *obj, const char *txt)
+{
+ if (flags.beginner)
+ pline("You have a strange feeling for a moment, then it passes.");
+ else
+ pline("%s", txt);
+ if (!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname)
+ docall(obj);
+ useup(obj);
+}
+
+static const char *bottlenames[] = {
+ "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
+};
+
+void
+potionhit(struct monst *mon, struct obj *obj)
+{
+ const 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);
+}
+
+void
+potionbreathe(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.
+ */
+int
+dodip(void)
+{
+ 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);
+}
+
+static void
+ghost_from_bottle(void)
+{
+ 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/hack/hack.pri.c b/hack/hack.pri.c
new file mode 100644
index 0000000..eed95b9
--- /dev/null
+++ b/hack/hack.pri.c
@@ -0,0 +1,721 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.pri.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.pri.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.pri.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+#include <curses.h>
+xchar scrlx, scrhx, scrly, scrhy; /* corners of new area on screen */
+
+extern char *CD;
+
+#ifdef NEWSCR
+static void pobj(struct obj *);
+#endif
+static void cornbot(int);
+
+void
+swallowed(void)
+{
+ 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;
+
+void
+panic(const char *str, ...)
+{
+ va_list ap;
+
+ if (panicking++) /* avoid loops - this should never happen*/
+ exit(1);
+ home();
+ puts(" Suddenly, the dungeon collapses.");
+ fputs(" ERROR: ", stdout);
+ va_start(ap, str);
+ vprintf(str, ap);
+ va_end(ap);
+#ifdef DEBUG
+#ifdef UNIX
+ if (!fork())
+ abort(); /* generate core dump */
+#endif /* UNIX */
+#endif /* DEBUG */
+ more(); /* contains a fflush() */
+ done("panicked");
+}
+
+void
+atl(int x, int y, char ch)
+{
+ 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);
+}
+
+void
+on_scr(int x, int 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
+ */
+
+void
+tmp_at(schar x, schar 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(50); 20150209 bkw: doesn't exist on linux */
+ 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 */
+void
+Tmp_at(schar x, schar y)
+{
+ static char let;
+ static xchar cnt;
+ static coord tc[COLNO]; /* but watch reflecting beams! */
+ int 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(50); 20150209 bkw: doesn't exist on linux */
+ 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 --- */
+ }
+}
+
+void
+setclipped(void)
+{
+ error("Hack needs a screen of size at least %d by %d.\n",
+ ROWNO + 2, COLNO);
+}
+
+void
+at(xchar x, xchar 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);
+ putchar(ch);
+ curx++;
+}
+
+void
+prme(void)
+{
+ if (!Invisible)
+ at(u.ux, u.uy, u.usym);
+}
+
+int
+doredraw(void)
+{
+ docrt();
+ return (0);
+}
+
+void
+docrt(void)
+{
+ int x, y;
+ struct rm *room;
+ 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();
+}
+
+void
+docorner(int xmin, int ymax)
+{
+ int x, y;
+ struct rm *room;
+ 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();
+ }
+ }
+}
+
+void
+curs_on_u(void)
+{
+ curs(u.ux, u.uy + 2);
+}
+
+void
+pru(void)
+{
+ if (u.udispl && (Invisible || u.udisx != u.ux || u.udisy != u.uy))
+ 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
+extern struct wseg *m_atseg;
+#endif /* NOWORM */
+
+/* print a position that is visible for @ */
+void
+prl(int x, int y)
+{
+ struct rm *room;
+ struct monst *mtmp;
+ 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(xchar x, xchar y)
+{
+ struct obj *otmp;
+ struct trap *ttmp;
+ struct rm *room;
+ 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;
+ default:
+ tmp = ERRCHAR;
+ }
+ return (tmp);
+}
+
+void
+newsym(int x, int 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 */
+void
+mnewsym(int x, int y)
+{
+ 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;
+ }
+ }
+}
+
+void
+nosee(int x, int y)
+{
+ 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
+void
+prl1(int x, int 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);
+ }
+}
+
+void
+nose1(int x, int 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 */
+
+bool
+vism_at(int x, int y)
+{
+ struct monst *mtmp;
+
+ return ((x == u.ux && y == u.uy && !Invisible)
+ ? 1 :
+ (mtmp = m_at(x, y))
+ ? ((Blind && Telepat) || canseemon(mtmp)) :
+ 0);
+}
+
+#ifdef NEWSCR
+static void
+pobj(struct obj *obj)
+{
+ 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 */
+
+void
+unpobj(struct obj *obj)
+{
+ if (!vism_at(obj->ox, obj->oy))
+ newsym(obj->ox, obj->oy);
+}
+
+void
+seeobjs(void)
+{
+ 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);
+ }
+}
+
+void
+seemons(void)
+{
+ 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 */
+ }
+}
+
+void
+pmon(struct monst *mon)
+{
+ 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;
+ }
+}
+
+void
+unpmon(struct monst *mon)
+{
+ if (mon->mdispl) {
+ newsym(mon->mdx, mon->mdy);
+ mon->mdispl = 0;
+ }
+}
+
+void
+nscr(void)
+{
+ int x, y;
+ 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];
+
+static void
+cornbot(int lth)
+{
+ if (lth < (int)sizeof(oldbot)) {
+ oldbot[lth] = 0;
+ flags.botl = 1;
+ }
+}
+
+void
+bot(void)
+{
+ char *ob = oldbot, *nb = newbot;
+ int i;
+
+ if (flags.botlx)
+ *ob = 0;
+ flags.botl = flags.botlx = 0;
+#ifdef GOLD_ON_BOTL
+ sprintf(newbot,
+ "Level %-2d Gold %-5lu Hp %3d(%d) Ac %-2d Str ",
+ dlevel, u.ugold, u.uhp, u.uhpmax, u.uac);
+#else
+ 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)
+ strcat(newbot, "18/**");
+ else
+ sprintf(eos(newbot), "18/%02d", u.ustr - 18);
+ } else
+ sprintf(eos(newbot), "%-2d ", u.ustr);
+#ifdef EXP_ON_BOTL
+ sprintf(eos(newbot), " Exp %2d/%-5lu ", u.ulevel, u.uexp);
+#else
+ sprintf(eos(newbot), " Exp %2u ", u.ulevel);
+#endif /* EXP_ON_BOTL */
+ strcat(newbot, hu_stat[u.uhs]);
+ if (flags.time)
+ sprintf(eos(newbot), " %ld", moves);
+ if (strlen(newbot) >= COLNO) {
+ 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);
+ putchar(*nb ? *nb : ' ');
+ curx++;
+ }
+ if (*ob)
+ ob++;
+ if (*nb)
+ nb++;
+ }
+ strcpy(oldbot, newbot);
+}
+
+#ifdef WAN_PROBING
+void
+mstatusline(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 */
+
+void
+cls(void)
+{
+ if (flags.toplin == 1)
+ more();
+ flags.toplin = 0;
+
+ clear_screen();
+
+ flags.botlx = 1;
+}
diff --git a/hack/hack.read.c b/hack/hack.read.c
new file mode 100644
index 0000000..6f74d26
--- /dev/null
+++ b/hack/hack.read.c
@@ -0,0 +1,572 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.read.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.read.c,v 1.6 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.read.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+static bool monstersym(char);
+
+int
+doread(void)
+{
+ struct obj *scroll;
+ boolean confused = (Confusion != 0);
+ boolean known = FALSE;
+
+ 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:
+ {
+ 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) {
+ 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:
+ {
+ int ct = 0;
+ 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:
+ {
+ 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(uchain);
+ uball->spe = 0;
+ uball->owornmask &= ~W_BALL;
+ uchain = uball = NULL;
+ }
+ break;
+ }
+ case SCR_CREATE_MONSTER:
+ {
+ int cnt = 1;
+
+ if (!rn2(73))
+ cnt += rnd(4);
+ if (confused)
+ cnt += 12;
+ while (cnt--)
+ makemon(confused ? PM_ACID_BLOB :
+ NULL, 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:
+ {
+ int i, j;
+ int bd = confused ? 5 : 1;
+ 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)))
+ tamedog(mtmp, NULL);
+ break;
+ }
+ case SCR_GENOCIDE:
+ {
+ char buf[BUFSZ];
+ 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 (!strchr(fut_geno, *buf))
+ charcat(fut_geno, *buf);
+ if (!strchr(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
+ int oux = u.ux, ouy = u.uy;
+ tele();
+ if (dist(oux, ouy) > 100)
+ known = TRUE;
+#else /* QUEST */
+ 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) {
+ 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 {
+ 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:
+ {
+ int ct = 0, ctu = 0;
+ struct obj *obj;
+ 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)
+ ; /* nothing */
+ return (1);
+ case SCR_MAGIC_MAPPING:
+ {
+ struct rm *lev;
+ 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:
+ {
+ 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:
+ {
+ int num = 0;
+ 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 (strchr("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);
+}
+
+int
+identify(struct obj *otmp) /* also called by newmail() */
+{
+ objects[otmp->otyp].oc_name_known = 1;
+ otmp->known = otmp->dknown = 1;
+ prinv(otmp);
+ return (1);
+}
+
+void
+litroom(bool on)
+{
+#ifndef QUEST
+ int num, zx, zy;
+#endif
+
+ /* 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 */
+static bool
+monstersym(char ch) /* arnold@ucsfcgl */
+{
+ struct permonst *mp;
+
+ /*
+ * can't genocide certain monsters
+ */
+ if (strchr("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/hack/hack.rip.c b/hack/hack.rip.c
new file mode 100644
index 0000000..33f06b2
--- /dev/null
+++ b/hack/hack.rip.c
@@ -0,0 +1,89 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.rip.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.rip.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.rip.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+static void center(int, const char *);
+
+static char rip[][60] = {
+" ----------",
+" / \\",
+" / REST \\",
+" / IN \\",
+" / PEACE \\",
+" / \\",
+" | |",
+" | |",
+" | |",
+" | |",
+" | |",
+" | 1001 |",
+" *| * * * | *",
+" _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n",
+};
+static const int n_rips = sizeof(rip) / sizeof(rip[0]);
+
+void
+outrip(void)
+{
+ char *dpx;
+ char buf[BUFSZ];
+ int j, x, y;
+
+ cls();
+ strcpy(buf, plname);
+ buf[16] = 0;
+ center(6, buf);
+ sprintf(buf, "%ld AU", u.ugold);
+ center(7, buf);
+ sprintf(buf, "killed by%s",
+ !strncmp(killer, "the ", 4) ? "" :
+ !strcmp(killer, "starvation") ? "" :
+ strchr(vowels, *killer) ? " an" : " a");
+ center(8, buf);
+ strcpy(buf, killer);
+ if (strlen(buf) > 16) {
+ 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);
+ sprintf(buf, "%4d", getyear());
+ center(11, buf);
+ for (y = 8, j = 0; j < n_rips; y++, j++) {
+ x = 0;
+ dpx = rip[j];
+ while (dpx[x]) {
+ while (dpx[x] == ' ')
+ x++;
+ curs(x, y);
+ while (dpx[x] && dpx[x] != ' ') {
+ if (done_stopprint)
+ return;
+ curx++;
+ putchar(dpx[x++]);
+ }
+ }
+ }
+ getret();
+}
+
+static void
+center(int line, const char *text)
+{
+ const char *ip = text;
+ char *op;
+
+ op = &rip[line][28 - ((strlen(text) + 1) / 2)];
+ while (*ip)
+ *op++ = *ip++;
+}
diff --git a/hack/hack.rumors.c b/hack/hack.rumors.c
new file mode 100644
index 0000000..5453732
--- /dev/null
+++ b/hack/hack.rumors.c
@@ -0,0 +1,90 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.rumors.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.rumors.c,v 1.3 1999/11/16 02:57:10 billf Exp $ */
+
+#include "hack.h" /* for RUMORFILE and BSD (index) */
+#define CHARSZ 8 /* number of bits in a char */
+int n_rumors = 0;
+int n_used_rumors = -1;
+char *usedbits;
+
+static void init_rumors(FILE *);
+static bool skipline(FILE *);
+static void outline(FILE *);
+static bool used(int);
+
+static void
+init_rumors(FILE *rumf)
+{
+ int i;
+
+ n_used_rumors = 0;
+ while (skipline(rumf))
+ n_rumors++;
+ rewind(rumf);
+ i = n_rumors / CHARSZ;
+ usedbits = alloc((unsigned)(i + 1));
+ for (; i >= 0; i--)
+ usedbits[i] = 0;
+}
+
+static bool
+skipline(FILE *rumf)
+{
+ char line[COLNO];
+
+ for (;;) {
+ if (!fgets(line, sizeof(line), rumf))
+ return (0);
+ if (strchr(line, '\n'))
+ return (1);
+ }
+}
+
+static void
+outline(FILE *rumf)
+{
+ char line[COLNO];
+ char *ep;
+
+ if (!fgets(line, sizeof(line), rumf))
+ return;
+ if ((ep = strchr(line, '\n')) != NULL)
+ *ep = 0;
+ pline("This cookie has a scrap of paper inside! It reads: ");
+ pline("%s", line);
+}
+
+void
+outrumor(void)
+{
+ int rn, i;
+ FILE *rumf;
+
+ if (n_rumors <= n_used_rumors ||
+ (rumf = fopen(RUMORFILE, "r")) == NULL)
+ 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)) {
+ skipline(rumf);
+ if (!used(i))
+ rn--;
+ i++;
+ }
+ usedbits[i / CHARSZ] |= (1 << (i % CHARSZ));
+ n_used_rumors++;
+ outline(rumf);
+none:
+ fclose(rumf);
+}
+
+static bool
+used(int i)
+{
+ return (usedbits[i / CHARSZ] & (1 << (i % CHARSZ)));
+}
diff --git a/hack/hack.save.c b/hack/hack.save.c
new file mode 100644
index 0000000..4ee9ac1
--- /dev/null
+++ b/hack/hack.save.c
@@ -0,0 +1,240 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.save.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.save.c,v 1.4 1999/11/16 10:26:37 marcel Exp $ */
+
+#include "hack.h"
+
+extern char SAVEF[], nul[];
+
+static bool dosave0(int);
+
+int
+dosave(void)
+{
+ if (dosave0(0)) {
+ settty("Be seeing you ...\n");
+ exit(0);
+ }
+ return (0);
+}
+
+#ifndef NOSAVEONHANGUP
+void
+hangup(int n __attribute__((unused)))
+{
+ dosave0(1);
+ exit(1);
+}
+#endif /* NOSAVEONHANGUP */
+
+/* returns 1 if save successful */
+static bool
+dosave0(int hu)
+{
+ int fd, ofd;
+ int tmp; /* not ! */
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ if ((fd = creat(SAVEF, FMASK)) < 0) {
+ if (!hu)
+ pline("Cannot open save file. (Continue or Quit)");
+ 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++) {
+ if (tmp == dlevel || !level_exists[tmp])
+ continue;
+ glo(tmp);
+ if ((ofd = open(lock, O_RDONLY)) < 0) {
+ if (!hu)
+ pline("Error while saving: cannot read %s.", lock);
+ close(fd);
+ unlink(SAVEF);
+ if (!hu)
+ done("tricked");
+ return (0);
+ }
+ getlev(ofd, hackpid, tmp);
+ close(ofd);
+ bwrite(fd, (char *)&tmp, sizeof(tmp)); /* level number */
+ savelev(fd, tmp); /* actual level */
+ unlink(lock);
+ }
+ close(fd);
+ glo(dlevel);
+ unlink(lock); /* get rid of current level --jgm */
+ glo(0);
+ unlink(lock);
+ return (1);
+}
+
+bool
+dorecover(int fd)
+{
+ int nfd;
+ int tmp; /* not a ! */
+ unsigned mid; /* idem */
+ struct obj *otmp;
+
+ 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 != (int)getuid()) { /* strange ... */
+ close(fd);
+ 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);
+ for (;;) {
+ 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);
+ close(nfd);
+ }
+ lseek(fd, (off_t)0, SEEK_SET);
+ getlev(fd, 0, 0);
+ close(fd);
+ 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) {
+ 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(int fd)
+{
+ struct obj *otmp, *otmp2;
+ struct obj *first = NULL;
+ int xl;
+
+ /* suppress "used before set" warning from lint */
+ otmp2 = NULL;
+ for (;;) {
+ 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(int fd)
+{
+ struct monst *mtmp, *mtmp2;
+ struct monst *first = NULL;
+ int xl;
+ struct permonst *monbegin;
+ long differ;
+
+ mread(fd, (char *)&monbegin, sizeof(monbegin));
+ differ = (char *)(&mons[0]) - (char *)(monbegin);
+
+ /* suppress "used before set" warning from lint */
+ mtmp2 = NULL;
+ for (;;) {
+ 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/hack/hack.search.c b/hack/hack.search.c
new file mode 100644
index 0000000..b1dd291
--- /dev/null
+++ b/hack/hack.search.c
@@ -0,0 +1,147 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.search.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.search.c,v 1.3 1999/11/16 02:57:11 billf Exp $ */
+/* $DragonFly: src/games/hack/hack.search.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+int
+findit(void) /* returns number of things found */
+{
+ int num;
+ xchar zx, zy;
+ struct trap *ttmp;
+ 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)) != NULL) {
+ if (ttmp->ttyp == PIERC) {
+ 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)) != NULL)
+ if (mtmp->mimic) {
+ seemimic(mtmp);
+ num++;
+ }
+ }
+ return (num);
+}
+
+int
+dosearch(void)
+{
+ xchar x, y;
+ struct trap *trap;
+ 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);
+ makemon(PM_PIERCER, x, y);
+ return (1);
+ }
+ trap->tseen = 1;
+ if (!vism_at(x, y))
+ atl(x, y, '^');
+ }
+ }
+ }
+ return (1);
+}
+
+int
+doidtrap(void)
+{
+ struct trap *trap;
+ 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);
+}
+
+void
+wakeup(struct monst *mtmp)
+{
+ mtmp->msleep = 0;
+ setmangry(mtmp);
+ if (mtmp->mimic)
+ seemimic(mtmp);
+}
+
+/* NOTE: we must check if (mtmp->mimic) before calling this routine */
+void
+seemimic(struct monst *mtmp)
+{
+ mtmp->mimic = 0;
+ mtmp->mappearance = 0;
+ unpmon(mtmp);
+ pmon(mtmp);
+}
diff --git a/hack/hack.sh b/hack/hack.sh
new file mode 100644
index 0000000..8136ec4
--- /dev/null
+++ b/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/hack/hack.shk.c b/hack/hack.shk.c
new file mode 100644
index 0000000..eea0558
--- /dev/null
+++ b/hack/hack.shk.c
@@ -0,0 +1,1085 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.shk.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.shk.c,v 1.5 1999/11/16 10:26:37 marcel Exp $ */
+
+#include "hack.h"
+#ifdef QUEST
+int shlevel = 0;
+struct monst *shopkeeper = NULL;
+struct obj *billobjs = NULL;
+
+void
+obfree(struct obj *obj, struct obj *merge)
+{
+ free(obj);
+}
+
+int
+inshop(void)
+{
+ return (0);
+}
+
+void
+shopdig(void)
+{
+}
+
+void
+addtobill(void)
+{
+}
+
+void
+subfrombill(void)
+{
+}
+
+void
+splitbill(void)
+{
+}
+
+int
+dopay(void)
+{
+ return (0);
+}
+
+void
+paybill(void)
+{
+}
+
+int
+doinvbill(void)
+{
+ return (0);
+}
+
+void
+shkdead(void)
+{
+}
+
+int
+shkcatch(void)
+{
+ return (0);
+}
+
+int
+shk_move(void)
+{
+ return (0);
+}
+
+void
+replshk(struct monst *mtmp, struct monst *mtmp2)
+{
+}
+
+const char *
+shkname(void)
+{
+ return ("");
+}
+
+#else /* QUEST */
+#include "hack.mfndpos.h"
+#include "def.eshk.h"
+
+#define ESHK(mon) ((struct eshk *)(&(mon->mextra[0])))
+#define NOTANGRY(mon) mon->mpeaceful
+#define ANGRY(mon) !NOTANGRY(mon)
+
+/* 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 = NULL;
+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 const char *shopnam[] = {
+ "engagement ring", "walking cane", "antique weapon",
+ "delicatessen", "second hand book", "liquor",
+ "used armor", "assorted antiques"
+};
+
+static void setpaid(void);
+static void addupbill(void);
+static void findshk(int);
+static struct bill_x *onbill(struct obj *);
+static void pay(long, struct monst *);
+static int dopayobj(struct bill_x *);
+static struct obj *bp_to_obj(struct bill_x *);
+static int getprice(struct obj *);
+static int realhunger(void);
+
+char *
+shkname(struct monst *mtmp) /* called in do_name.c */
+{
+ return (ESHK(mtmp)->shknam);
+}
+
+void
+shkdead(struct monst *mtmp) /* called in mon.c */
+{
+ struct eshk *eshk = ESHK(mtmp);
+
+ if (eshk->shoplevel == dlevel)
+ rooms[eshk->shoproom].rtype = 0;
+ if (mtmp == shopkeeper) {
+ setpaid();
+ shopkeeper = NULL;
+ bill = (struct bill_x *) - 1000; /* dump core when referenced */
+ }
+}
+
+void
+replshk(struct monst *mtmp, struct monst *mtmp2)
+{
+ if (mtmp == shopkeeper) {
+ shopkeeper = mtmp2;
+ bill = &(ESHK(shopkeeper)->bill[0]);
+ }
+}
+
+static void
+setpaid(void) /* caller has checked that shopkeeper exists */
+ /* either we paid or left the shop or he just died */
+{
+ struct obj *obj;
+ 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) != NULL) {
+ billobjs = obj->nobj;
+ free(obj);
+ }
+ ESHK(shopkeeper)->billct = 0;
+}
+
+static void
+addupbill(void) /* delivers result in total */
+ /* caller has checked that shopkeeper exists */
+{
+ int ct = ESHK(shopkeeper)->billct;
+ struct bill_x *bp = bill;
+
+ total = 0;
+ while (ct--) {
+ total += bp->price * bp->bquan;
+ bp++;
+ }
+}
+
+int
+inshop(void)
+{
+ int roomno = inroom(u.ux, u.uy);
+
+ /* 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 = NULL;
+ shlevel = 0;
+ }
+ u.uinshop = 0;
+ }
+
+ /* Did we just enter a zoo of some kind? */
+ if (roomno >= 0) {
+ int rt = rooms[roomno].rtype;
+ 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;
+ 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(int roomno)
+{
+ 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 = NULL;
+ shlevel = 0;
+ bill = (struct bill_x *) - 1000; /* dump core when referenced */
+}
+
+static struct bill_x *
+onbill(struct obj *obj)
+{
+ 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 */
+void
+obfree(struct obj *obj, struct obj *merge)
+{
+ struct bill_x *bp = onbill(obj);
+ 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(obj);
+}
+
+static void
+pay(long tmp, 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;
+ }
+}
+
+int
+dopay(void)
+{
+ long ltmp;
+ struct bill_x *bp;
+ struct monst *shkp;
+ int pass, tmp;
+
+ multi = 0;
+ 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 int
+dopayobj(struct bill_x *bp)
+{
+ 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) {
+ 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(obj);
+ }
+ return (1);
+}
+
+/* routine called after dying (or quitting) with nonempty bill */
+void
+paybill(void)
+{
+ 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 */
+static struct obj *
+bp_to_obj(struct bill_x *bp)
+{
+ struct obj *obj;
+ struct monst *mtmp;
+ 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)) != NULL)
+ break;
+ for (mtmp = fallen_down; mtmp; mtmp = mtmp->nmon)
+ if ((obj = o_on(id, mtmp->minvent)) != NULL)
+ break;
+ }
+ return (obj);
+}
+
+/* called in hack.c when we pickup an object */
+void
+addtobill(struct obj *obj)
+{
+ 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;
+}
+
+void
+splitbill(struct obj *obj, struct obj *otmp)
+{
+ /* otmp has been split off from obj */
+ struct bill_x *bp;
+ 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;
+
+ 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++;
+ }
+}
+
+void
+subfrombill(struct obj *obj)
+{
+ long ltmp;
+ int tmp;
+ struct obj *otmp;
+ 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)) != NULL) {
+ 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) || strchr("_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));
+}
+
+int
+doinvbill(int mode) /* 0: deliver count 1: paged */
+{
+ struct bill_x *bp;
+ struct obj *obj;
+ long totused, thisused;
+ char buf[BUFSZ];
+
+ if (mode == 0) {
+ 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) {
+ 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 */
+ sprintf(buf, "x - %s", doname(obj));
+ obj->quan = oquan; /* restore value */
+ for (cnt = 0; buf[cnt]; cnt++)
+ ; /* nothing */
+ while (cnt < 50)
+ buf[cnt++] = ' ';
+ sprintf(&buf[cnt], " %5ld zorkmids", thisused);
+ if (page_line(buf))
+ goto quit;
+ }
+ }
+ 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 int
+getprice(struct obj *obj)
+{
+ int tmp, ac;
+
+ 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 int
+realhunger(void) /* not completely foolproof */
+{
+ int tmp = u.uhunger;
+ 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);
+}
+
+bool
+shkcatch(struct obj *obj)
+{
+ 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
+ */
+int
+shk_move(struct monst *shkp)
+{
+ struct monst *mtmp;
+ struct permonst *mdat = shkp->data;
+ xchar gx, gy, omx, omy, nx, ny, nix, niy;
+ schar appr, i;
+ int udist;
+ int z;
+ schar shkroom, chi, chcnt, cnt;
+ boolean uondoor = 0, satdoor, avoid = 0, badinv;
+ coord poss[9];
+ int info[9];
+ struct obj *ib = NULL;
+
+ omx = shkp->mx;
+ omy = shkp->my;
+
+ if ((udist = dist(omx, omy)) < 3) {
+ if (ANGRY(shkp)) {
+ 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 */
+ 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) {
+ 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. */
+void
+shopdig(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) {
+ 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 */
+
+bool
+online(int x, int 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? */
+bool
+follower(struct monst *mtmp)
+{
+ return (mtmp->mtame || strchr("1TVWZi&, ", mtmp->data->mlet)
+#ifndef QUEST
+ || (mtmp->isshk && ESHK(mtmp)->following)
+#endif /* QUEST */
+ );
+}
diff --git a/hack/hack.shknam.c b/hack/hack.shknam.c
new file mode 100644
index 0000000..2f6052a
--- /dev/null
+++ b/hack/hack.shknam.c
@@ -0,0 +1,149 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.shknam.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.shknam.c,v 1.3 1999/11/16 02:57:11 billf Exp $ */
+/* $DragonFly: src/games/hack/hack.shknam.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+static const 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
+};
+
+static const 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
+};
+
+static const 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
+};
+
+static const 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
+};
+
+static const 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
+};
+
+static const 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
+};
+
+static const 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
+};
+
+static const 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;
+ const 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 }
+};
+
+void
+findname(char *nampt, char let)
+{
+ struct shk_nx *p = shk_nx;
+ const char **q;
+ 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
+ strcpy(nampt, "Dirk");
+ return;
+ }
+ strncpy(nampt, q[i], PL_NSIZ);
+ nampt[PL_NSIZ - 1] = 0;
+}
diff --git a/hack/hack.steal.c b/hack/hack.steal.c
new file mode 100644
index 0000000..84111bb
--- /dev/null
+++ b/hack/hack.steal.c
@@ -0,0 +1,210 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.steal.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.steal.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.steal.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+static void stealarm(void);
+
+/* actually returns something that fits in an int */
+long
+somegold(void)
+{
+ return ((u.ugold < 100) ? u.ugold :
+ (u.ugold > 10000) ? rnd(10000) : rnd((int)u.ugold));
+}
+
+void
+stealgold(struct monst *mtmp)
+{
+ struct gold *gold = g_at(u.ux, u.uy);
+ 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 */
+
+static void
+stealarm(void)
+{
+ struct monst *mtmp;
+ 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 */
+bool
+steal(struct monst *mtmp)
+{
+ struct obj *otmp;
+ int tmp;
+ int 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(NULL, 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++;
+ armoroff(otmp);
+ otmp->cursed = curssv;
+ if (multi < 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(NULL);
+ if (otmp->olet == CHAIN_SYM)
+ impossible("How come you are carrying that chain?");
+ if (Punished && otmp == uball) {
+ Punished = 0;
+ freeobj(uchain);
+ free(uchain);
+ uchain = NULL;
+ uball->spe = 0;
+ uball = NULL; /* superfluous */
+ }
+ freeinv(otmp);
+ pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp));
+ mpickobj(mtmp, otmp);
+ return ((multi < 0) ? 0 : 1);
+}
+
+void
+mpickobj(struct monst *mtmp, struct obj *otmp)
+{
+ otmp->nobj = mtmp->minvent;
+ mtmp->minvent = otmp;
+}
+
+bool
+stealamulet(struct monst *mtmp)
+{
+ struct obj *otmp;
+
+ for (otmp = invent; otmp; otmp = otmp->nobj) {
+ if (otmp->olet == AMULET_SYM) {
+ /* might be an imitation one */
+ if (otmp == uwep)
+ setuwep(NULL);
+ 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 */
+void
+relobj(struct monst *mtmp, int show)
+{
+ 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 = NULL;
+ if (mtmp->mgold || mtmp->data->mlet == 'L') {
+ 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/hack/hack.termcap.c b/hack/hack.termcap.c
new file mode 100644
index 0000000..e2b8679
--- /dev/null
+++ b/hack/hack.termcap.c
@@ -0,0 +1,268 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.termcap.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.termcap.c,v 1.10 1999/11/16 10:26:38 marcel Exp $ */
+
+#include <termcap.h>
+#include "hack.h"
+
+static char tbuf[512];
+static char *HO, *CL, *CE, *tcUP, *CM, *ND, *XD, *tcBC, *SO, *SE, *TI, *TE;
+static char *VS, *VE;
+static int SG;
+static char tcPC = '\0';
+char *CD; /* tested in pri.c: docorner() */
+int CO, LI; /* used in pri.c and whatis.c */
+
+static void nocmov(int, int);
+static void cmov(int, int);
+static int xputc(int);
+static void xputs(char *); /* 20150209 bkw: change return type int => void */
+
+void
+startup(void)
+{
+ char *term;
+ char *tptr;
+ char *tbufptr, *pc;
+
+ tptr = alloc(1024);
+
+ tbufptr = tbuf;
+ if (!(term = getenv("TERM")))
+ error("Can't get TERM.");
+ if (tgetent(tptr, term) < 1)
+ error("Unknown terminal type: %s.", term);
+ if (tgetflag(__DECONST(char *, "NP")) ||
+ tgetflag(__DECONST(char *, "nx")))
+ flags.nonull = 1;
+ if ((pc = tgetstr(__DECONST(char *, "pc"), &tbufptr)))
+ tcPC = *pc;
+ if (!(tcBC = tgetstr(__DECONST(char *, "bc"), &tbufptr))
+ && !(tcBC = tgetstr(__DECONST(char *, "le"), &tbufptr))) {
+ if (!tgetflag(__DECONST(char *, "bs")))
+ error("Terminal must backspace.");
+ tcBC = tbufptr;
+ tbufptr += 2;
+ *tcBC = '\b';
+ }
+ HO = tgetstr(__DECONST(char *, "ho"), &tbufptr);
+ CO = tgetnum(__DECONST(char *, "co"));
+ LI = tgetnum(__DECONST(char *, "li"));
+ if (CO < COLNO || LI < ROWNO + 2)
+ setclipped();
+ if (!(CL = tgetstr(__DECONST(char *, "cl"), &tbufptr)))
+ error("Hack needs CL.");
+ ND = tgetstr(__DECONST(char *, "nd"), &tbufptr);
+ if (tgetflag(__DECONST(char *, "os")))
+ error("Hack can't have OS.");
+ CE = tgetstr(__DECONST(char *, "ce"), &tbufptr);
+ tcUP = tgetstr(__DECONST(char *, "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(__DECONST(char *, "xd"), &tbufptr);
+/* not: XD = tgetstr("do", &tbufptr); */
+ if (!(CM = tgetstr(__DECONST(char *, "cm"), &tbufptr))) {
+ if (!tcUP && !HO)
+ error("Hack needs CM or UP or HO.");
+ printf("Playing hack on terminals without cm is suspect...\n");
+ getret();
+ }
+ SO = tgetstr(__DECONST(char *, "so"), &tbufptr);
+ SE = tgetstr(__DECONST(char *, "se"), &tbufptr);
+ SG = tgetnum(__DECONST(char *, "sg"));
+ if (!SO || !SE || (SG > 0)) SO = SE = NULL;
+ CD = tgetstr(__DECONST(char *, "cd"), &tbufptr);
+ set_whole_screen(); /* uses LI and CD */
+ if (tbufptr - tbuf > (int)sizeof(tbuf)) error(
+ "TERMCAP entry too big...\n");
+ free(tptr);
+}
+
+void
+start_screen(void)
+{
+ xputs(TI);
+ xputs(VS);
+}
+
+void
+end_screen(void)
+{
+ xputs(VE);
+ xputs(TE);
+}
+
+/* not xchar: perhaps xchar is unsigned and curx-x would be unsigned as well */
+void
+curs(int x, int y)
+{
+ 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))) {
+ putchar('\r');
+ curx = 1;
+ nocmov(x, y);
+ } else if (!CM)
+ nocmov(x, y);
+ else
+ cmov(x, y);
+}
+
+static void
+nocmov(int x, int y)
+{
+ if (cury > y) {
+ if (tcUP)
+ while (cury > y) { /* Go up. */
+ xputs(tcUP);
+ 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(tcBC);
+ curx--;
+ }
+ }
+}
+
+static void
+cmov(int x, int y)
+{
+ xputs(tgoto(CM, x - 1, y - 1));
+ cury = y;
+ curx = x;
+}
+
+static int
+xputc(int c)
+{
+ return (fputc(c, stdout));
+}
+
+static void /* 20150209 bkw: change return type int => void */
+xputs(char *s)
+{
+ return (tputs(s, 1, xputc));
+}
+
+void
+cl_end(void)
+{
+ 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 */
+ int cx = curx, cy = cury;
+
+ while (curx < COLNO) {
+ xputc(' ');
+ curx++;
+ }
+ curs(cx, cy);
+ }
+}
+
+void
+clear_screen(void)
+{
+ xputs(CL);
+ curx = cury = 1;
+}
+
+void
+home(void)
+{
+ if (HO)
+ xputs(HO);
+ else if (CM)
+ xputs(tgoto(CM, 0, 0));
+ else
+ curs(1, 1); /* using tcUP ... */
+ curx = cury = 1;
+}
+
+void
+standoutbeg(void)
+{
+ if (SO)
+ xputs(SO);
+}
+
+void
+standoutend(void)
+{
+ if (SE)
+ xputs(SE);
+}
+
+void
+backsp(void)
+{
+ xputs(tcBC);
+ curx--;
+}
+
+void
+bell(void)
+{
+ putchar('\007'); /* curx does not change */
+ fflush(stdout);
+}
+
+void
+cl_eos(void) /* free after Robert Viduya */
+{ /* must only be called with curx = 1 */
+ if (CD)
+ xputs(CD);
+ else {
+ int cx = curx, cy = cury;
+ while (cury <= LI - 2) {
+ cl_end();
+ xputc('\n');
+ curx = 1;
+ cury++;
+ }
+ cl_end();
+ curs(cx, cy);
+ }
+}
diff --git a/hack/hack.timeout.c b/hack/hack.timeout.c
new file mode 100644
index 0000000..005e000
--- /dev/null
+++ b/hack/hack.timeout.c
@@ -0,0 +1,72 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.timeout.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.timeout.c,v 1.3 1999/11/16 02:57:12 billf Exp $ */
+
+#include "hack.h"
+
+static void stoned_dialogue(void);
+
+void
+p_timeout(void)
+{
+ 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 */
+static const 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 */
+};
+
+static void
+stoned_dialogue(void)
+{
+ long i = (Stoned & TIMEOUT);
+
+ if (i > 0 && i <= SIZE(stoned_texts))
+ pline("%s", stoned_texts[SIZE(stoned_texts) - i]);
+ if (i == 5)
+ Fast = 0;
+ if (i == 3)
+ nomul(-3);
+}
diff --git a/hack/hack.topl.c b/hack/hack.topl.c
new file mode 100644
index 0000000..75d612a
--- /dev/null
+++ b/hack/hack.topl.c
@@ -0,0 +1,236 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.topl.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.topl.c,v 1.3 1999/11/16 02:57:12 billf Exp $ */
+
+#include "hack.h"
+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 */
+
+static void redotoplin(void);
+static void xmore(const char *);
+
+int
+doredotopl(void)
+{
+ 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)
+ strcpy(toplines, last_redone_topl->topl_text);
+ redotoplin();
+ return (0);
+}
+
+static void
+redotoplin(void)
+{
+ home();
+ if (strchr(toplines, '\n'))
+ cl_end();
+ putstr(toplines);
+ cl_end();
+ tlx = curx;
+ tly = cury;
+ flags.toplin = 1;
+ if (tly > 1)
+ more();
+}
+
+void
+remember_topl(void)
+{
+ struct topl *tl;
+ 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 = NULL;
+ tl = alloc((unsigned)(strlen(toplines) + sizeof(struct topl) + 1));
+ tl->next_topl = old_toplines;
+ tl->topl_text = (char *)(tl + 1);
+ strcpy(tl->topl_text, toplines);
+ old_toplines = tl;
+ while (cnt && tl) {
+ cnt--;
+ tl = tl->next_topl;
+ }
+ if (tl && tl->next_topl) {
+ free(tl->next_topl);
+ tl->next_topl = NULL;
+ }
+}
+
+void
+addtopl(const char *s)
+{
+ curs(tlx, tly);
+ if (tlx + (int)strlen(s) > CO)
+ putsym('\n');
+ putstr(s);
+ tlx = curx;
+ tly = cury;
+ flags.toplin = 1;
+}
+
+static void
+xmore(const 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;
+}
+
+void
+more(void)
+{
+ xmore("");
+}
+
+void
+cmore(const char *s)
+{
+ xmore(s);
+}
+
+void
+clrlin(void)
+{
+ if (flags.toplin) {
+ home();
+ cl_end();
+ if (tly > 1)
+ docorner(1, tly - 1);
+ remember_topl();
+ }
+ flags.toplin = 0;
+}
+
+void
+pline(const char *line, ...)
+{
+ va_list ap;
+ va_start(ap, line);
+ vpline(line, ap);
+ va_end(ap);
+}
+
+/*VARARGS1*/
+void
+vpline(const char *line, va_list ap)
+{
+ char pbuf[BUFSZ];
+ char *bp = pbuf, *tl;
+ int n, n0;
+
+ if (!line || !*line)
+ return;
+ if (!strchr(line, '%'))
+ strcpy(pbuf, line);
+ else
+ vsprintf(pbuf, line, ap);
+ 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 + (int)strlen(toplines) + 3 < CO - 8 && /* room for --More-- */
+ strncmp(bp, "You ", 4)) {
+ strcat(toplines, " ");
+ 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;
+ }
+ 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])
+ strcat(tl, "\n");
+ }
+ redotoplin();
+}
+
+void
+putsym(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++;
+ }
+ putchar(c);
+}
+
+void
+putstr(const char *s)
+{
+ while (*s)
+ putsym(*s++);
+}
diff --git a/hack/hack.track.c b/hack/hack.track.c
new file mode 100644
index 0000000..76a398a
--- /dev/null
+++ b/hack/hack.track.c
@@ -0,0 +1,49 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.track.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.track.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.track.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+#define UTSZ 50
+
+coord utrack[UTSZ];
+int utcnt = 0;
+int utpnt = 0;
+
+void
+initrack(void)
+{
+ utcnt = utpnt = 0;
+}
+
+/* add to track */
+void
+settrack(void)
+{
+ if (utcnt < UTSZ)
+ utcnt++;
+ if (utpnt == UTSZ)
+ utpnt = 0;
+ utrack[utpnt].x = u.ux;
+ utrack[utpnt].y = u.uy;
+ utpnt++;
+}
+
+coord *
+gettrack(int x, int y)
+{
+ int i, cnt, dst;
+ coord tc;
+
+ cnt = utcnt;
+ for (i = utpnt - 1; cnt--; i--) {
+ if (i == -1)
+ i = UTSZ - 1;
+ tc = utrack[i];
+ dst = (x - tc.x) * (x - tc.x) + (y - tc.y) * (y - tc.y);
+ if (dst < 3)
+ return (dst ? &(utrack[i]) : 0);
+ }
+ return (0);
+}
diff --git a/hack/hack.trap.c b/hack/hack.trap.c
new file mode 100644
index 0000000..cb41c67
--- /dev/null
+++ b/hack/hack.trap.c
@@ -0,0 +1,488 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.trap.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.trap.c,v 1.5 1999/11/16 10:26:38 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.trap.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+char vowels[] = "aeiou";
+
+const char *traps[] = {
+ " bear trap",
+ "n arrow trap",
+ " dart trap",
+ " trapdoor",
+ " teleportation trap",
+ " pit",
+ " sleeping gas trap",
+ " piercer",
+ " mimic"
+};
+
+static void vtele(void);
+static void teleds(int, int);
+static bool teleok(int, int);
+
+struct trap *
+maketrap(int x, int y, int typ)
+{
+ 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);
+}
+
+void
+dotrap(struct trap *trap)
+{
+ 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
+ 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 {
+ 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);
+ }
+ }
+}
+
+int
+mintrap(struct monst *mtmp)
+{
+ struct trap *trap = t_at(mtmp->mx, mtmp->my);
+ int wasintrap = mtmp->mtrapped;
+
+ if (!trap)
+ mtmp->mtrapped = 0; /* perhaps teleported? */
+ else if (wasintrap) {
+ if (!rn2(40))
+ mtmp->mtrapped = 0;
+ } else {
+ int tt = trap->ttyp;
+ int in_sight = cansee(mtmp->mx, mtmp->my);
+
+ 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 (strchr(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 (!strchr("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);
+}
+
+void
+selftouch(const 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");
+ }
+}
+
+void
+float_up(void)
+{
+ 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!");
+}
+
+void
+float_down(void)
+{
+ struct trap *trap;
+
+ pline("You float gently to the ground.");
+ if ((trap = t_at(u.ux, u.uy)) != NULL)
+ switch (trap->ttyp) {
+ case PIERC:
+ break;
+ case TRAPDOOR:
+ if (!xdnstair || u.ustuck)
+ break;
+ /* fall into next case */
+ default:
+ dotrap(trap);
+ }
+ pickup(1);
+}
+
+static void
+vtele(void)
+{
+ struct mkroom *croom;
+
+ for (croom = &rooms[0]; croom->hx >= 0; croom++)
+ if (croom->rtype == VAULT) {
+ int 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();
+}
+
+void
+tele(void)
+{
+ coord cc;
+ 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);
+}
+
+static void
+teleds(int nux, int 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();
+ inshop();
+ pickup(1);
+ if (!Blind)
+ read_engr_at(u.ux, u.uy);
+}
+
+static bool
+teleok(int x, int 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) */
+}
+
+int
+dotele(void)
+{
+ 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);
+}
+
+void
+placebc(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;
+ }
+ }
+}
+
+void
+unplacebc(void)
+{
+ if (!carried(uball)) {
+ freeobj(uball);
+ unpobj(uball);
+ }
+ freeobj(uchain);
+ unpobj(uchain);
+}
+
+void
+level_tele(void)
+{
+ 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 */
+}
+
+void
+drown(void)
+{
+ pline("You fall into a pool!");
+ pline("You can't swim!");
+ if (rn2(3) < u.uluck + 2) {
+ /* most scrolls become unreadable */
+ 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 */
+ dotele();
+ if (levl[u.ux][u.uy].typ != POOL)
+ return;
+ }
+ pline("You drown ...");
+ killer = "pool of water";
+ done("drowned");
+}
diff --git a/hack/hack.tty.c b/hack/hack.tty.c
new file mode 100644
index 0000000..37f12bf
--- /dev/null
+++ b/hack/hack.tty.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * 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. 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.
+ *
+ * @(#)hack.tty.c 8.1 (Berkeley) 5/31/93
+ * $FreeBSD: src/games/hack/hack.tty.c,v 1.6.2.1 2000/07/20 10:35:07 kris Exp $
+ * $DragonFly: src/games/hack/hack.tty.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $
+ */
+
+/* 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 <termios.h>
+#include "hack.h"
+
+/*
+ * Some systems may have getchar() return EOF for various reasons, and
+ * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
+ * FIXME: is this still valid nowadays?
+ */
+#define NR_OF_EOFS 20
+
+static char erase_char, kill_char;
+static boolean settty_needed = FALSE;
+struct termios inittyb, curttyb;
+
+static void setctty(void);
+
+/*
+ * 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
+ */
+void
+gettty(void)
+{
+ if (tcgetattr(fileno(stdin), &inittyb) < 0)
+ perror("Hack (gettty)");
+ curttyb = inittyb;
+ erase_char = inittyb.c_cc[VERASE];
+ kill_char = inittyb.c_cc[VKILL];
+ getioctls();
+
+ /* do not expand tabs - they might be needed inside a cm sequence */
+ if (curttyb.c_oflag & OXTABS) {
+ curttyb.c_oflag &= ~OXTABS;
+ setctty();
+ }
+ settty_needed = TRUE;
+}
+
+/* reset terminal to original state */
+void
+settty(const char *s)
+{
+ clear_screen();
+ end_screen();
+ if (s)
+ printf("%s", s);
+ fflush(stdout);
+ if (tcsetattr(fileno(stdin), TCSANOW, &inittyb) < 0)
+ perror("Hack (settty)");
+ flags.echo = (inittyb.c_lflag & ECHO) ? ON : OFF;
+ flags.cbreak = (inittyb.c_lflag & ICANON) ? OFF : ON;
+ setioctls();
+}
+
+static void
+setctty(void)
+{
+ if (tcsetattr(fileno(stdin), TCSANOW, &curttyb) < 0)
+ perror("Hack (setctty)");
+}
+
+void
+setftty(void)
+{
+ u_long ef = 0; /* desired value of flags & ECHO */
+ u_long cf = !(ICANON); /* desired value of flags & CBREAK */
+ int change = 0;
+
+ flags.cbreak = ON;
+ flags.echo = OFF;
+ /* Should use (ECHO|CRMOD) here instead of ECHO */
+ if ((curttyb.c_lflag & ECHO) != ef) {
+ curttyb.c_lflag &= ~ECHO;
+ change++;
+ }
+ if ((curttyb.c_lflag & ICANON) != cf) {
+ curttyb.c_lflag &= ~ICANON;
+ curttyb.c_lflag |= cf;
+ /* be satisfied with one character; no timeout */
+ curttyb.c_cc[VMIN] = 1; /* was VEOF */
+ curttyb.c_cc[VTIME] = 0; /* was VEOL */
+ change++;
+ }
+ if (change)
+ setctty();
+ start_screen();
+}
+
+/* fatal error */
+/* VARARGS1 */
+void
+error(const char *s, ...)
+{
+ va_list ap;
+
+ if (settty_needed)
+ settty(NULL);
+ va_start(ap, s);
+ vprintf(s, ap);
+ va_end(ap);
+ 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".
+ */
+void
+getlin(char *bufp)
+{
+ char *obufp = bufp;
+ int c;
+
+ flags.toplin = 2; /* nonempty, no --More-- required */
+ for (;;) {
+ 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();
+ }
+}
+
+void
+getret(void)
+{
+ cgetret("");
+}
+
+void
+cgetret(const 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 */
+
+void
+xwaitforspace(const char *s) /* chars allowed besides space or return */
+{
+ int c;
+
+ morc = 0;
+ while ((c = readchar()) != '\n') {
+ if (flags.cbreak) {
+ if (c == ' ')
+ break;
+ if (s && strchr(s, c)) {
+ morc = c;
+ break;
+ }
+ bell();
+ }
+ }
+}
+
+char *
+parse(void)
+{
+ static char inputline[COLNO];
+ int 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(void)
+{
+ int sym;
+
+ 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.
+ */
+ 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);
+}
+
+void
+end_of_input(void)
+{
+ settty("End of input?\n");
+ clearlocks();
+ exit(0);
+}
diff --git a/hack/hack.u_init.c b/hack/hack.u_init.c
new file mode 100644
index 0000000..4bb25cd
--- /dev/null
+++ b/hack/hack.u_init.c
@@ -0,0 +1,385 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.u_init.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.u_init.c,v 1.4 1999/11/16 02:57:13 billf Exp $ */
+/* $DragonFly: src/games/hack/hack.u_init.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+#define Strcpy (void) strcpy
+#define Strcat (void) strcat
+#define UNDEF_TYP 0
+#define UNDEF_SPE '\177'
+
+struct you zerou;
+char pl_character[PL_CSIZ];
+/*
+ * must all have distinct first letter
+ * roles[4] may be changed to -man
+ */
+char roles[][12 + 1] = {
+ "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 }
+};
+
+static void ini_inv(struct trobj *);
+#ifdef WIZARD
+static void wiz_inv(void);
+#endif
+static int role_index(char);
+
+void
+u_init(void)
+{
+ int i;
+ char exper = 'y', pc;
+
+ if (flags.female) /* should have been set in HACKOPTIONS */
+ strlcpy(roles[4], "Cave-woman", sizeof(roles[4]));
+ for (i = 0; i < NR_OF_ROLES; i++)
+ rolesyms[i] = roles[i][0];
+ rolesyms[i] = 0;
+
+ if ((pc = pl_character[0]) != '\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 (!strchr("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 (strchr("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()) != '\0') {
+ if ('a' <= pc && pc <= 'z')
+ pc += 'A' - 'a';
+ if ((i = role_index(pc)) >= 0) {
+ printf("%c\n", pc); /* echo */
+ 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 */
+ putchar('\n');
+ fflush(stdout);
+ }
+ if (exper)
+ roles[i][0] = pc;
+
+got_suffix:
+
+ 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)) {
+ int d1 = rn2(7) - 2; /* biased variation */
+ u.ustr += d1;
+ u.ustrmax += d1;
+ }
+
+#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++;
+}
+
+static void
+ini_inv(struct trobj *trop)
+{
+ struct obj *obj;
+
+ 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) /* make a similar object */
+ continue;
+#else
+ if (trop->trquan) { /* check if zero first */
+ --trop->trquan;
+ if (trop->trquan)
+ continue; /* make a similar object */
+ }
+#endif /* PYRAMID_BUG */
+ trop++;
+ }
+}
+
+#ifdef WIZARD
+static void
+wiz_inv(void)
+{
+ struct trobj *trop = &Extra_objs[0];
+ char *ep = getenv("INVENT");
+ int type;
+
+ while (ep && *ep) {
+ type = atoi(ep);
+ ep = strchr(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 */
+
+void
+plnamesuffix(void)
+{
+ char *p;
+
+ if ((p = strrchr(plname, '-')) != NULL) {
+ *p = 0;
+ pl_character[0] = p[1];
+ pl_character[1] = 0;
+ if (!plname[0]) {
+ askname();
+ plnamesuffix();
+ }
+ }
+}
+
+static int
+role_index(char pc)
+{ /* must be called only from u_init() */
+ /* so that rolesyms[] is defined */
+ char *cp;
+
+ if ((cp = strchr(rolesyms, pc)) != NULL)
+ return (cp - rolesyms);
+ return (-1);
+}
diff --git a/hack/hack.unix.c b/hack/hack.unix.c
new file mode 100644
index 0000000..abb0981
--- /dev/null
+++ b/hack/hack.unix.c
@@ -0,0 +1,421 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.unix.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.unix.c,v 1.8 1999/11/16 02:57:13 billf Exp $ */
+
+/* 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 <errno.h>
+#include "hack.h"
+
+#include <sys/types.h> /* for time_t and stat */
+#include <sys/stat.h>
+#include <time.h>
+
+static struct tm *getlt(void);
+static bool veryold(int);
+#ifdef MAIL
+static void newmail(void);
+static void mdrush(struct monst *, bool);
+#endif
+
+void
+setrandom(void)
+{
+ srandomdev();
+}
+
+static struct tm *
+getlt(void)
+{
+ time_t date;
+
+ time(&date);
+ return (localtime(&date));
+}
+
+int
+getyear(void)
+{
+ return (1900 + getlt()->tm_year);
+}
+
+char *
+getdate(void)
+{
+ static char datestr[7];
+ struct tm *lt = getlt();
+
+ snprintf(datestr, sizeof(datestr), "%02d%02d%02d",
+ lt->tm_year % 100, lt->tm_mon + 1, lt->tm_mday);
+ return (datestr);
+}
+
+int
+phase_of_the_moon(void) /* 0-7, with 0: new, 4: full */
+{ /* moon period: 29.5306 days */
+ /* year: 365.2422 days */
+ struct tm *lt = getlt();
+ 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);
+}
+
+bool
+night(void)
+{
+ int hour = getlt()->tm_hour;
+
+ return (hour < 6 || hour > 21);
+}
+
+bool
+midnight(void)
+{
+ return (getlt()->tm_hour == 0);
+}
+
+struct stat buf, hbuf;
+
+void
+gethdate(const char *name)
+{
+/* old version - for people short of space */
+ char *np;
+
+ /* 20150213 bkw: try /usr/games/hack if no /usr/games/hide/hack */
+ name = "/usr/games/hide/hack";
+ if (stat(name, &hbuf)) name = "/usr/games/hack";
+ if (stat(name, &hbuf))
+ error("Cannot get status of %s.",
+ (np = strrchr(name, '/')) ? np + 1 : name);
+}
+
+bool
+uptodate(int 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 */
+static bool
+veryold(int fd)
+{
+ int i;
+ time_t date;
+
+ if (fstat(fd, &buf)) /* cannot get status */
+ return (0);
+ if (buf.st_size != sizeof(int)) /* not an xlock file */
+ return (0);
+ time(&date);
+ if (date - buf.st_mtime < 3L * 24L * 60L * 60L) { /* recent */
+ 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);
+ }
+ close(fd);
+ for (i = 1; i <= MAXLEVEL; i++) { /* try to remove all */
+ glo(i);
+ unlink(lock);
+ }
+ glo(0);
+ if (unlink(lock)) /* cannot remove it */
+ return (0);
+ return (1); /* success! */
+}
+
+void
+getlock(void)
+{
+ int i = 0, fd;
+
+ fflush(stdout);
+
+ /* we ignore QUIT and INT at this point */
+ if (link(HLOCK, LLOCK) == -1) {
+ 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("%s", "");
+ /* NOTREACHED */
+ }
+
+ regularize(lock);
+ glo(0);
+ if (locknum > 25)
+ locknum = 25;
+
+ do {
+ if (locknum)
+ lock[0] = 'a' + i++;
+
+ if ((fd = open(lock, O_RDONLY)) == -1) {
+ if (errno == ENOENT) /* no such file */
+ goto gotlock;
+ perror(lock);
+ unlink(LLOCK);
+ error("Cannot open %s", lock);
+ }
+
+ if (veryold(fd)) /* if true, this closes fd and unlinks lock */
+ goto gotlock;
+ close(fd);
+ } while (i < locknum);
+
+ 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;
+
+void
+getmailstatus(void)
+{
+ if (!(mailbox = getenv("MAIL")))
+ return;
+ if (stat(mailbox, &omstat)) {
+#ifdef PERMANENT_MAILBOX
+ pline("Cannot get status of MAIL=%s .", mailbox);
+ mailbox = NULL;
+#else
+ omstat.st_mtime = 0;
+#endif /* PERMANENT_MAILBOX */
+ }
+}
+
+void
+ckmailstatus(void)
+{
+ 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 = NULL;
+#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 ... */
+ }
+}
+
+static void
+newmail(void)
+{
+ /* produce a scroll of mail */
+ struct obj *obj;
+ struct monst *md;
+ 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);
+ identify(obj); /* set known and do prinv() */
+}
+
+/* make md run through the cave */
+static void
+mdrush(struct monst *md, bool away)
+{
+ int uroom = inroom(u.ux, u.uy);
+
+ if (uroom >= 0) {
+ int tmp = rooms[uroom].fdoor;
+ int cnt = rooms[uroom].doorct;
+ 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) {
+ 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);
+}
+
+void
+readmail(void)
+{
+#ifdef DEF_MAILREADER /* This implies that UNIX is defined */
+ char *mr = NULL;
+
+ more();
+ if (!(mr = getenv("MAILREADER")))
+ mr = DEF_MAILREADER;
+ if (child(1)) {
+ execl(mr, mr, NULL);
+ exit(1);
+ }
+#else /* DEF_MAILREADER */
+ 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 */
+
+void
+regularize(char *s) /* normalize file name - we don't like ..'s or /'s */
+{
+ char *lp;
+
+ while ((lp = strchr(s, '.')) || (lp = strchr(s, '/')))
+ *lp = '_';
+}
diff --git a/hack/hack.vault.c b/hack/hack.vault.c
new file mode 100644
index 0000000..a02d024
--- /dev/null
+++ b/hack/hack.vault.c
@@ -0,0 +1,310 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.vault.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.vault.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */
+
+#include "hack.h"
+#ifdef QUEST
+void
+setgd(void)
+{
+}
+
+int
+gd_move(void)
+{
+ return (2);
+}
+
+void
+gddead(struct monst *mtmp __attribute__((unused)))
+{
+}
+
+void
+replgd(struct monst *mtmp __attribute__((unused)), struct monst *mtmp2 __unused)
+{
+}
+
+void
+invault(void)
+{
+}
+
+#else
+
+#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 void restfakecorr(void);
+static bool goldincorridor(void);
+
+static void
+restfakecorr(void)
+{
+ int fcx, fcy, fcbeg;
+ 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 = NULL;
+}
+
+static bool
+goldincorridor(void)
+{
+ 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);
+}
+
+void
+setgd(void)
+{
+ struct monst *mtmp;
+
+ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
+ if (mtmp->isgd) {
+ guard = mtmp;
+ gdlevel = dlevel;
+ return;
+ }
+ guard = NULL;
+}
+
+void
+invault(void)
+{
+ int 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];
+ int 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) {
+ 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 = NULL;
+ 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 = NULL;
+ 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;
+ }
+}
+
+int
+gd_move(void)
+{
+ int x, y, dx, dy, gx, gy, nx, ny, typ;
+ struct fakecorridor *fcp;
+ 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) {
+ 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);
+}
+
+void
+gddead(void)
+{
+ guard = NULL;
+}
+
+void
+replgd(struct monst *mtmp, struct monst *mtmp2)
+{
+ if (mtmp == guard)
+ guard = mtmp2;
+}
+
+#endif /* QUEST */
diff --git a/hack/hack.version.c b/hack/hack.version.c
new file mode 100644
index 0000000..84e93af
--- /dev/null
+++ b/hack/hack.version.c
@@ -0,0 +1,20 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.version.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.version.c,v 1.3 1999/08/27 23:29:05 peter Exp $ */
+/* $DragonFly: src/games/hack/hack.version.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "date.h"
+#include "hack.h"
+
+int
+doversion(void)
+{
+ pline("%s 1.0.3 - last edit %s.", (
+#ifdef QUEST
+ "Quest"
+#else
+ "Hack"
+#endif /* QUEST */
+ ), datestring);
+ return (0);
+}
diff --git a/hack/hack.wield.c b/hack/hack.wield.c
new file mode 100644
index 0000000..1cf5ea7
--- /dev/null
+++ b/hack/hack.wield.c
@@ -0,0 +1,111 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.wield.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.wield.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.wield.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+extern struct obj zeroobj;
+
+void
+setuwep(struct obj *obj)
+{
+ setworn(obj, W_WEP);
+}
+
+int
+dowield(void)
+{
+ struct obj *wep;
+ 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(NULL);
+ 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);
+}
+
+void
+corrode_weapon(void)
+{
+ 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--;
+ }
+}
+
+bool
+chwepon(struct obj *otmp, int amount)
+{
+ const char *color = (amount < 0) ? "black" : "green";
+ const char *ltime;
+
+ 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;
+ ltime = (amount * amount == 1) ? "moment" : "while";
+ pline("Your %s %s for a %s.",
+ aobjnam(uwep, "glow"), color, ltime);
+ uwep->spe += amount;
+ if (amount > 0)
+ uwep->cursed = 0;
+ return (1);
+}
diff --git a/hack/hack.wizard.c b/hack/hack.wizard.c
new file mode 100644
index 0000000..a99db3f
--- /dev/null
+++ b/hack/hack.wizard.c
@@ -0,0 +1,198 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.wizard.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.wizard.c,v 1.3 1999/11/16 02:57:14 billf Exp $ */
+/* $DragonFly: src/games/hack/hack.wizard.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+/* wizard code - inspired by rogue code from Merlyn Leroy (digi-g!brian) */
+
+#include "hack.h"
+extern struct permonst pm_wizard;
+
+#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";
+
+static void aggravate(void);
+static void clonewiz(struct monst *);
+
+/* If he has found the Amulet, make the wizard appear after some time */
+void
+amulet(void)
+{
+ struct obj *otmp;
+ 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;
+ }
+}
+
+bool
+wiz_hit(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)) {
+ 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))
+ return (0);
+ } else
+ inrange(mtmp); /* try magic */
+ return (0);
+}
+
+void
+inrange(struct monst *mtmp)
+{
+ 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) */
+ makemon(NULL, 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);
+ }
+}
+
+static void
+aggravate(void)
+{
+ struct monst *mtmp;
+
+ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+ mtmp->msleep = 0;
+ if (mtmp->mfroz && !rn2(5))
+ mtmp->mfroz = 0;
+ }
+}
+
+static void
+clonewiz(struct monst *mtmp)
+{
+ struct monst *mtmp2;
+
+ if ((mtmp2 = makemon(PM_WIZARD, mtmp->mx, mtmp->my)) != NULL) {
+ flags.no_of_wizards = 2;
+ unpmon(mtmp2);
+ mtmp2->mappearance = wizapp[rn2(sizeof(wizapp) - 1)];
+ pmon(mtmp);
+ }
+}
diff --git a/hack/hack.worm.c b/hack/hack.worm.c
new file mode 100644
index 0000000..3a8d360
--- /dev/null
+++ b/hack/hack.worm.c
@@ -0,0 +1,223 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.worm.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.worm.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */
+
+#include "hack.h"
+#ifndef NOWORM
+
+struct wseg *wsegs[32]; /* linked list, tail first */
+struct wseg *wheads[32];
+long wgrowtime[32];
+
+static void remseg(struct wseg *);
+
+bool
+getwn(struct monst *mtmp)
+{
+ int 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 */
+void
+initworm(struct monst *mtmp)
+{
+ struct wseg *wtmp;
+ int tmp = mtmp->wormno;
+
+ if (!tmp)
+ return;
+ wheads[tmp] = wsegs[tmp] = wtmp = newseg();
+ wgrowtime[tmp] = 0;
+ wtmp->wx = mtmp->mx;
+ wtmp->wy = mtmp->my;
+ wtmp->nseg = 0;
+}
+
+void
+worm_move(struct monst *mtmp)
+{
+ struct wseg *wtmp, *whd;
+ int tmp = mtmp->wormno;
+
+ wtmp = newseg();
+ wtmp->wx = mtmp->mx;
+ wtmp->wy = mtmp->my;
+ wtmp->nseg = 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);
+}
+
+void
+worm_nomove(struct monst *mtmp)
+{
+ int tmp;
+ struct wseg *wtmp;
+
+ tmp = mtmp->wormno;
+ wtmp = wsegs[tmp];
+ if (wtmp == wheads[tmp])
+ return;
+ if (wtmp == NULL || wtmp->nseg == 0)
+ panic("worm_nomove?");
+ wsegs[tmp] = wtmp->nseg;
+ remseg(wtmp);
+ mtmp->mhp -= 3; /* mhpmax not changed ! */
+}
+
+void
+wormdead(struct monst *mtmp)
+{
+ int tmp = mtmp->wormno;
+ struct wseg *wtmp, *wtmp2;
+
+ if (!tmp)
+ return;
+ mtmp->wormno = 0;
+ for (wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2) {
+ wtmp2 = wtmp->nseg;
+ remseg(wtmp);
+ }
+ wsegs[tmp] = NULL;
+}
+
+void
+wormhit(struct monst *mtmp)
+{
+ int tmp = mtmp->wormno;
+ struct wseg *wtmp;
+
+ if (!tmp) /* worm without tail */
+ return;
+ for (wtmp = wsegs[tmp]; wtmp; wtmp = wtmp->nseg)
+ hitu(mtmp, 1);
+}
+
+void
+wormsee(unsigned int tmp)
+{
+ 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;
+ }
+}
+
+void
+pwseg(struct wseg *wtmp)
+{
+ if (!wtmp->wdispl) {
+ atl(wtmp->wx, wtmp->wy, '~');
+ wtmp->wdispl = 1;
+ }
+}
+
+/* weptyp: uwep->otyp or 0 */
+void
+cutworm(struct monst *mtmp, xchar x, xchar y, uchar weptyp)
+{
+ struct wseg *wtmp, *wtmp2;
+ struct monst *mtmp2;
+ int tmp, tmp2;
+
+ if (mtmp->mx == x && mtmp->my == y) /* hit headon */
+ return;
+
+ /* 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");
+}
+
+static void
+remseg(struct wseg *wtmp)
+{
+ if (wtmp->wdispl)
+ newsym(wtmp->wx, wtmp->wy);
+ free(wtmp);
+}
+#endif /* NOWORM */
diff --git a/hack/hack.worn.c b/hack/hack.worn.c
new file mode 100644
index 0000000..a754aee
--- /dev/null
+++ b/hack/hack.worn.c
@@ -0,0 +1,70 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.worn.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/hack.worn.c,v 1.3 1999/11/16 02:57:14 billf Exp $ */
+
+#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 }
+};
+
+void
+setworn(struct obj *obj, long mask)
+{
+ struct worn *wp;
+ 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 */
+void
+setnotworn(struct obj *obj)
+{
+ struct worn *wp;
+
+ for (wp = worn; wp->w_mask; wp++)
+ if (obj == *(wp->w_obj)) {
+ *(wp->w_obj) = NULL;
+ obj->owornmask &= ~wp->w_mask;
+ }
+ if (uarm2 && !uarm) {
+ uarm = uarm2;
+ uarm2 = 0;
+ uarm->owornmask ^= (W_ARM | W_ARM2);
+ }
+}
diff --git a/hack/hack.zap.c b/hack/hack.zap.c
new file mode 100644
index 0000000..61eb963
--- /dev/null
+++ b/hack/hack.zap.c
@@ -0,0 +1,688 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* hack.zap.c - version 1.0.3 */
+/* $FreeBSD: src/games/hack/hack.zap.c,v 1.4 1999/11/16 10:26:38 marcel Exp $ */
+/* $DragonFly: src/games/hack/hack.zap.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+extern struct monst youmonst;
+
+static const char *fl[]= {
+ "magic missile",
+ "bolt of fire",
+ "sleep ray",
+ "bolt of cold",
+ "death ray"
+};
+
+static void bhitm(struct monst *, struct obj *);
+static bool bhito(struct obj *, struct obj *);
+static char dirlet(int, int);
+static int zhit(struct monst *, int);
+static bool revive(struct obj *);
+static void rloco(struct obj *);
+static void burn_scrolls(void);
+
+/* Routines for IMMEDIATE wands. */
+/* bhitm: monster mtmp was hit by the effect of wand otmp */
+static void
+bhitm(struct monst *mtmp, struct obj *otmp)
+{
+ wakeup(mtmp);
+ switch (otmp->otyp) {
+ case WAN_STRIKING:
+ if (u.uswallow || rnd(20) < 10 + mtmp->data->ac) {
+ 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 (strchr(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);
+ }
+}
+
+/*
+ * object obj was hit by the effect of wand otmp
+ * returns TRUE if sth was done
+ */
+static bool
+bhito(struct obj *obj, struct obj *otmp)
+{
+ 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);
+}
+
+int
+dozap(void)
+{
+ 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) {
+ struct obj *otmp = o_at(u.ux, u.uy);
+ if (otmp)
+ bhito(otmp, obj);
+ }
+ } else
+ 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:
+ {
+ int cnt = 1;
+ if (!rn2(23))
+ cnt += rn2(7) + 1;
+ while (cnt--)
+ makemon(NULL, u.ux, u.uy);
+ }
+ break;
+ case WAN_WISHING:
+ {
+ char buf[BUFSZ];
+ struct obj *otmp;
+ 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.
+ */
+ {
+ struct rm *room;
+ int digdepth;
+ if (u.uswallow) {
+ 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);
+}
+
+const char *
+exclam(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) ? "." : "!");
+}
+
+void
+hit(const char *str, struct monst *mtmp, const char *force)
+{
+ /* 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);
+}
+
+void
+miss(const char *str, 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() */
+
+/* ddx, ddy, range: direction and range
+ * sym: symbol displayed on path
+ * fhitm, fhito: fns called when mon/obj hit
+ * obj: 2nd arg to fhitm/fhito
+ */
+struct monst *
+bhit(int ddx, int ddy, int range, char sym,
+ void (*fhitm)(struct monst *, struct obj *),
+ bool (*fhito)(struct obj *, struct obj *), struct obj *obj)
+{
+ struct monst *mtmp;
+ struct obj *otmp;
+ int typ;
+
+ bhitpos.x = u.ux;
+ bhitpos.y = u.uy;
+
+ if (sym) /* open call */
+ tmp_at(-1, sym);
+ 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(int dx, int dy)
+{
+ int i, ct;
+ struct monst *mtmp;
+ char sym = ')';
+
+ 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)) != NULL) {
+ 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 */
+ 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);
+}
+
+static char
+dirlet(int dx, int 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 */
+void
+buzz(int type, xchar sx, xchar sy, int dx, int dy)
+{
+ int abstype = abs(type);
+ const char *fltxt = (type == -1) ? "blaze of fire" : fl[abstype];
+ struct rm *lev;
+ xchar range;
+ struct monst *mon;
+
+ if (u.uswallow) {
+ 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) {
+ 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) {
+ 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);
+}
+
+static int
+zhit(struct monst *mon, int type) /* returns damage to mon */
+{
+ int tmp = 0;
+
+ switch (type) {
+ case 0: /* magic missile */
+ tmp = d(2, 6);
+ break;
+ case -1: /* Dragon blazing fire */
+ case 1: /* fire */
+ if (strchr("Dg", mon->data->mlet))
+ break;
+ tmp = d(6, 6);
+ if (strchr("YF", mon->data->mlet))
+ tmp += 7;
+ break;
+ case 2: /* sleep*/
+ mon->mfroz = 1;
+ break;
+ case 3: /* cold */
+ if (strchr("YFgf", mon->data->mlet))
+ break;
+ tmp = d(6, 6);
+ if (mon->data->mlet == 'D')
+ tmp += 7;
+ break;
+ case 4: /* death*/
+ if (strchr(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))
+static bool
+revive(struct obj *obj)
+{
+ struct monst *mtmp = NULL;
+
+ 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 */
+}
+
+static void
+rloco(struct obj *obj)
+{
+ int 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);
+}
+
+/* fractured by pick-axe or wand of striking */
+/* no texts here! */
+void
+fracture_rock(struct obj *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);
+}
+
+static void
+burn_scrolls(void)
+{
+ struct obj *obj, *obj2;
+ 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/hack/help b/hack/help
new file mode 100644
index 0000000..24b22a5
--- /dev/null
+++ b/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/hack/hh b/hack/hh
new file mode 100644
index 0000000..d777102
--- /dev/null
+++ b/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 invent list the inventory (all objects you are carrying)
+I invent list selected parts of the inventory
+ IU: list unpaid objects
+ IX: list unpaid but used up items
+ I$: count your money
+p pay pay your bill
+q drink quaff a potion
+r read read a scroll
+s search search for secret doors, hidden traps and monsters
+t throw throw or shoot a weapon
+w wield wield a weapon (w- wield nothing)
+z zap zap a wand
+C name name an individual monster (e.g., baptize your dog)
+D Drop drop several things
+E Engrave write a message in the dust on the floor (E- use fingers)
+P wear put on a ring
+R remove remove a ring
+T remove take off some armor
+W wear put on some armor
+< up go up the stairs
+> down go down the stairs
+^ trap_id identify a previously found trap
+),[,= ask for current weapon, armor, rings, respectively
+$ gold count your gold
+. rest wait a moment
+, pickup pick up all you can carry
+: look look at what is here
diff --git a/hack/makedefs.c b/hack/makedefs.c
new file mode 100644
index 0000000..7b66485
--- /dev/null
+++ b/hack/makedefs.c
@@ -0,0 +1,266 @@
+/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/* makedefs.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/makedefs.c,v 1.4 1999/11/16 02:57:15 billf Exp $ */
+/* $DragonFly: src/games/hack/makedefs.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* construct definitions of object constants */
+#define LINSZ 1000
+#define STRSZ 40
+
+int fd;
+char string[STRSZ];
+
+static void readline(void);
+static char nextchar(void);
+static bool skipuntil(const char *);
+static bool getentry(void);
+static void capitalize(char *);
+static bool letter(char);
+static bool digit(char);
+
+int
+main(int argc, char **argv)
+{
+ int idx = 0;
+ int propct = 0;
+ char *sp;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: makedefs file\n");
+ exit(1);
+ }
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ perror(argv[1]);
+ exit(1);
+ }
+ skipuntil("objects[] = {");
+ while (getentry()) {
+ if (!*string) {
+ idx++;
+ continue;
+ }
+ for (sp = string; *sp; sp++)
+ if (*sp == ' ' || *sp == '\t' || *sp == '-')
+ *sp = '_';
+ if (!strncmp(string, "RIN_", 4)) {
+ capitalize(string + 4);
+ printf("#define %s u.uprops[%d].p_flgs\n",
+ string + 4, propct++);
+ }
+ for (sp = string; *sp; sp++)
+ capitalize(sp);
+ /* avoid trouble with stupid C preprocessors */
+ if (!strncmp(string, "WORTHLESS_PIECE_OF_", 19))
+ printf("/* #define %s %d */\n", string, idx);
+ else
+ printf("#define %s %d\n", string, idx);
+ idx++;
+ }
+ printf("\n#define CORPSE DEAD_HUMAN\n");
+ printf("#define LAST_GEM (JADE+1)\n");
+ printf("#define LAST_RING %d\n", propct);
+ printf("#define NROFOBJECTS %d\n", idx - 1);
+ exit(0);
+}
+
+char line[LINSZ], *lp = line, *lp0 = line, *lpe = line;
+int eof;
+
+static void
+readline(void)
+{
+ int n = read(fd, lp0, (line + LINSZ) - lp0);
+
+ if (n < 0) {
+ printf("Input error.\n");
+ exit(1);
+ }
+ if (n == 0)
+ eof++;
+ lpe = lp0 + n;
+}
+
+static char
+nextchar(void)
+{
+ if (lp == lpe) {
+ readline();
+ lp = lp0;
+ }
+ return ((lp == lpe) ? 0 : *lp++);
+}
+
+static bool
+skipuntil(const char *s)
+{
+ const char *sp0;
+ char *sp1;
+loop:
+ while (*s != nextchar())
+ if (eof) {
+ printf("Cannot skipuntil %s\n", s);
+ exit(1);
+ }
+ if ((int)strlen(s) > lpe - lp + 1) {
+ char *lp1, *lp2;
+ lp2 = lp;
+ lp1 = lp = lp0;
+ while (lp2 != lpe)
+ *lp1++ = *lp2++;
+ lp2 = lp0; /* save value */
+ lp0 = lp1;
+ readline();
+ lp0 = lp2;
+ if ((int)strlen(s) > lpe - lp + 1) {
+ printf("error in skipuntil");
+ exit(1);
+ }
+ }
+ sp0 = s + 1;
+ sp1 = lp;
+ while (*sp0 && *sp0 == *sp1)
+ sp0++, sp1++;
+ if (!*sp0) {
+ lp = sp1;
+ return (1);
+ }
+ goto loop;
+}
+
+static bool
+getentry(void)
+{
+ int inbraces = 0, inparens = 0, stringseen = 0, commaseen = 0;
+ int prefix = 0;
+ char ch;
+#define NSZ 10
+ char identif[NSZ], *ip;
+
+ string[0] = string[4] = 0;
+ /* read until {...} or XXX(...) followed by ,
+ * skip comment and #define lines
+ * deliver 0 on failure
+ */
+ for (;;) {
+ ch = nextchar();
+swi:
+ if (letter(ch)) {
+ ip = identif;
+ do {
+ if (ip < identif + NSZ - 1)
+ *ip++ = ch;
+ ch = nextchar();
+ } while (letter(ch) || digit(ch));
+ *ip = 0;
+ while (ch == ' ' || ch == '\t')
+ ch = nextchar();
+ if (ch == '(' && !inparens && !stringseen)
+ if (!strcmp(identif, "WAND") ||
+ !strcmp(identif, "RING") ||
+ !strcmp(identif, "POTION") ||
+ !strcmp(identif, "SCROLL"))
+ strncpy(string, identif, 3),
+ string[3] = '_',
+ prefix = 4;
+ }
+ switch (ch) {
+ case '/':
+ /* watch for comment */
+ if ((ch = nextchar()) == '*')
+ skipuntil("*/");
+ goto swi;
+ case '{':
+ inbraces++;
+ continue;
+ case '(':
+ inparens++;
+ continue;
+ case '}':
+ inbraces--;
+ if (inbraces < 0)
+ return (0);
+ continue;
+ case ')':
+ inparens--;
+ if (inparens < 0) {
+ printf("too many ) ?");
+ exit(1);
+ }
+ continue;
+ case '\n':
+ /* watch for #define at begin of line */
+ if ((ch = nextchar()) == '#') {
+ char pch;
+ /* skip until '\n' not preceded by '\\' */
+ do {
+ pch = ch;
+ ch = nextchar();
+ } while (ch != '\n' || pch == '\\');
+ continue;
+ }
+ goto swi;
+ case ',':
+ if (!inparens && !inbraces) {
+ if (prefix && !string[prefix])
+ string[0] = 0;
+ if (stringseen)
+ return (1);
+ printf("unexpected ,\n");
+ exit(1);
+ }
+ commaseen++;
+ continue;
+ case '\'':
+ if ((ch = nextchar()) == '\\')
+ ch = nextchar();
+ if (nextchar() != '\'') {
+ printf("strange character denotation?\n");
+ exit(1);
+ }
+ continue;
+ case '"':
+ {
+ char *sp = string + prefix;
+ char pch;
+ int store = (inbraces || inparens)
+ && !stringseen++ && !commaseen;
+ do {
+ pch = ch;
+ ch = nextchar();
+ if (store && sp < string + STRSZ)
+ *sp++ = ch;
+ } while (ch != '"' || pch == '\\');
+ if (store)
+ *--sp = 0;
+ continue;
+ }
+ }
+ }
+}
+
+static void
+capitalize(char *sp)
+{
+ if ('a' <= *sp && *sp <= 'z')
+ *sp += 'A' - 'a';
+}
+
+static bool
+letter(char ch)
+{
+ return (('a' <= ch && ch <= 'z') ||
+ ('A' <= ch && ch <= 'Z'));
+}
+
+static bool
+digit(char ch)
+{
+ return ('0' <= ch && ch <= '9');
+}
diff --git a/hack/pathnames.h b/hack/pathnames.h
new file mode 100644
index 0000000..fbb4a97
--- /dev/null
+++ b/hack/pathnames.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * 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. 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 5/31/93
+ */
+
+#define _PATH_MAIL "/usr/bin/mail"
+#define _PATH_QUEST "/var/games/questdir"
+#define _PATH_HACK "/var/games/hackdir"
+
diff --git a/hack/rnd.c b/hack/rnd.c
new file mode 100644
index 0000000..b761dd9
--- /dev/null
+++ b/hack/rnd.c
@@ -0,0 +1,35 @@
+/* rnd.c - version 1.0.2 */
+/* $FreeBSD: src/games/hack/rnd.c,v 1.5 1999/11/16 10:26:38 marcel Exp $ */
+/* $DragonFly: src/games/hack/rnd.c,v 1.3 2006/08/21 19:45:32 pavalos Exp $ */
+
+#include "hack.h"
+
+#define RND(x) (random() % x)
+
+int
+rn1(int x, int y)
+{
+ return (RND(x) + y);
+}
+
+int
+rn2(int x)
+{
+ return (RND(x));
+}
+
+int
+rnd(int x)
+{
+ return (RND(x) + 1);
+}
+
+int
+d(int n, int x)
+{
+ int tmp = n;
+
+ while (n--)
+ tmp += RND(x);
+ return (tmp);
+}
diff --git a/hack/rumors b/hack/rumors
new file mode 100644
index 0000000..9435a5f
--- /dev/null
+++ b/hack/rumors
@@ -0,0 +1,505 @@
+"Quit" is a four letter word.
+"So when I die, the first thing I will see in Heaven is a score list?"
+-- more --
+...and rings may protect your fingers.
+...and sometimes a piercer drops by.
+A Quasit is even faster than a jaguar!
+A chameleon imitating a postman often delivers scrolls of fire.
+A chameleon imitating a postman sometimes delivers scrolls of punishment.
+A clove of garlic a day keeps your best friends away.
+A cockatrice's corpse is guaranteed to be untainted!
+A confused acid blob may attack.
+A dead lizard is a good thing to turn undead.
+A dragon is just a Snake that ate a scroll of fire.
+A fading corridor enlightens your insight.
+A glowing potion is too hot to drink.
+A good amulet may protect you against guards.
+A homunculus wouldnt want to hurt a wizard.
+A jaguar shouldn't frighten you.
+A long worm can be defined recursively. So how should you attack it?
+A long worm hits with all of its length.
+A magic vomit pump is a necessity for gourmands.
+A monstrous mind is a toy for ever.
+A nurse a day keeps the doctor away.
+A potion of blindness makes you see invisible things.
+A ring is just a wound wand.
+A ring of adornment protects against Nymphs.
+A ring of conflict is a bad thing if there is a nurse in the room.
+A ring of extra ringfinger is useless if not enchanted.
+A ring of stealth can be recognised by that it does not teleport you.
+A rope may form a trail in a maze.
+A rumour has it that rumours are just rumours.
+A scroll of enchant amulet is only useful on your way back.
+A smoky potion surely affects your vision.
+A spear might hit a nurse.
+A spear will hit an ettin.
+A staff may recharge if you drop it for awhile.
+A tin of smoked eel is a wonderful find.
+A truly wise man never plays leapfrog with a unicorn.
+A two-handed sword usually misses.
+A unicorn can be tamed only by a fair maiden.
+A visit to the Zoo is very educational; you meet interesting animals.
+A wand of deaf is a more dangerous weapon than a wand of sheep.
+A wand of vibration might bring the whole cave crashing about your ears.
+A winner never quits. A quitter never wins.
+A xan is a small animal. It doesn't reach higher than your leg.
+Acid blobs should be attacked bare-handed.
+Affairs with Nymphs are often very expensive.
+Afraid of Mimics? Try to wear a ring of true seeing.
+Afraid of falling piercers? Wear a helmet!
+After being attacked by a Harpy you have a lot of arrows.
+All monsters are created evil, but some are more evil than others.
+Always attack a floating Eye from behind!
+Always be aware of the phase of the moon!
+Always read the info about a monster before dealing with it.
+Always sweep the floor before engraving important messages.
+Amulets are hard to make. Even for a wand of wishing.
+An Umber hulk can be a confusing sight.
+An elven cloak is always the height of fashion.
+An elven cloak protects against magic.
+An ettin is hard to kill; an imp is hard to hit. See the difference?
+Any small object that is accidentally dropped will hide under a larger object.
+Are you blind? Catch a floating Eye!
+Asking about monsters may be very useful.
+Attack long worms from the rear - that is so much safer!
+Attacking an eel when there is none usually is a fatal mistake!
+Balrogs only appear on the deeper levels.
+Be careful when eating bananas. Monsters might slip on the peels.
+Be careful when eating salmon - your fingers might become greasy.
+Be careful when the moon is in its last quarter.
+Be careful when throwing a boomerang - you might hit the back of your head.
+Be nice to a nurse: put away your weapon and take off your clothes.
+Being digested is a painfully slow process.
+Better go home and hit your kids. They are just little monsters!
+Better go home and play with your kids. They are just little monsters!
+Better leave the dungeon, otherwise you might get hurt badly.
+Beware of dark rooms - they may be the Morgue.
+Beware of death rays!
+Beware of falling rocks, wear a helmet!
+Beware of hungry dogs!
+Beware of the minotaur. He's very horny!
+Beware of the potion of Nitroglycerine - it's not for the weak of heart.
+Beware of wands of instant disaster.
+Beware: there's always a chance that your wand explodes as you try to zap it!
+Beyond the 23-rd level lies a happy retirement in a room of your own.
+Blank scrolls make more interesting reading.
+Blind? Eat a carrot!
+Booksellers never read scrolls; it might carry them too far away.
+Booksellers never read scrolls; it might leave their shop unguarded.
+Changing your suit without dropping your sword? You must be kidding!
+Cockatrices might turn themselves to stone faced with a mirror.
+Consumption of home-made food is strictly forbidden in this dungeon.
+Dark gems are just coloured glass.
+Dark room? Just flash often with your camera.
+Dark room? Your chance to develop your photographs!
+Dark rooms are not *completely* dark: just wait and let your eyes adjust...
+Dead lizards protect against a cockatrice.
+Death is just around the next door.
+Death is life's way of telling you you've been fired.
+Descend in order to meet more decent monsters.
+Did you know worms had teeth?
+Didn't you forget to pay?
+Didn't you forget to pay?
+Direct a direct hit on your direct opponent, directing in the right direction.
+Do something big today: lift a boulder.
+Do you want to visit hell? Dig a *very* deep hole.
+Dogs are attracted by the smell of tripe.
+Dogs do not eat when the moon is full.
+Dogs never step on cursed items.
+Dogs of ghosts aren't angry, just hungry.
+Don't bother about money: only Leprechauns and shopkeepers are interested.
+Don't create fireballs: they might turn against you.
+Don't eat too much: you might start hiccoughing!
+Don't forget! Large dogs are MUCH harder to kill than little dogs.
+Don't play hack at your work, your boss might hit you!
+Don't swim with weapons or armour: they might rust!
+Don't tell a soul you found a secret door, otherwise it isn't secret anymore.
+Don't throw gems. They are so precious! Besides, you might hit a roommate.
+Drinking might affect your health.
+Drop your vanity and get rid of your jewels! Pickpockets about!
+Dungeon expects every monster to do his duty.
+Dust is an armor of poor quality.
+Eat 10 cloves of garlic and keep all humans at a two-square distance.
+Eat a homunculus if you want to avoid sickness.
+Eating a Wraith is a rewarding experience!
+Eating a freezing sphere is like eating a yeti.
+Eating a killer bee is like eating a scorpion.
+Eating a tengu is like eating a Nymph.
+Eating unpaid Leprechauns may be advantageous.
+Eels hide under mud. Use a unicorn to clear the water and make them visible.
+Elven cloaks cannot rust.
+Engrave your wishes with a wand of wishing.
+Eventually all wands of striking do strike.
+Eventually you will come to admire the swift elegance of a retreating nymph.
+Ever fought with an enchanted tooth?
+Ever heard hissing outside? I *knew* you hadn't!
+Ever seen a leocrotta dancing the tengu?
+Ever slept in the arms of a homunculus?
+Ever tamed a shopkeeper?
+Ever tried digging through a Vault Guard?
+Ever tried enchanting a rope?
+Ever tried to catch a flying boomerang?
+Ever tried to put a Troll into a large box?
+Ever wondered why one would want to dip something in a potion?
+Every dog should be a domesticated one.
+Every hand has only one finger to put a ring on. You've got only two hands. So?
+Every level contains a shop; only the entrance is often hidden.
+Everybody should have tasted a scorpion at least once in his life.
+Expensive cameras have penetrating flashlights.
+Feeding the animals is strictly prohibited. The Management.
+Feeling lousy? Why don't you drink a potion of tea?
+Fiery letters might deter monsters.
+First Law of Hacking: leaving is much more difficult than entering.
+For any remedy there is a misery.
+Fourth Law of Hacking: you will find the exit at the entrance.
+Gems are the droppings of other inmates.
+Gems do get a burden.
+Genocide on shopkeepers is punishable.
+Getting Hungry? Stop wearing rings!
+Getting Hungry? Wear an amulet!
+Ghosts always empty the fridge.
+Ghosts are visible because they don't leave a trace.
+Giant beetles make giant holes in giant trees!
+Giving head to a long worm is like a long lasting reception.
+Gold is a heavy metal.
+Good day for overcoming obstacles. Try a steeplechase.
+Gossip is the opiate of the depressed.
+Hackers do it with bugs.
+Half Moon tonight. (At least it's better than no Moon at all.)
+Handle your flasks carefully - there might be a ghost inside!
+Have a good meal today: eat a minotaur.
+Hey guys, you *WIELD* a dead lizard against a cocatrice! [David London]
+Hissing is a sound I hate.
+Hitting is the lingua franca in these regions.
+Humans use walking canes when they grow old.
+Hunger is a confusing experience for a dog!
+Hungry dogs are unreliable.
+Hungry? There is an abundance of food on the next level.
+Hungry? Wear an amulet!
+I doubt whether nurses are virgins.
+I guess you have never hit a postman with an Amulet of Yendor yet...
+I once knew a hacker who ate too fast and choked to death.....
+I smell a maze of twisty little passages.
+I wished, I never wished a wand of wishing. (Wishful thinking)
+If "nothing happens", something *has* happened anyway!!
+If a chameleon mimics a mace, it really mimics a Mimic mimicking a mace.
+If a shopkeeper kicks you out of his shop, he'll kick you out of the dungeon.
+If you are being punished, it's done with a deadly weapon.
+If you are the shopkeeper you can take things for free.
+If you are too cute some monsters might be tempted to embrace you.
+If you can't learn to do it well, learn to enjoy doing it badly.
+If you need a wand of digging, kindly ask the minotaur.
+If you see nurses you better start looking somewhere for a doctor.
+If you turn blind: don't expect your dog to be turned into a seeing-eye dog.
+If you want to feal great, you must eat something real big.
+If you want to float you'd better eat a floating eye.
+If you want to genocide nurses, genocide @'s.
+If you want to hit, use a dagger.
+If you want to rob a shop, train your dog.
+If you're afraid of trapdoors, just cover the floor with all you've got.
+If you're lost, try buying a map next time you're in a shop.
+If your ghost kills a player, it increases your score.
+Important mail? Be careful that it isn't stolen!
+Improve your environment, using a wand of rearrangement.
+In a hurry? Try a ride on a fast moving quasit!
+In a way, a scorpion is like a snake.
+In need of a rest? Quaff a potion of sickness!
+In total, there are eight sorts of shops.
+Increase mindpower: Tame your own ghost!
+Inside a shop you better take a look at the price tags before buying anything.
+It furthers one to see the great man.
+It is bad manners to use a wand in a shop.
+It is not always a good idea to whistle for your dog.
+It is said that Giant Rabbits can be tamed with carrots only.
+It is said that purple worms and trappers fill the same niche.
+It might be a good idea to offer the unicorn a ruby.
+It seems you keep overlooking a sign reading "No trespassing"!
+It would be peculiarly sad were your dog turned to stone.
+It's all a matter of life and death, so beware of the undead.
+It's bad luck to drown a postman.
+It's bad luck, being punished.
+It's easy to overlook a monster in a wood.
+It's not safe to Save.
+Jackals are intrinsically rotten.
+Just below any trapdoor there may be another one. Just keep falling!
+Keep a clear mind: quaff clear potions.
+Keep your armours away from rust.
+Keep your weaponry away from acids.
+Kicking the terminal doesn't hurt the monsters.
+Kill a unicorn and you kill your luck.
+Killer bees keep appearing till you kill their queen.
+Large dogs make larger turds than little ones.
+Latest news? Put 'net.games.hack' in your .newsrc !
+Latest news? Put newsgroup 'netUNX.indoor.hackers-scroll' in your .newsrc!
+Learn how to spell. Play Hack!
+Leather armour cannot rust.
+Leprechauns are the most skilled cutpurses in this dungeon.
+Leprechauns hide their gold in a secret room.
+Let your fingers do the walking on the yulkjhnb keys.
+Let's face it: this time you're not going to win.
+Let's have a party, drink a lot of booze.
+Liquor sellers do not drink; they hate to see you twice.
+Looking for a monster -- use a staff of monster summoning.
+Looking pale? Quaff a red potion!
+M.M.Vault cashiers teleport any amount of gold to the next local branch.
+Many monsters make a murdering mob.
+Meet yourself! Commit suicide and type "hack"
+Meeting your own ghost decreases your luck considerably!
+Memory flaw - core dumped.
+Money is the root of all evil.
+Money to invest? Take it to the local branch of the Magic Memory Vault!
+Monsters come from nowhere to hit you everywhere.
+Monsters sleep because you are boring, not because they ever get tired.
+Most monsters can't swim.
+Most monsters prefer minced meat. That's why they are hitting you!
+Most rumors are just as misleading as this one.
+Much ado Nothing Happens.
+Murder complaint? Mail to 'netnix!devil!gamble!freak!trap!lastwill!rip'.
+Need money? Sell your corpses to a tin factory.
+Never ask a shopkeeper for a price list.
+Never attack a guard.
+Never drop a crysknife! No, never even unwield it, until...
+Never eat with glowing hands!
+Never fight a monster: you might get killed.
+Never go into the dungeon at midnight.
+Never kick a sleeping dog.
+Never kiss an animal. It may cause kissing disease.
+Never map the labyrinth.
+Never mind the monsters hitting you: they just replace the charwomen.
+Never ride a long worm.
+Never step on a cursed engraving.
+Never swim with a camera: there's nothing to take pictures of.
+Never trust a random generator in magic fields.
+Never use a wand of death.
+Never use your best weapon to engrave a curse.
+Never vomit on a door mat.
+No easy fighting with a heavy load!
+No level contains two shops. The maze is no level. So...
+No part of this fortune may be reproduced, stored in a retrieval system, ...
+No weapon is better than a crysknife.
+Not all rumors are as misleading as this one.
+Not even a spear will hit a Xorn.
+Now what is it that cures digestion?
+Nurses are accustomed to touch naked persons: they don't harm them.
+Nurses prefer undressed hackers.
+Nymphs and nurses like beautiful rings.
+Nymphs are blondes. Are you a gentleman?
+Nymphs are very pleased when you call them by their real name: Lorelei.
+Offering a unicorn a worthless piece of glass might prove to be fatal!
+Old hackers never die: young ones do.
+Old trees sometimes fall without a warning!
+Once your little dog will be a big dog, and you will be proud of it.
+One can even choke in a fortune cookie!
+One has to leave shops before closing time.
+One homunculus a day keeps the doctor away.
+One level further down somebody is getting killed, right now.
+One wand of concentration equals eight scrolls of create monster.
+Only Today! A dramatic price-cut on slightly used wands.
+Only a Nymph knows how to unlock chains.
+Only a dragon will never get a cold from a wand of cold.
+Only a real dummy would ever call his sword 'Elbereth'.
+Only a wizard can use a magic whistle.
+Only adventurers of evil alignment think of killing their dog.
+Only cave-women can catch a unicorn. And then only with a golden rope.
+Only chaotic evils kill sleeping monsters.
+Only david can find the zoo!
+Only real trappers escape traps.
+Only real wizards can write scrolls.
+Only wizards are able to zap a wand.
+Opening a tin is difficult, especially when you are not so strong!
+Opening a tin is difficult, especially when you attempt this bare handed!
+Operation coded OVERKILL has started now.
+Orcs and killer bees share their lifestyle.
+Orcs do not procreate in dark rooms.
+PLEASE ignore previous rumour.
+Plain nymphs are harmless.
+Playing billiards pays when you are in a shop.
+Polymorphing your dog probably makes you safer.
+Praying will frighten Demons.
+Punishment is a thing you call over yourself. So why complain?
+Pursue the monsters and you will be had indeed.
+Put on a ring of teleportation: it will take you away from onslaught.
+Rays aren't boomerangs, of course, but still...
+Read the manual before entering the cave - You might get killed otherwise.
+Reading Herbert will disgust you, but in one case it might be enlightening.
+Reading Tolkien might help you.
+Reading might change your vision.
+Reading might improve your scope.
+Relying on a dog might turn you in a dog addict.
+Reward your doggie with a giant Bat.
+Ropes are made from the long, blond hairs of dead Nymphs.
+Row (3x) that boat gently down the stream, Charon (4x), death is but a dream.
+Running is good for your legs.
+Rust monsters love water. There are potions they hate, however.
+Savings do include amnesia.
+Scorpions often hide under tripe rations.
+Screw up your courage! You've screwed up everything else.
+Scrolls of fire are useful against fog clouds.
+Second Law of Hacking: first in, first out.
+Selling and rebuying a wand will recharge it.
+Shopkeepers accept creditcards, as long as you pay cash.
+Shopkeepers are vegetarians: they only eat Swedes.
+Shopkeepers can't read, so what use is engraving in a shop?
+Shopkeepers can't swim.
+Shopkeepers have incredible patience.
+Shopkeepers often have strange names.
+Shopkeepers sometimes die from old age.
+Sleeping may increase your strength.
+Snakes are often found under worthless objects.
+Some Balrogs don't attack if you offer them a ring.
+Some mazes (especially small ones) have no solutions, says man 6 maze.
+Some monsters can be tamed. I once saw a hacker with a tame Dragon!
+Some potions are quite mind-expanding.
+Some questions Sphynxes ask just *don't* have any answers.
+Sometimes "mu" is the answer.
+Sometimes monsters are more likely to fight each other than attack you.
+Sorry, no fortune this time. Better luck next cookie!
+Spare your scrolls of make-edible until it's really necessary!
+Speed Kills (The Doors)
+Spinach, carrot, and a melon - a meal fit for a nurse!
+Stay clear of the level of no return.
+Suddenly the dungeon will collapse ...
+Surprise your dog with an acid blob!
+Tainted meat is even more sickening than poison!
+Take a long worm from the rear, according to its mate it's a lot more fun.
+Tame a troll and it will learn you fighting.
+Taming a postman may cause a system security violation.
+Taming is a gradual process of excercising and rewarding.
+Telepathy is just a trick: once you know how to do it, it's easy.
+Teleportation lessens your orientation.
+The "pray" command is not yet implemented.
+The Jackal only eats bad food.
+The Leprechaun Gold Tru$t is no division of the Magic Memory Vault.
+The Leprechauns hide their treasure in a small hidden room.
+The air is positively magic in here. Better wear a negative armor.
+The best equipment for your work is, of course, the most expensive.
+The emptiness of a ghost is too heavy to bear.
+The key to this game is that there are no keys.
+The longer the wand the better.
+The moon is not the only heavenly body to influence this game.
+The postman always rings twice.
+The proof of the quivering blob is in the eating thereof.
+The secret of wands of Nothing Happens: try again!
+The use of dynamite is dangerous.
+There are better information sources than fortune cookies.
+There are monsters of softening penetration.
+There are monsters of striking charity.
+There have been people like you in here; their ghosts seek revenge on you.
+There is a VIP-lounge on this level. Only first-class travellers admitted.
+There is a big treasure hidden in the zoo!
+There is a message concealed in each fortune cookie.
+There is a trap on this level!
+There is more magic in this cave than meets the eye.
+There is no business like throw business.
+There is no harm in praising a large dog.
+There is nothing like eating a Mimic.
+There seem to be monsters of touching benevolence.
+They say a gelatinous cube can paralyse you...
+They say that Elven cloaks absorb enchantments.
+They say that a dagger hits.
+They say that a dog avoids traps.
+They say that a dog can be trained to fetch objects.
+They say that a dog never steps on a cursed object.
+They say that a spear will hit a Dragon.
+They say that a spear will hit a Xorn.
+They say that a spear will hit a neo-otyugh. (Do YOU know what that is?)
+They say that a spear will hit an ettin.
+They say that a two-handed sword misses.
+They say that a unicorn might bring you luck.
+They say that an elven cloak may be worn over your armor.
+They say that an elven cloak protects against magic.
+They say that cavemen seldom find tins in the dungeon.
+They say that dead lizards protect against a cockatrice.
+They say that killing a shopkeeper brings bad luck.
+They say that monsters never step on a scare monster scroll.
+They say that only david can find the zoo!
+They say that shopkeepers often have a large amount of money in their purse.
+They say that the owner of the dungeon might change it slightly.
+They say that the use of dynamite is dangerous.
+They say that the walls in shops are made of extra hard material.
+They say that there is a big treasure hidden in the zoo!
+They say that there is a message concealed in each fortune cookie.
+They say that there is a trap on this level!
+They say that throwing food at a wild dog might tame him.
+They say that you can meet old friends in the caves.
+They say that you can't take your pick-axe into a shop.
+They say that you cannot trust scrolls of rumour.
+They say that you need a key in order to open locked doors.
+Third Law of Hacking: the last blow counts most.
+This dungeon is restroom equipped (for your convenience).
+This fortune cookie is property of Fortune Cookies, Inc.
+This is not a fortune.
+This is the Leprechaun Law: every purse has a price.
+Throwing food at a wild dog might tame him.
+Tin openers are rare indeed.
+Tired of irritating bats? Try a scroll of silence.
+To hit or not to hit, that is the question.
+To reach heaven, escape the dungeon while wearing a ring of levitation.
+Tranquillizers might get you killed.
+Travel fast, use some magic speed!
+Tripe on its own is revolting, but with onions it's delicious!
+Try hacking in the wee hours: you will have more room.
+Try the fall back end run play against ghosts.
+Ulch, that meat was painted.
+Unwanted mail? Sell it to the bookshop!
+Vampires hate garlic.
+Vault guards always make sure you aren't a shopkeeper.
+Vault guards never disturb their Lords.
+Visitors are requested not to apply genocide to shopkeepers.
+WARNING from H.M. Govt: Quaffing may be dangerous to your health.
+Wanna fly? Eat a bat.
+Want a hint? Zap a wand of make invisible on your weapon!
+Want fun? Throw a potion in a pool and go swimming!
+Want to conserve your dead corpses? Go to the tin factory!
+Wanted: shopkeepers. Send a scroll of mail to: Mage of Yendor/Level 35/Dungeon.
+Warning: end of file 'fortunes' reached.
+Warning: people who eat dragons can go to hell!!
+Watch your steps on staircases.
+Wear armor, going naked seems to offend public decency in here.
+What a pity, you cannot read it!
+What do you think is the use of dead lizards?
+What do you think would be the use of a two handed sword called "Orcrist" ?
+When a piercer drops in on you, you will be tempted to hit the ceiling!
+When in a maze follow the right wall and you will never get lost.
+When in a shop, do as shopkeepers do.
+When punished, watch your steps on the stairs!
+When you have a key, you don't have to wait for the guard.
+When you have seen one killer bee, you have seen them all.
+When your dog follows you through a trap door, don't hit it!
+Where do you think all those demons come from? From Hell, of course.
+Where do you think the hell is located? It must be deep, deep down.
+Who should ever have thought one could live from eating fog clouds?
+Why a "2" for the postman? Well, how many times does he ring?
+Why should one ever throw an egg to a cockatrice?
+Why would anybody in his sane mind engrave "Elbereth" ?
+Wish for a master key and open the Magic Memory Vault!
+Wish for a pass-key and pass all obstacles!
+Wish for a skeleton-key and open all doors!
+Wishing too much may bring you too little.
+Wizards do not sleep.
+You are heading for head-stone for sure.
+You are just the kind of bad food some monsters like to digest.
+You can always wear an elven cloak.
+You can eat what your dog can eat.
+You can get a genuine Amulet of Yendor by doing the following: -- more --
+You can't get rid of a cursed plate mail with a can-opener.
+You can't leave a shop through the back door: there ain't one!
+You cannot ride a long worm.
+You cannot trust scrolls of rumour.
+You die...
+You feel greedy and want more gold? Why don't you try digging?
+You feel like someone is pulling your leg.
+You have to outwit a Sphynx or pay her.
+You may get rich selling letters, but beware of being blackmailed!
+You may have a kick from kicking a little dog.
+You might choke on your food by eating fortune cookies.
+You might cut yourself on a long sword.
+You might trick a shopkeeper if you're invisible.
+You need a key in order to open locked doors.
+You offend Shai-Hulud by sheathing your crysknife without having drawn blood.
+You want to regain strength? Two levels ahead is a guesthouse!
+You'll need a spear if you want to attack a Dragon.
+You've got to know how to put out a yellow light.
+Your dog can buy cheaper than you do.
+Zapping a wand of Nothing Happens doesn't harm you a bit.
+Zapping a wand of undead turning might bring your dog back to life.