aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile70
-rw-r--r--Makefile.inc26
-rw-r--r--README101
-rw-r--r--boggle/Makefile40
-rw-r--r--boggle/README66
-rw-r--r--boggle/boggle/Makefile19
-rw-r--r--boggle/boggle/bog.c714
-rw-r--r--boggle/boggle/bog.h83
-rw-r--r--boggle/boggle/boggle.6127
-rw-r--r--boggle/boggle/extern.h62
-rw-r--r--boggle/boggle/help.c105
-rw-r--r--boggle/boggle/helpfile92
-rw-r--r--boggle/boggle/mach.c665
-rw-r--r--boggle/boggle/prtable.c127
-rw-r--r--boggle/boggle/timer.c119
-rw-r--r--boggle/boggle/word.c215
-rw-r--r--boggle/mkdict/Makefile10
-rw-r--r--boggle/mkdict/mkdict.c124
-rw-r--r--boggle/mkindex/Makefile10
-rw-r--r--boggle/mkindex/mkindex.c131
-rw-r--r--bs/Makefile10
-rw-r--r--bs/bs.676
-rw-r--r--bs/bs.c1196
-rw-r--r--cgram/Makefile11
-rw-r--r--cgram/cgram.665
-rw-r--r--cgram/cgram.c344
-rw-r--r--cgram/pathnames.h30
-rw-r--r--ching/Makefile5
-rw-r--r--ching/Makefile.inc5
-rw-r--r--ching/castching/Makefile7
-rw-r--r--ching/castching/castching.c135
-rw-r--r--ching/ching/Makefile13
-rw-r--r--ching/ching/ching.6154
-rw-r--r--ching/ching/ching.sh81
-rw-r--r--ching/ching/hexagrams2337
-rw-r--r--ching/ching/macros126
-rw-r--r--ching/include/ching.h44
-rw-r--r--ching/printching/Makefile7
-rw-r--r--ching/printching/pathnames.h38
-rw-r--r--ching/printching/printching.c326
-rw-r--r--colorbars/Makefile9
-rw-r--r--colorbars/colorbars.647
-rw-r--r--colorbars/colorbars.c124
-rw-r--r--dab/Makefile14
-rw-r--r--dab/algor.cc310
-rw-r--r--dab/algor.h76
-rw-r--r--dab/board.cc253
-rw-r--r--dab/board.h86
-rw-r--r--dab/box.cc150
-rw-r--r--dab/box.h93
-rw-r--r--dab/dab.6112
-rw-r--r--dab/defs.h43
-rw-r--r--dab/gamescreen.cc43
-rw-r--r--dab/gamescreen.h72
-rw-r--r--dab/human.cc149
-rw-r--r--dab/human.h53
-rw-r--r--dab/main.cc191
-rw-r--r--dab/player.cc91
-rw-r--r--dab/player.h70
-rw-r--r--dab/random.cc79
-rw-r--r--dab/random.h66
-rw-r--r--dab/test.cc50
-rw-r--r--dab/ttyscrn.cc233
-rw-r--r--dab/ttyscrn.h74
-rw-r--r--dm/Makefile18
-rw-r--r--dm/dm.8106
-rw-r--r--dm/dm.c335
-rw-r--r--dm/dm.conf.5114
-rw-r--r--dm/pathnames.h37
-rw-r--r--grdc/Makefile9
-rw-r--r--grdc/grdc.643
-rw-r--r--grdc/grdc.c266
-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
-rw-r--r--hals_end/Makefile9
-rw-r--r--hals_end/hals_end.666
-rw-r--r--hals_end/hals_end.c151
-rw-r--r--include/bsdcompat.h61
-rw-r--r--include/sys/tty.h1
-rw-r--r--larn/COPYRIGHT8
-rw-r--r--larn/Fixed.Bugs218
-rw-r--r--larn/Makefile83
-rw-r--r--larn/OWNER3
-rw-r--r--larn/README150
-rw-r--r--larn/action.c305
-rw-r--r--larn/bill.c163
-rw-r--r--larn/config.c50
-rw-r--r--larn/create.c606
-rw-r--r--larn/data.c664
-rw-r--r--larn/datfiles/larn.help140
-rw-r--r--larn/datfiles/larnmaze288
-rw-r--r--larn/datfiles/larnopts12
-rw-r--r--larn/diag.c411
-rw-r--r--larn/display.c637
-rw-r--r--larn/extern.h226
-rw-r--r--larn/fortune.c94
-rw-r--r--larn/global.c861
-rw-r--r--larn/header.h444
-rw-r--r--larn/help.c129
-rw-r--r--larn/io.c991
-rw-r--r--larn/larn.6157
-rw-r--r--larn/main.c1341
-rw-r--r--larn/monster.c1911
-rw-r--r--larn/moreobj.c296
-rw-r--r--larn/movem.c447
-rw-r--r--larn/nap.c23
-rw-r--r--larn/object.c1336
-rw-r--r--larn/pathnames.h38
-rw-r--r--larn/regen.c187
-rw-r--r--larn/savelev.c67
-rw-r--r--larn/scores.c855
-rw-r--r--larn/signal.c138
-rw-r--r--larn/store.c910
-rw-r--r--larn/tok.c235
-rw-r--r--out404
-rw-r--r--paranoia/Makefile16
-rw-r--r--paranoia/README8
-rw-r--r--paranoia/README.linux10
-rw-r--r--paranoia/paranoia.c1036
-rw-r--r--rogue/CHANGES55
-rw-r--r--rogue/Makefile19
-rw-r--r--rogue/USD.doc/Makefile10
-rw-r--r--rogue/USD.doc/rogue.me834
-rw-r--r--rogue/hit.c466
-rw-r--r--rogue/init.c366
-rw-r--r--rogue/inventory.c841
-rw-r--r--rogue/level.c900
-rw-r--r--rogue/machdep.c493
-rw-r--r--rogue/main.c84
-rw-r--r--rogue/message.c378
-rw-r--r--rogue/monster.c886
-rw-r--r--rogue/move.c649
-rw-r--r--rogue/object.c802
-rw-r--r--rogue/pack.c574
-rw-r--r--rogue/pathnames.h35
-rw-r--r--rogue/play.c299
-rw-r--r--rogue/random.c146
-rw-r--r--rogue/ring.c338
-rw-r--r--rogue/rogue.6107
-rw-r--r--rogue/rogue.h702
-rw-r--r--rogue/room.c671
-rw-r--r--rogue/save.c427
-rw-r--r--rogue/score.c674
-rw-r--r--rogue/spec_hit.c536
-rw-r--r--rogue/throw.c324
-rw-r--r--rogue/trap.c282
-rw-r--r--rogue/use.c625
-rw-r--r--rogue/zap.c407
-rw-r--r--tetris/Makefile13
-rw-r--r--tetris/input.c162
-rw-r--r--tetris/input.h39
-rw-r--r--tetris/pathnames.h37
-rw-r--r--tetris/scores.c960
-rw-r--r--tetris/scores.h89
-rw-r--r--tetris/screen.c430
-rw-r--r--tetris/screen.c.orig424
-rw-r--r--tetris/screen.h54
-rw-r--r--tetris/shapes.c106
-rw-r--r--tetris/tetris.6161
-rw-r--r--tetris/tetris.c338
-rw-r--r--tetris/tetris.h175
248 files changed, 66290 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..75fd54f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,70 @@
+# Makefile for bsd-games-extra, by B. Watson. This file is
+# released under the WTFPL, so do WTF you want with it.
+
+# You don't want to parallelize this. Use "make -j1" if you
+# normally set -jN in your MAKEFLAGS.
+
+# This is just the date, yyyymmdd
+VERSION=20150209
+
+# Slackware-specific stuff. Try MANDIR=/usr/share/man and PMAKE=bmake
+# for other Linux distros.
+MANDIR=/usr/man
+PMAKE=pmake
+
+# This bit of ugliness requires GNU make. I don't know BSD make
+# well enough to write the equivalent for it.
+INC:=$(shell pwd)/include
+CFLAGS="$(OPTFLAGS) -I$(INC) -include bsdcompat.h -std=gnu99"
+
+# Which games are we building?
+DIRS=boggle bs cgram ching colorbars \
+ dab dm grdc hack hals_end larn \
+ paranoia rogue tetris
+
+# BSD Makefiles don't create subdirs for man pages, so we have
+# to do it here.
+MANSECTS=5 6 8
+
+# Some of this stuff installs with wrong permissions,
+# fix_perms rule fixes it according to these 3 variables:
+SCOREFILES=tetris.scores rogue.scores
+SGIDBINS=tetris rogue hack
+SAVEDIRS=hackdir larn
+
+all:
+ for dir in $(DIRS); do ( cd $$dir && $(PMAKE) CFLAGS=$(CFLAGS) ); done
+ [ -x boggle/mkdict/mkdict ] && cd boggle && $(PMAKE) realall
+
+clean:
+ for dir in $(DIRS); do ( cd $$dir && $(PMAKE) clean ); done
+
+install: install_files fix_perms
+
+install_files:
+ for sect in $(MANSECTS); do \
+ mkdir -p $(DESTDIR)/$(MANDIR)/man$$sect ; \
+ done
+ for dir in $(DIRS); do \
+ ( cd $$dir && \
+ $(PMAKE) install \
+ INSTALL="install -D" \
+ MANDIR=$(MANDIR) \
+ DOCDIR=/usr/doc/bsd-games-extra-$(VERSION) \
+ ) ; \
+ done
+ rm -f $(DESTDIR)/mkdict $(DESTDIR)/mkindex
+
+fix_perms:
+ for file in $(SCOREFILES); do \
+ install -D -o root -g games -m664 /dev/null $(DESTDIR)/var/games/$$file ; \
+ done
+ for bin in $(SGIDBINS); do \
+ chown root:games $(DESTDIR)/usr/games/$$bin ; \
+ chmod 2755 $(DESTDIR)/usr/games/$$bin ; \
+ done
+ for dir in $(SAVEDIRS); do \
+ mkdir -p $(DESTDIR)/var/games/$$dir ; \
+ chown -R root:games $(DESTDIR)/var/games/$$dir ; \
+ chmod -R ug+rw $(DESTDIR)/var/games/$$dir ; \
+ done
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..48090e0
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,26 @@
+# $NetBSD: Makefile.inc,v 1.16 2014/03/23 00:17:40 dholland Exp $
+# @(#)Makefile.inc 8.1 (Berkeley) 5/31/93
+
+MKHIDEGAME?= no
+
+.if defined(HIDEGAME) && (${MKHIDEGAME} != no) && defined(PROG)
+BINDIR= /usr/games/hide
+BINGRP= games
+.if defined(SETGIDGAME)
+USE_FORT?= yes
+BINMODE= 2550
+.else
+BINMODE= 550
+.endif
+SYMLINKS+= dm /usr/games/${PROG}
+.else
+BINDIR= /usr/games
+.if defined(SETGIDGAME)
+BINGRP= games
+BINMODE= 2555
+.endif
+.endif
+# Note: do not bother with WARNS=6 until -Wconversion is either
+# removed from WARNS=6 or rendered useful by improving gcc; as it is
+# (with gcc48) it produces buckets of drivel.
+WARNS?= 5
diff --git a/README b/README
new file mode 100644
index 0000000..4b7980a
--- /dev/null
+++ b/README
@@ -0,0 +1,101 @@
+bsd-games-extra - more games ported from *BSD.
+
+This package is meant to have all the games from BSD that are missing
+from Slackware's bsd-games. These are:
+
+boggle
+bs
+cgram
+ching
+colorbars
+dab
+dm
+grdc
+hack
+hals_end
+larn
+paranoia
+rogue
+tetris
+
+boggle, dm, and tetris are included in the Linux bsd-games-2.13 source
+but are not built by Slackware's bsd-games.SlackBuild.
+
+hack, larn, paranoia, and rogue were removed from the Linux port of
+bsd-games for license reasons.
+
+The others were never included in the Linux bsd-games source.
+
+Sources:
+
+bs, grdc, hack from DragonFlyBSD git:
+http://gitweb.dragonflybsd.org/dragonfly.git/tree/v4.0.3:/games
+
+boggle cgram ching colorbars dab dm hals_end larn rogue tetris are
+from NetBSD:
+http://ftp.netbsd.org/pub/NetBSD/NetBSD-current/tar_files/src/games.tar.gz
+
+paranoia isn't actually a BSD game (it came from a magazine), but
+it used to be distributed with bsd-games-1.3. I grabbed a copy here:
+http://web.mit.edu/games/src/bsd-games/paranoia/
+
+Building:
+
+You need GNU make and the Slackware pmake package (which is an old and
+unmaintained port of Berkeley make). Run "make" in the top-level dir,
+which will run pmake for each subdir. "make install" will install
+everything, "make install DESTDIR=/somewhere" is useful for packagers.
+
+If you're not on Slackware, you probably will need MANDIR=/usr/share/man
+on the make command line. Also, if your Berkeley make is called bmake,
+say PMAKE=bmake (good luck).
+
+License:
+
+This README and the top-level Makefile are released under the WTFPL,
+so do WTF you want with them. include/bsdcompat.h is mostly copied from
+either libbsd's headers, or NetBSD source. The games are mostly licensed
+with BSD licenses (big surprise there), but see the games themselves
+for license details (comments in the source, mostly).
+
+Notes:
+
+I made no gameplay changes, and only two cosmetic changes:
+
+- larn was missing the player's @ symbol. In the ancient versions
+ of larn (posted to usenet, ported to DOS, Atari ST, etc), the @
+ was there, like most roguelike games use. The code that printed the @
+ started causing a segfault on UNIX at some point, so someone removed it,
+ and just let the terminal's cursor indicate the player's position. I
+ put the @ back and fixed the segfaulting bug because this is how
+ the game's original author intended it to be.
+
+- tetris was using black as a color, for the borders and for one of
+ the pieces. This is no good in the Linux console, or default
+ settings for Konsole or xfce4-terminal, since those all have black
+ backgrounds. xterm and rxvt still default to white, but I'm assuming
+ anyone old-school enough to use xterm or rxvt for playing Tetris is
+ going to already know how to change the bg color.
+
+I took a different approach than the bsd-games package uses. Instead of
+rewriting the Makefiles from scratch for GNU make, or using autotools,
+I used the BSD Makefiles, with as few modifications as I could get
+away with. You can grep for my initials (bkw) to find my changes to the
+Makefiles and sources.
+
+Functions like strlcpy() are from libbsd, but trying to #include anything
+from /usr/include/bsd just led to breakage, so the neccessary BSD-flavored
+macros and prototypes are in include/bsdcompat.h.
+
+For the most part, I stuck with NetBSD sources, where available. The games
+ported from DragonFlyBSD mostly don't exist in NetBSD (the exception being
+hack; the NetBSD version was giving me fits so I used the DragonFlyBSD
+one instead).
+
+A few of these are installed setgid games. YMMV on whether that's an
+acceptable security risk, but they were designed to work that way.
+
+If you're not familiar with BSD-style nroff documentation, it might
+help to know how to read the rogue manual:
+
+nroff -me /usr/doc/bsd-games-extra-20150209/rogue.me | less -R
diff --git a/boggle/Makefile b/boggle/Makefile
new file mode 100644
index 0000000..4671c08
--- /dev/null
+++ b/boggle/Makefile
@@ -0,0 +1,40 @@
+# $NetBSD: Makefile,v 1.22 2003/10/21 10:01:19 lukem Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/11/93
+
+.include <bsd.own.mk>
+
+SUBDIR= boggle mkdict mkindex
+
+MKDICTDIR!= cd $(.CURDIR)/mkdict; ${PRINTOBJDIR}
+MKDICT=${MKDICTDIR}/mkdict
+MKINDEXDIR!= cd $(.CURDIR)/mkindex; ${PRINTOBJDIR}
+MKINDEX=${MKINDEXDIR}/mkindex
+# 20150209 bkw: look for dictionary words here:
+WORDS=/usr/share/dict/words
+DICTFILES=dictionary dictindex
+.if ${MKSHARE} != "no"
+FILES=${DICTFILES}
+FILESDIR=/usr/share/games/boggle
+.endif
+CLEANFILES+=${DICTFILES}
+
+realall: ${FILES}
+
+${MKDICT}:
+ @cd ${.CURDIR}/mkdict && ${MAKE}
+
+${MKINDEX}:
+ @cd ${.CURDIR}/mkindex && ${MAKE}
+
+dictionary: ${WORDS} ${MKDICT}
+ ${_MKTARGET_CREATE}
+ rm -f ${.TARGET}
+ ./mkdict/${MKDICT} < ${WORDS} > ${.TARGET}
+
+dictindex: dictionary ${MKINDEX}
+ ${_MKTARGET_CREATE}
+ rm -f ${.TARGET}
+ ./mkindex/${MKINDEX} < dictionary > ${.TARGET}
+
+.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
diff --git a/boggle/README b/boggle/README
new file mode 100644
index 0000000..fc38842
--- /dev/null
+++ b/boggle/README
@@ -0,0 +1,66 @@
+$NetBSD: README,v 1.2 1995/03/21 12:14:21 cgd Exp $
+
+Bog is a fairly portable simulation of Parker Brother's game of Boggle and
+is similar to the 4.[23] BSD "boggle" and Sun's "boggletool".
+Bog has not been derived from any proprietary code.
+It has been tested on the Sun 3 under SunOS 3.2 and on the Atari 1040ST (MWC).
+
+What You Need
+
+You will need curses/termcap and a large word list.
+The minix word list or /usr/dict/words will do nicely.
+The word list must already be sorted (you can use "sort -c" to check).
+
+Contents
+
+ README - this file
+ Makefile
+ bog.man - half-hearted man page (use the game's help command)
+ bog.h - configuration and header info
+ bog.c - machine independent game code
+ word.c - machine independent word list routines
+ help.c - (curses) help routine
+ mach.c - (curses) display code
+ prtable.c - ditto
+ timer.c - machine dependent (os) input polling
+ mkdict.c - convert a word list to a bog dictionary
+ mkindex.c - create an index file for the bog dictionary
+ showdict.c - print a bog dictionary to stdout
+
+Portability
+
+- I've tried to make bog.c (the program logic) independent of the I/O.
+ My plan was to make it straightforward to adapt the game to run under a
+ windowing system (eg., Suntools, GEM). I have no plan to actually do this.
+ I've stuck to a small subset of the curses routines.
+- The program runs with the input in raw mode.
+- If you want the running timer you must #define TIMER in bog.h
+ and insert the input polling code in timer.c for your system. There is
+ already code there for BSD, SYSV, and ATARI.
+
+Setup
+
+1. Check bog.h and Makefile and edit to fit your environment
+2. "make all"
+ This will make all the binaries and create the dictionary and index files
+3. Move "dict", "dict.ind", and "helpfile" to where you specified in bog.h
+4. Play away
+
+Distribution
+
+You may use this software for your enjoyment and you may share it with others.
+You may not sell this software or use it for any commercial purposes
+whatsoever. All modified versions of the software that you redistribute must
+clearly indicate your changes.
+
+If you come across any bugs or make any changes you'd like to share please
+send mail to me rather than posting to the net.
+
+Enjoy. [But beware: boggle can be addictive!]
+
+-----
+Barry Brachman | UUCP: {alberta,uw-beaver,uunet}!
+Dept. of Computer Science| ubc-vision!ubc-csgrads!brachman
+Univ. of British Columbia| Internet: brachman@cs.ubc.ca
+Vancouver, B.C. V6T 1W5 | brachman%ubc.csnet@csnet-relay.arpa
+(604) 228-5010 | brachman@ubc.csnet
diff --git a/boggle/boggle/Makefile b/boggle/boggle/Makefile
new file mode 100644
index 0000000..400b438
--- /dev/null
+++ b/boggle/boggle/Makefile
@@ -0,0 +1,19 @@
+# $NetBSD: Makefile,v 1.10 2010/02/06 23:45:25 he Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/11/93
+
+.include <bsd.own.mk>
+
+PROG= boggle
+SRCS= bog.c help.c mach.c prtable.c timer.c word.c
+DPADD= ${LIBCURSES} ${LIBTERMINFO}
+# 20150209 bkw: removed -lterminfo from LDADD, added -lbsd
+LDADD= -lcurses -lbsd
+HIDEGAME=hidegame
+MAN= boggle.6
+.if ${MKSHARE} != "no"
+FILES= helpfile
+FILESDIR=/usr/share/games/boggle
+.endif
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/boggle/boggle/bog.c b/boggle/boggle/bog.c
new file mode 100644
index 0000000..e24d9e2
--- /dev/null
+++ b/boggle/boggle/bog.c
@@ -0,0 +1,714 @@
+/* $NetBSD: bog.c,v 1.29 2014/03/22 23:39:04 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)bog.c 8.2 (Berkeley) 5/4/95";
+#else
+__RCSID("$NetBSD: bog.c,v 1.29 2014/03/22 23:39:04 dholland Exp $");
+#endif
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/tty.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static char *batchword(FILE *);
+static void playgame(void);
+static int validword(const char *);
+static void checkdict(void);
+static void newgame(const char *);
+static int compar(const void *, const void *);
+static void clearwordpath(int full);
+static void usage(void) __dead;
+
+struct dictindex dictindex[26];
+
+/*
+ * Cube position numbering:
+ *
+ * 0 1 2 3
+ * 4 5 6 7
+ * 8 9 A B
+ * C D E F
+ */
+static int adjacency[16][16] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ { 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 0 */
+ { 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 1 */
+ { 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 2 */
+ { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* 3 */
+ { 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, /* 4 */
+ { 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0 }, /* 5 */
+ { 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0 }, /* 6 */
+ { 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, /* 7 */
+ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0 }, /* 8 */
+ { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0 }, /* 9 */
+ { 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 }, /* A */
+ { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1 }, /* B */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, /* C */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0 }, /* D */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1 }, /* E */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 } /* F */
+};
+
+static int letter_map[26][16];
+
+char board[17];
+int wordpath[MAXWORDLEN + 1];
+int wordlen; /* Length of last word returned by nextword() */
+int usedbits;
+
+const char *pword[MAXPWORDS];
+static char pwords[MAXPSPACE], *pwordsp;
+int npwords;
+
+const char *mword[MAXMWORDS];
+static char mwords[MAXMSPACE], *mwordsp;
+int nmwords;
+
+int ngames = 0;
+int tnmwords = 0, tnpwords = 0;
+
+#include <setjmp.h>
+jmp_buf env;
+
+time_t start_t;
+int debug;
+int tlimit;
+
+static FILE *dictfp;
+static int batch;
+static int minlength;
+static int reuse;
+
+int
+main(int argc, char *argv[])
+{
+ long seed;
+ int ch, done, i, selfuse, sflag;
+ char *bspec, *p;
+
+ /* revoke setgid privileges */
+ setgid(getgid());
+
+ seed = 0;
+ batch = debug = reuse = selfuse = sflag = 0;
+ bspec = NULL;
+ minlength = 3;
+ tlimit = 180; /* 3 minutes is standard */
+
+ while ((ch = getopt(argc, argv, "bds:t:w:")) != -1)
+ switch(ch) {
+ case 'b':
+ batch = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 's':
+ sflag = 1;
+ seed = atol(optarg);
+ break;
+ case 't':
+ if ((tlimit = atoi(optarg)) < 1)
+ errx(1, "bad time limit");
+ break;
+ case 'w':
+ if ((minlength = atoi(optarg)) < 3)
+ errx(1, "min word length must be > 2");
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* process final arguments */
+ if (argc > 0) {
+ if (strcmp(argv[0], "+") == 0)
+ reuse = 1;
+ else if (strcmp(argv[0], "++") == 0)
+ selfuse = 1;
+ }
+
+ if (reuse || selfuse) {
+ argc -= 1;
+ argv += 1;
+ }
+
+ if (argc > 0) {
+ if (islower((unsigned char)argv[0][0])) {
+ if (strlen(argv[0]) != 16) {
+ usage();
+ } else {
+ /* This board is assumed to be valid... */
+ bspec = argv[0];
+ }
+ } else {
+ usage();
+ }
+ }
+
+ if (batch && bspec == NULL)
+ errx(1, "must give both -b and a board setup");
+
+ if (selfuse)
+ for (i = 0; i < 16; i++)
+ adjacency[i][i] = 1;
+
+ if (batch) {
+ newgame(bspec);
+ while ((p = batchword(stdin)) != NULL)
+ (void) printf("%s\n", p);
+ exit (0);
+ }
+ setup(sflag, seed);
+ prompt("Loading the dictionary...");
+ if ((dictfp = opendict(DICT)) == NULL) {
+ warn("%s", DICT);
+ cleanup();
+ exit(1);
+ }
+#ifdef LOADDICT
+ if (loaddict(dictfp) < 0) {
+ warnx("can't load %s", DICT);
+ cleanup();
+ exit(1);
+ }
+ (void)fclose(dictfp);
+ dictfp = NULL;
+#endif
+ if (loadindex(DICTINDEX) < 0) {
+ warnx("can't load %s", DICTINDEX);
+ cleanup();
+ exit(1);
+ }
+
+ prompt("Type <space> to begin...");
+ while (inputch() != ' ');
+
+ for (done = 0; !done;) {
+ newgame(bspec);
+ bspec = NULL; /* reset for subsequent games */
+ playgame();
+#ifdef NEW_STYLE
+ prompt("Type <q>uit, <esc> locate, any other key to continue...");
+#else
+ prompt("Type <space> to continue, any cap to quit...");
+#endif
+ delay(50); /* wait for user to quit typing */
+ flushin(stdin);
+ for (;;) {
+ ch = inputch();
+#ifdef NEW_STYLE
+ if (ch == '\e')
+ findword();
+ else if (ch == CTRL('l') || ch == CTRL('r'))
+ redraw();
+ else {
+ if (ch == 'q') {
+ done = 1;
+ break;
+ } else {
+ break;
+ }
+ }
+#else
+ if (ch == '\e')
+ findword();
+ else if (ch == CTRL('l') || ch == CTRL('r'))
+ redraw();
+ else {
+ if (isupper(ch)) {
+ done = 1;
+ break;
+ }
+ if (ch == ' ')
+ break;
+ }
+#endif
+ }
+ }
+ cleanup();
+ exit (0);
+}
+
+/*
+ * Read a line from the given stream and check if it is legal
+ * Return a pointer to a legal word or a null pointer when EOF is reached
+ */
+static char *
+batchword(FILE *fp)
+{
+ char *w;
+
+ clearwordpath(1);
+ while ((w = nextword(fp)) != NULL) {
+ if (wordlen < minlength)
+ continue;
+ clearwordpath(0);
+ usedbits = 0;
+ if (checkword(w, -1, wordpath) != -1)
+ return (w);
+ }
+ return (NULL);
+}
+
+/*
+ * Play a single game
+ * Reset the word lists from last game
+ * Keep track of the running stats
+ */
+static void
+playgame(void)
+{
+ int i;
+ time_t t;
+ char buf[MAXWORDLEN + 1];
+
+ ngames++;
+ npwords = 0;
+ pwordsp = pwords;
+ nmwords = 0;
+ mwordsp = mwords;
+
+ time(&start_t);
+
+ clearwordpath(1);
+ showboard(board);
+ startwords();
+ if (setjmp(env)) {
+ badword();
+ goto timesup;
+ }
+
+ while (1) {
+ if (get_line(buf) == NULL) {
+ if (feof(stdin))
+ clearerr(stdin);
+ break;
+ }
+ time(&t);
+ if (t - start_t >= tlimit) {
+ badword();
+ break;
+ }
+ if (buf[0] == '\0') {
+ int remaining;
+
+ remaining = tlimit - (int) (t - start_t);
+ (void)snprintf(buf, sizeof(buf),
+ "%d:%02d", remaining / 60, remaining % 60);
+ showstr(buf, 1);
+ continue;
+ }
+ if (strlen(buf) < (size_t)minlength) {
+ badword();
+ continue;
+ }
+
+ clearwordpath(0);
+ usedbits = 0;
+
+ if (checkword(buf, -1, wordpath) < 0)
+ badword();
+ else {
+ if (debug) {
+ (void) printf("[");
+ for (i = 0; wordpath[i] != -1; i++)
+ (void) printf(" %d", wordpath[i]);
+ (void) printf(" ]\n");
+ }
+ for (i = 0; i < npwords; i++) {
+ if (strcmp(pword[i], buf) == 0)
+ break;
+ }
+ if (i != npwords) { /* already used the word */
+ badword();
+ showword(i);
+ }
+ else if (!validword(buf))
+ badword();
+ else {
+ size_t len;
+
+ len = strlen(buf) + 1;
+ if (npwords == MAXPWORDS - 1 ||
+ pwordsp + len >= &pwords[MAXPSPACE]) {
+ warnx("Too many words!");
+ cleanup();
+ exit(1);
+ }
+ pword[npwords++] = pwordsp;
+ (void) strcpy(pwordsp, buf);
+ pwordsp += len;
+ addword(buf);
+ }
+ }
+ }
+
+timesup: ;
+
+ /*
+ * Sort the player's words and terminate the list with a null
+ * entry to help out checkdict()
+ */
+ qsort(pword, npwords, sizeof(pword[0]), compar);
+ pword[npwords] = NULL;
+
+ /*
+ * These words don't need to be sorted since the dictionary is sorted
+ */
+ checkdict();
+
+ tnmwords += nmwords;
+ tnpwords += npwords;
+
+ results();
+}
+
+/*
+ * Check if the given word is present on the board, with the constraint
+ * that the first letter of the word is adjacent to square 'prev'
+ * Keep track of the current path of squares for the word
+ * A 'q' must be followed by a 'u'
+ * Words must end with a null
+ * Return 1 on success, -1 on failure
+ */
+int
+checkword(const char *word, int prev, int *path)
+{
+ const char *p;
+ char *q;
+ int i, *lm;
+
+ if (debug) {
+ (void) printf("checkword(%s, %d, [", word, prev);
+ for (i = 0; wordpath[i] != -1; i++)
+ (void) printf(" %d", wordpath[i]);
+ (void) printf(" ]\n");
+ }
+
+ if (*word == '\0')
+ return (1);
+
+ lm = letter_map[*word - 'a'];
+
+ if (prev == -1) {
+ char subword[MAXWORDLEN + 1];
+
+ /*
+ * Check for letters not appearing in the cube to eliminate some
+ * recursive calls
+ * Fold 'qu' into 'q'
+ */
+ p = word;
+ q = subword;
+ while (*p != '\0') {
+ if (*letter_map[*p - 'a'] == -1)
+ return (-1);
+ *q++ = *p;
+ if (*p++ == 'q') {
+ if (*p++ != 'u')
+ return (-1);
+ }
+ }
+ *q = '\0';
+ while (*lm != -1) {
+ *path = *lm;
+ usedbits |= (1 << *lm);
+ if (checkword(subword + 1, *lm, path + 1) > 0)
+ return (1);
+ usedbits &= ~(1 << *lm);
+ lm++;
+ }
+ return (-1);
+ }
+
+ /*
+ * A cube is only adjacent to itself in the adjacency matrix if selfuse
+ * was set, so a cube can't be used twice in succession if only the
+ * reuse flag is set
+ */
+ for (i = 0; lm[i] != -1; i++) {
+ if (adjacency[prev][lm[i]]) {
+ int used;
+
+ used = 1 << lm[i];
+ /*
+ * If necessary, check if the square has already
+ * been used.
+ */
+ if (!reuse && (usedbits & used))
+ continue;
+ *path = lm[i];
+ usedbits |= used;
+ if (checkword(word + 1, lm[i], path + 1) > 0)
+ return (1);
+ usedbits &= ~used;
+ }
+ }
+ *path = -1; /* in case of a backtrack */
+ return (-1);
+}
+
+/*
+ * A word is invalid if it is not in the dictionary
+ * At this point it is already known that the word can be formed from
+ * the current board
+ */
+static int
+validword(const char *word)
+{
+ int j;
+ const char *q, *w;
+
+ j = word[0] - 'a';
+ if (dictseek(dictfp, dictindex[j].start, SEEK_SET) < 0) {
+ (void) fprintf(stderr, "Seek error\n");
+ cleanup();
+ exit(1);
+ }
+
+ while ((w = nextword(dictfp)) != NULL) {
+ int ch;
+
+ if (*w != word[0]) /* end of words starting with word[0] */
+ break;
+ q = word;
+ while ((ch = *w++) == *q++ && ch != '\0')
+ ;
+ if (*(w - 1) == '\0' && *(q - 1) == '\0')
+ return (1);
+ }
+ if (dictfp != NULL && feof(dictfp)) /* Special case for z's */
+ clearerr(dictfp);
+ return (0);
+}
+
+/*
+ * Check each word in the dictionary against the board
+ * Delete words from the machine list that the player has found
+ * Assume both the dictionary and the player's words are already sorted
+ */
+static void
+checkdict(void)
+{
+ char *p, *w;
+ const char **pw;
+ int i;
+ int prevch, previndex, st;
+
+ mwordsp = mwords;
+ nmwords = 0;
+ pw = pword;
+ prevch ='a';
+
+ (void) dictseek(dictfp, 0L, SEEK_SET);
+ while ((w = nextword(dictfp)) != NULL) {
+ if (wordlen < minlength)
+ continue;
+ if (*w != prevch) {
+ /*
+ * If we've moved on to a word with a different first
+ * letter then we can speed things up by skipping all
+ * words starting with a letter that doesn't appear in
+ * the cube.
+ */
+ i = (int) (*w - 'a');
+ while (i < 26 && letter_map[i][0] == -1)
+ i++;
+ if (i == 26)
+ break;
+ previndex = prevch - 'a';
+ prevch = i + 'a';
+ /*
+ * Fall through if the word's first letter appears in
+ * the cube (i.e., if we can't skip ahead), otherwise
+ * seek to the beginning of words in the dictionary
+ * starting with the next letter (alphabetically)
+ * appearing in the cube and then read the first word.
+ */
+ if (i != previndex + 1) {
+ if (dictseek(dictfp,
+ dictindex[i].start, SEEK_SET) < 0) {
+ warnx("seek error in checkdict()");
+ cleanup();
+ exit(1);
+ }
+ continue;
+ }
+ }
+
+ clearwordpath(0);
+ usedbits = 0;
+ if (checkword(w, -1, wordpath) == -1)
+ continue;
+
+ st = 1;
+ while (*pw != NULL && (st = strcmp(*pw, w)) < 0)
+ pw++;
+ if (st == 0) /* found it */
+ continue;
+ if (nmwords == MAXMWORDS ||
+ mwordsp + wordlen + 1 >= &mwords[MAXMSPACE]) {
+ warnx("too many words!");
+ cleanup();
+ exit(1);
+ }
+ mword[nmwords++] = mwordsp;
+ p = w;
+ while ((*mwordsp++ = *p++) != '\0')
+ ;
+ }
+}
+
+/*
+ * Crank up a new game
+ * If the argument is non-null then it is assumed to be a legal board spec
+ * in ascending cube order, oth. make a random board
+ */
+static void
+newgame(const char *b)
+{
+ int i, p, q;
+ const char *tmp;
+ int *lm[26];
+ static const char *cubes[16] = {
+ "ednosw", "aaciot", "acelrs", "ehinps",
+ "eefhiy", "elpstu", "acdemp", "gilruw",
+ "egkluy", "ahmors", "abilty", "adenvz",
+ "bfiorx", "dknotu", "abjmoq", "egintv"
+ };
+
+ if (b == NULL) {
+ /*
+ * Shake the cubes and make the board
+ */
+ i = 0;
+ while (i < 100) {
+ p = (int) (random() % 16);
+ q = (int) (random() % 16);
+ if (p != q) {
+ tmp = cubes[p];
+ cubes[p] = cubes[q];
+ cubes[q] = tmp;
+ i++;
+ }
+ /* else try again */
+ }
+
+ for (i = 0; i < 16; i++)
+ board[i] = cubes[i][random() % 6];
+ }
+ else {
+ for (i = 0; i < 16; i++)
+ board[i] = b[i];
+ }
+ board[16] = '\0';
+
+ /*
+ * Set up the map from letter to location(s)
+ * Each list is terminated by a -1 entry
+ */
+ for (i = 0; i < 26; i++) {
+ lm[i] = letter_map[i];
+ *lm[i] = -1;
+ }
+
+ for (i = 0; i < 16; i++) {
+ int j;
+
+ j = (int) (board[i] - 'a');
+ *lm[j] = i;
+ *(++lm[j]) = -1;
+ }
+
+ if (debug) {
+ for (i = 0; i < 26; i++) {
+ int ch, j;
+
+ (void) printf("%c:", 'a' + i);
+ for (j = 0; (ch = letter_map[i][j]) != -1; j++)
+ (void) printf(" %d", ch);
+ (void) printf("\n");
+ }
+ }
+
+}
+
+/*
+ * Clear wordpath[].
+ */
+static void
+clearwordpath(int full)
+{
+ size_t pos;
+ const size_t max = MAXWORDLEN + 1;
+
+ for (pos = 0; pos < max && (full || wordpath[pos] != -1); pos++) {
+ wordpath[pos] = -1;
+ }
+}
+
+static int
+compar(const void *p, const void *q)
+{
+ return (strcmp(*(const char *const *)p, *(const char *const *)q));
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "usage: %s [-bd] [-s#] [-t#] [-w#] [+[+]] [boardspec]\n",
+ getprogname());
+ exit(1);
+}
diff --git a/boggle/boggle/bog.h b/boggle/boggle/bog.h
new file mode 100644
index 0000000..06d2f5b
--- /dev/null
+++ b/boggle/boggle/bog.h
@@ -0,0 +1,83 @@
+/* $NetBSD: bog.h,v 1.3 2003/08/07 09:37:05 agc Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ *
+ * @(#)bog.h 8.1 (Berkeley) 6/11/93
+ */
+
+#define LOADDICT 1 /* Load the dictionary for speed */
+
+/*
+ * Locations for the dictionary (generated by mkdict),
+ * index (generated by mkindex), and helpfile
+ */
+#define DICT "/usr/share/games/boggle/dictionary"
+#define DICTINDEX "/usr/share/games/boggle/dictindex"
+#define HELPFILE "/usr/share/games/boggle/helpfile"
+
+/*
+ * The theoretical maximum for MAXWORDLEN is ('a' - 1) == 96
+ */
+#define MAXWORDLEN 40 /* Maximum word length */
+#define MAXPWORDS 200 /* Maximum number of player's words */
+#define MAXMWORDS 200 /* Maximum number of machine's words */
+#define MAXPSPACE 2000 /* Space for player's words */
+#define MAXMSPACE 4000 /* Space for machines's words */
+
+#define MAXCOLS 20
+
+/*
+ * The following determine the screen layout
+ */
+#define PROMPT_COL 20
+#define PROMPT_LINE 2
+
+#define BOARD_COL 0
+#define BOARD_LINE 0
+
+#define SCORE_COL 20
+#define SCORE_LINE 0
+
+#define LIST_COL 0
+#define LIST_LINE 10
+
+#define TIMER_COL 20
+#define TIMER_LINE 2
+
+/*
+ * Internal dictionary index
+ * Initialized from the file created by mkindex
+ */
+struct dictindex {
+ long start;
+ long length;
+};
diff --git a/boggle/boggle/boggle.6 b/boggle/boggle/boggle.6
new file mode 100644
index 0000000..f5812ff
--- /dev/null
+++ b/boggle/boggle/boggle.6
@@ -0,0 +1,127 @@
+.\" $NetBSD: boggle.6,v 1.9 2006/09/24 01:38:57 jmcneill Exp $
+.\"
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Barry Brachman.
+.\"
+.\" 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.
+.\"
+.\" @(#)boggle.6 8.1 (Berkeley) 6/11/93
+.\"
+.Dd September 23, 2006
+.Dt BOGGLE 6
+.Os
+.Sh NAME
+.Nm boggle
+.Nd word search game
+.Sh SYNOPSIS
+.Nm
+.Op Fl bd
+.Op Fl s Ar seed
+.Op Fl t Ar time
+.Op Fl w Ar length
+.Op + Op +
+.Op boardspec
+.Sh DESCRIPTION
+The object of
+.Nm
+is to find as many words as possible on the Boggle board within the three
+minute time limit.
+A Boggle board is a four by four arrangement of Boggle cubes, each side of
+each cube displaying a letter of the alphabet or `qu'.
+Words are formed by finding a sequence of cubes (letters) that are in the
+game's dictionary.
+The (N+1)th cube in the word must be horizontally,
+vertically, or diagonally adjacent to the Nth cube.
+Cubes cannot be reused.
+Words consist solely of lower case letters and must be at least 3 letters long.
+.Pp
+Command line flags can be given to change the rules of the game.
+.Bl -tag -width boardspec
+.It Fl b
+Run
+.Nm
+in batch mode.
+A
+.Ar boardspec
+must also be given.
+The dictionary is read from stdin and a list of words appearing in
+.Ar boardspec
+is printed to stdout.
+.It Fl d
+Enable debugging output.
+.It Fl s Ar seed
+Specify a seed
+.Ar seed
+other than the time of day.
+.It Fl t Ar time
+Set the time limit for each game from the default 3 minutes to
+.Ar time
+seconds.
+.It Fl w Ar length
+Change the minimum word length from 3 letters to
+.Ar length .
+.It Ar +
+This flag allows a cube to be used multiple times, but not in succession.
+.It Ar ++
+This flag allows the same cubes to be considered adjacent to itself.
+.It Ar boardspec
+A starting board position can be specified on the command line by
+listing the board left to right and top to bottom.
+.El
+.Pp
+Help is available during play by typing
+.Sq Ic \&? .
+More detailed information on the game is given there.
+.Sh AUTHORS
+Boggle is a trademark of Parker Brothers.
+.Pp
+Barry Brachman
+.Pp
+Dept. of Computer Science
+.Pp
+University of British Columbia
+.Sh BUGS
+If there are a great many words in the cube the final display of the words
+may scroll off of the screen.
+(On a 25 line screen about 130 words can be displayed.)
+.Pp
+No word can contain a
+.Sq q
+that is not immediately followed by a
+.Sq u .
+.Pp
+When using the
+.Ar +
+or
+.Ar ++
+options the display of words found in the board doesn't indicate reused cubes.
+.Pp
+The dictionary that
+.Nx
+installs omits many words that belong in the English language, most
+notably inflected forms.
diff --git a/boggle/boggle/extern.h b/boggle/boggle/extern.h
new file mode 100644
index 0000000..ee9a340
--- /dev/null
+++ b/boggle/boggle/extern.h
@@ -0,0 +1,62 @@
+/* $NetBSD: extern.h,v 1.11 2009/08/12 05:29:40 dholland Exp $ */
+
+/*-
+ * Copyright (c) 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/11/93
+ */
+
+#include <time.h>
+
+void addword(const char *);
+void badword(void);
+int checkword(const char *, int, int *);
+void cleanup(void);
+void delay(int);
+long dictseek(FILE *, long, int);
+void findword(void);
+void flushin(FILE *);
+char *get_line(char *);
+int help(void);
+int inputch(void);
+int loaddict(FILE *);
+int loadindex(const char *);
+char *nextword(FILE *);
+FILE *opendict(const char *);
+void prompt(const char *);
+void prtable(const char *const [],
+ int, int, int, void (*)(const char *const [], int),
+ int (*)(const char *const [], int));
+void redraw(void);
+void results(void);
+int setup(int, time_t);
+void showboard(const char *);
+void showstr(const char *, int);
+void showword(int);
+void startwords(void);
+int timerch(void);
diff --git a/boggle/boggle/help.c b/boggle/boggle/help.c
new file mode 100644
index 0000000..0310683
--- /dev/null
+++ b/boggle/boggle/help.c
@@ -0,0 +1,105 @@
+/* $NetBSD: help.c,v 1.7 2011/08/31 16:24:55 plunky Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)help.c 8.1 (Berkeley) 6/11/93";
+#else
+__RCSID("$NetBSD: help.c,v 1.7 2011/08/31 16:24:55 plunky Exp $");
+#endif
+#endif /* not lint */
+
+#include <curses.h>
+#include <stdio.h>
+
+#include "bog.h"
+#include "extern.h"
+
+extern int nlines;
+
+int
+help(void)
+{
+ int eof, i;
+ FILE *fp;
+ WINDOW *win;
+ char buf[BUFSIZ];
+
+ if ((fp = fopen(HELPFILE, "r")) == NULL)
+ return(-1);
+ win = newwin(0, 0, 0, 0);
+ clearok(win, 1);
+
+ eof = 0;
+ if (ungetc(getc(fp), fp) == EOF) {
+ wprintw(win, "There doesn't seem to be any help.");
+ eof = 1; /* Nothing there... */
+ }
+
+ while (!eof) {
+ for (i = 0; i < nlines - 3; i++) {
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ eof = 1;
+ break;
+ }
+ if (buf[0] == '.' && buf[1] == '\n')
+ break;
+ wprintw(win, "%s", buf);
+ }
+ if (eof || ungetc(getc(fp), fp) == EOF) {
+ eof = 1;
+ break;
+ }
+ wmove(win, nlines - 1, 0);
+ wprintw(win,
+ "Type <space> to continue, anything else to quit...");
+ wrefresh(win);
+ if ((inputch() & 0177) != ' ')
+ break;
+ wclear(win);
+ }
+
+ fclose(fp);
+ if (eof) {
+ wmove(win, nlines - 1, 0);
+ wprintw(win, "Hit any key to continue...");
+ wrefresh(win);
+ inputch();
+ }
+ delwin(win);
+ clearok(stdscr, 1);
+ refresh();
+ return(0);
+}
diff --git a/boggle/boggle/helpfile b/boggle/boggle/helpfile
new file mode 100644
index 0000000..c511efe
--- /dev/null
+++ b/boggle/boggle/helpfile
@@ -0,0 +1,92 @@
+
+Commands:
+
+Enter word: <return> or <linefeed> or <space>
+Delete previous character: <delete> or <backspace>
+Delete line: <^u> or <^w>
+Redraw screen: <^l> or <^r>
+Pause game: <^s>
+Resume game: <^q> or <^s>
+Suspend game (BSD only): <^z>
+Give up on current cube: <^d>
+Show remaining time: <space> first thing on a line
+Show help: ? (Suspends timer until done)
+Exit game: <^c>
+
+(^u means "control u", etc.)
+
+[Note for users of versions of this program that do not display a timer:
+The first word entered after the timer has run out causes a list of all the
+words you found, the words you missed, and your running statistics to be
+displayed.]
+
+Any time you are prompted while the board is displayed you can type:
+ <esc>word
+to see where "word" is on the board.
+
+Usage:
+ bog [-b] [-d] [-s#] [-t#] [-w#] [+[+]] [boardspec]
+
+ -b: batch mode (boardspec must be present); dictionary read from stdin
+ -d: debug mode
+ -s#: use # as the random number seed
+ -t#: time limit is # seconds instead of default 180
+ -w#: minimum word length is # letters instead of default 3
+ +: can reuse a cube, but not twice in succession
+ ++: can reuse cubes arbitrarily
+ boardspec: the first board to use (use 'q' for 'qu'); e.g.:
+ bog nolezeebnqieegei
+.
+ Default Rules
+
+A Boggle board is a four by four arrangement of Boggle cubes.
+You have 3 minutes to find as many words as possible in the Boggle board.
+Words are formed by finding a sequence of cubes (letters) that are in the
+game's dictionary. The (N+1)th cube in the word must be horizontally,
+vertically, or diagonally adjacent to the Nth cube. Cubes cannot be reused.
+Words consist solely of lower case letters and must be at least 3 letters long.
+.
+ Options
+
+Command line flags can be given to change the rules of the game.
+The '+' flag allows a cube to be used multiple times, but not in succession.
+The '++' flag makes each cube adjacent to itself.
+The time limit can be changed from the default 3 minutes by using the flag
+'-t#', where # is the duration (in seconds) of each game.
+The minimum word length can be changed from 3 letters by specifying 'w#',
+where # is the minimum number of letters to use.
+.
+ Bugs and Limitations
+
+The following bugs and problems are known to exist:
+
+- If there are a great many words in the cube the final display of the words
+ may scroll off of the screen. (On a 25 line screen about 130 words can be
+ displayed.)
+
+- Computing the complete word list can be too slow on small machines.
+
+- No word can contain a 'q' that is not immediately followed by a 'u'.
+
+- When using the '+' or '++' options the display of words found in the board
+ doesn't indicate reused cubes.
+.
+ About This Program
+
+Permission is given to freely copy and distribute this software providing:
+
+1) You do not sell it,
+2) You do not use it for commercial advantage,
+3) If you pass the program on you must make the source code available, and
+4) This notice must accompany the distribution
+
+Please notify the author of any bugs or if you have any suggestions.
+
+Copyright (c) 1988
+Barry Brachman | UUCP: {alberta,uw-beaver,uunet}!
+Dept. of Computer Science| ubc-vision!ubc-csgrads!brachman
+Univ. of British Columbia| Internet: brachman@cs.ubc.ca
+Vancouver, B.C. V6T 1W5 | brachman%ubc.csnet@csnet-relay.arpa
+(604) 228-5010 | brachman@ubc.csnet
+
+Boggle is a trademark of Parker Brothers.
diff --git a/boggle/boggle/mach.c b/boggle/boggle/mach.c
new file mode 100644
index 0000000..7c86176
--- /dev/null
+++ b/boggle/boggle/mach.c
@@ -0,0 +1,665 @@
+/* $NetBSD: mach.c,v 1.21 2011/08/31 16:24:55 plunky Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mach.c 8.1 (Berkeley) 6/11/93";
+#else
+__RCSID("$NetBSD: mach.c,v 1.21 2011/08/31 16:24:55 plunky Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * Terminal interface
+ *
+ * Input is raw and unechoed
+ */
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <curses.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static int ccol, crow, maxw;
+static int colstarts[MAXCOLS], ncolstarts;
+static int lastline;
+static int ncols;
+int nlines;
+
+extern const char *pword[], *mword[];
+extern int ngames, nmwords, npwords, tnmwords, tnpwords;
+extern char board[];
+extern int usedbits, wordpath[];
+extern time_t start_t;
+extern int debug;
+
+static void cont_catcher(int);
+static int prwidth(const char *const [], int);
+static void prword(const char *const [], int);
+static void stop_catcher(int);
+static void tty_cleanup(void);
+static int tty_setup(void);
+static void tty_showboard(const char *);
+static void winch_catcher(int);
+static void getword(char *);
+static void starttime(void);
+static void stoptime(void);
+
+
+/*
+ * Do system dependent initialization
+ * This is called once, when the program starts
+ */
+int
+setup(int sflag, time_t seed)
+{
+ if (tty_setup() < 0)
+ return(-1);
+
+ if (!sflag)
+ time(&seed);
+ srandom(seed);
+ if (debug)
+ (void) printf("seed = %ld\n", (long) seed);
+ return(0);
+}
+
+/*
+ * Do system dependent clean up
+ * This is called once, just before the program terminates
+ */
+void
+cleanup(void)
+{
+ tty_cleanup();
+}
+
+/*
+ * Display the player's word list, the list of words not found, and the running
+ * stats
+ */
+void
+results(void)
+{
+ int col, row;
+ int denom1, denom2;
+
+ move(LIST_LINE, LIST_COL);
+ clrtobot();
+ printw("Words you found (%d):", npwords);
+ refresh();
+ move(LIST_LINE + 1, LIST_COL);
+ prtable(pword, npwords, 0, ncols, prword, prwidth);
+
+ getyx(stdscr, row, col);
+ move(row + 1, col);
+ printw("Words you missed (%d):", nmwords);
+ refresh();
+ move(row + 2, col);
+ prtable(mword, nmwords, 0, ncols, prword, prwidth);
+
+ denom1 = npwords + nmwords;
+ denom2 = tnpwords + tnmwords;
+
+ move(SCORE_LINE, SCORE_COL);
+ printw("Percentage: %0.2f%% (%0.2f%% over %d game%s)\n",
+ denom1 ? (100.0 * npwords) / (double) (npwords + nmwords) : 0.0,
+ denom2 ? (100.0 * tnpwords) / (double) (tnpwords + tnmwords) : 0.0,
+ ngames, ngames > 1 ? "s" : "");
+}
+
+static void
+prword(const char *const base[], int indx)
+{
+ printw("%s", base[indx]);
+}
+
+static int
+prwidth(const char *const base[], int indx)
+{
+ return (strlen(base[indx]));
+}
+
+/*
+ * Main input routine
+ *
+ * - doesn't accept words longer than MAXWORDLEN or containing caps
+ */
+char *
+get_line(char *q)
+{
+ int ch, done;
+ char *p;
+ int row, col;
+
+ p = q;
+ done = 0;
+ while (!done) {
+ ch = timerch();
+ switch (ch) {
+ case '\n':
+ case '\r':
+ case ' ':
+ done = 1;
+ break;
+ case '\e':
+ findword();
+ break;
+ case '\177': /* <del> */
+ case CTRL('h'): /* <bs> */
+ if (p == q)
+ break;
+ p--;
+ getyx(stdscr, row, col);
+ move(row, col - 1);
+ clrtoeol();
+ refresh();
+ break;
+ case CTRL('u'): /* <^u> */
+ case CTRL('w'): /* <^w> */
+ if (p == q)
+ break;
+ getyx(stdscr, row, col);
+ move(row, col - (int) (p - q));
+ p = q;
+ clrtoeol();
+ refresh();
+ break;
+#ifdef SIGTSTP
+ case CTRL('z'): /* <^z> */
+ stop_catcher(0);
+ break;
+#endif
+ case CTRL('s'): /* <^s> */
+ stoptime();
+ printw("<PAUSE>");
+ refresh();
+ while ((ch = inputch()) != '\021' && ch != '\023')
+ ;
+ move(crow, ccol);
+ clrtoeol();
+ refresh();
+ starttime();
+ break;
+ case CTRL('c'): /* <^c> */
+ cleanup();
+ exit(0);
+ /*NOTREACHED*/
+ case CTRL('d'): /* <^d> */
+ done = 1;
+ ch = EOF;
+ break;
+ case CTRL('r'): /* <^l> */
+ case CTRL('l'): /* <^r> */
+ redraw();
+ break;
+ case '?':
+ stoptime();
+ if (help() < 0)
+ showstr("Can't open help file", 1);
+ touchwin(stdscr);
+ starttime();
+ break;
+ default:
+ if (!islower(ch))
+ break;
+ if ((int) (p - q) == MAXWORDLEN) {
+ p = q;
+ badword();
+ break;
+ }
+ *p++ = ch;
+ addch(ch);
+ refresh();
+ break;
+ }
+ }
+ *p = '\0';
+ if (ch == EOF)
+ return (NULL);
+ return(q);
+}
+
+int
+inputch(void)
+{
+ return (getch() & 0177);
+}
+
+void
+redraw(void)
+{
+ clearok(stdscr, 1);
+ refresh();
+}
+
+void
+flushin(FILE *fp)
+{
+
+ (void) tcflush(fileno(fp), TCIFLUSH);
+}
+
+static int gone;
+
+/*
+ * Stop the game timer
+ */
+static void
+stoptime(void)
+{
+ time_t t;
+
+ (void)time(&t);
+ gone = (int) (t - start_t);
+}
+
+/*
+ * Restart the game timer
+ */
+static void
+starttime(void)
+{
+ time_t t;
+
+ (void)time(&t);
+ start_t = t - (long) gone;
+}
+
+/*
+ * Initialize for the display of the player's words as they are typed
+ * This display starts at (LIST_LINE, LIST_COL) and goes "down" until the last
+ * line. After the last line a new column is started at LIST_LINE
+ * Keep track of each column position for showword()
+ * There is no check for exceeding COLS
+ */
+void
+startwords(void)
+{
+ crow = LIST_LINE;
+ ccol = LIST_COL;
+ maxw = 0;
+ ncolstarts = 1;
+ colstarts[0] = LIST_COL;
+ move(LIST_LINE, LIST_COL);
+ refresh();
+}
+
+/*
+ * Add a word to the list and start a new column if necessary
+ * The maximum width of the current column is maintained so we know where
+ * to start the next column
+ */
+void
+addword(const char *w)
+{
+ int n;
+
+ if (crow == lastline) {
+ crow = LIST_LINE;
+ ccol += (maxw + 5);
+ colstarts[ncolstarts++] = ccol;
+ maxw = 0;
+ move(crow, ccol);
+ }
+ else {
+ move(++crow, ccol);
+ if ((n = strlen(w)) > maxw)
+ maxw = n;
+ }
+ refresh();
+}
+
+/*
+ * The current word is unacceptable so erase it
+ */
+void
+badword(void)
+{
+
+ move(crow, ccol);
+ clrtoeol();
+ refresh();
+}
+
+/*
+ * Highlight the nth word in the list (starting with word 0)
+ * No check for wild arg
+ */
+void
+showword(int n)
+{
+ int col, row;
+
+ row = LIST_LINE + n % (lastline - LIST_LINE + 1);
+ col = colstarts[n / (lastline - LIST_LINE + 1)];
+ move(row, col);
+ standout();
+ printw("%s", pword[n]);
+ standend();
+ move(crow, ccol);
+ refresh();
+ delay(15);
+ move(row, col);
+ printw("%s", pword[n]);
+ move(crow, ccol);
+ refresh();
+}
+
+/*
+ * Get a word from the user and check if it is in either of the two
+ * word lists
+ * If it's found, show the word on the board for a short time and then
+ * erase the word
+ *
+ * Note: this function knows about the format of the board
+ */
+void
+findword(void)
+{
+ int c, col, found, i, r, row;
+ char buf[MAXWORDLEN + 1];
+
+ getyx(stdscr, r, c);
+ getword(buf);
+ found = 0;
+ for (i = 0; i < npwords; i++) {
+ if (strcmp(buf, pword[i]) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ for (i = 0; i < nmwords; i++) {
+ if (strcmp(buf, mword[i]) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ for (i = 0; i < MAXWORDLEN; i++)
+ wordpath[i] = -1;
+ usedbits = 0;
+ if (!found || checkword(buf, -1, wordpath) == -1) {
+ move(r, c);
+ clrtoeol();
+ addstr("[???]");
+ refresh();
+ delay(10);
+ move(r, c);
+ clrtoeol();
+ refresh();
+ return;
+ }
+
+ standout();
+ for (i = 0; wordpath[i] != -1; i++) {
+ row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1;
+ col = BOARD_COL + (wordpath[i] % 4) * 4 + 2;
+ move(row, col);
+ if (board[wordpath[i]] == 'q')
+ printw("Qu");
+ else
+ printw("%c",
+ toupper((unsigned char)board[wordpath[i]]));
+ move(r, c);
+ refresh();
+ delay(5);
+ }
+
+ standend();
+
+ for (i = 0; wordpath[i] != -1; i++) {
+ row = BOARD_LINE + (wordpath[i] / 4) * 2 + 1;
+ col = BOARD_COL + (wordpath[i] % 4) * 4 + 2;
+ move(row, col);
+ if (board[wordpath[i]] == 'q')
+ printw("Qu");
+ else
+ printw("%c",
+ toupper((unsigned char)board[wordpath[i]]));
+ }
+ move(r, c);
+ clrtoeol();
+ refresh();
+}
+
+/*
+ * Display a string at the current cursor position for the given number of secs
+ */
+void
+showstr(const char *str, int delaysecs)
+{
+ addstr(str);
+ refresh();
+ delay(delaysecs * 10);
+ move(crow, ccol);
+ clrtoeol();
+ refresh();
+}
+
+/*
+ * Get a valid word and put it in the buffer
+ */
+static void
+getword(char *q)
+{
+ int ch, col, done, i, row;
+ char *p;
+
+ done = 0;
+ i = 0;
+ p = q;
+ addch('[');
+ refresh();
+ while (!done && i < MAXWORDLEN - 1) {
+ ch = getch() & 0177;
+ switch (ch) {
+ case '\177': /* <del> */
+ case '\010': /* <bs> */
+ if (p == q)
+ break;
+ p--;
+ getyx(stdscr, row, col);
+ move(row, col - 1);
+ clrtoeol();
+ break;
+ case '\025': /* <^u> */
+ case '\027': /* <^w> */
+ if (p == q)
+ break;
+ getyx(stdscr, row, col);
+ move(row, col - (int) (p - q));
+ p = q;
+ clrtoeol();
+ break;
+ case ' ':
+ case '\n':
+ case '\r':
+ done = 1;
+ break;
+ case '\014': /* <^l> */
+ case '\022': /* <^r> */
+ clearok(stdscr, 1);
+ refresh();
+ break;
+ default:
+ if (islower(ch)) {
+ *p++ = ch;
+ addch(ch);
+ i++;
+ }
+ break;
+ }
+ refresh();
+ }
+ *p = '\0';
+ addch(']');
+ refresh();
+}
+
+void
+showboard(const char *b)
+{
+ tty_showboard(b);
+}
+
+void
+prompt(const char *mesg)
+{
+ move(PROMPT_LINE, PROMPT_COL);
+ printw("%s", mesg);
+ move(PROMPT_LINE + 1, PROMPT_COL);
+ refresh();
+}
+
+static int
+tty_setup(void)
+{
+ if (!initscr()) {
+ fprintf(stderr, "couldn't initialize screen\n");
+ exit (0);
+ }
+ raw();
+ noecho();
+
+ /*
+ * Does curses look at the winsize structure?
+ * Should handle SIGWINCH ...
+ */
+ nlines = LINES;
+ lastline = nlines - 1;
+ ncols = COLS;
+
+ signal(SIGTSTP, stop_catcher);
+ signal(SIGCONT, cont_catcher);
+ signal(SIGWINCH, winch_catcher);
+ return(0);
+}
+
+static void
+stop_catcher(int signo __attribute__((unused)))
+{
+ sigset_t isigset, osigset;
+
+ stoptime();
+ noraw();
+ echo();
+ move(nlines - 1, 0);
+ refresh();
+
+ signal(SIGTSTP, SIG_DFL);
+ sigemptyset(&isigset);
+ sigaddset(&isigset, SIGTSTP);
+ sigprocmask(SIG_UNBLOCK, &isigset, &osigset);
+ kill(0, SIGTSTP);
+ sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+ signal(SIGTSTP, stop_catcher);
+}
+
+static void
+cont_catcher(int signo __attribute__((unused)))
+{
+ noecho();
+ raw();
+ clearok(stdscr, 1);
+ move(crow, ccol);
+ refresh();
+ starttime();
+}
+
+/*
+ * The signal is caught but nothing is done about it...
+ * It would mean reformatting the entire display
+ */
+static void
+winch_catcher(int signo __attribute__((unused)))
+{
+ struct winsize win;
+
+ (void) signal(SIGWINCH, winch_catcher);
+ (void) ioctl(fileno(stdout), TIOCGWINSZ, &win);
+ /*
+ LINES = win.ws_row;
+ COLS = win.ws_col;
+ */
+}
+
+static void
+tty_cleanup(void)
+{
+ move(nlines - 1, 0);
+ refresh();
+ noraw();
+ echo();
+ endwin();
+}
+
+static void
+tty_showboard(const char *b)
+{
+ int i;
+ int line;
+
+ clear();
+ move(BOARD_LINE, BOARD_COL);
+ line = BOARD_LINE;
+ printw("+---+---+---+---+");
+ move(++line, BOARD_COL);
+ for (i = 0; i < 16; i++) {
+ if (b[i] == 'q')
+ printw("| Qu");
+ else
+ printw("| %c ", toupper((unsigned char)b[i]));
+ if ((i + 1) % 4 == 0) {
+ printw("|");
+ move(++line, BOARD_COL);
+ printw("+---+---+---+---+");
+ move(++line, BOARD_COL);
+ }
+ }
+ move(SCORE_LINE, SCORE_COL);
+ printw("Type '?' for help");
+ refresh();
+}
diff --git a/boggle/boggle/prtable.c b/boggle/boggle/prtable.c
new file mode 100644
index 0000000..63d57e1
--- /dev/null
+++ b/boggle/boggle/prtable.c
@@ -0,0 +1,127 @@
+/* $NetBSD: prtable.c,v 1.10 2013/10/19 17:23:08 christos Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ *
+ * @(#)prtable.c 8.1 (Berkeley) 6/11/93
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: prtable.c,v 1.10 2013/10/19 17:23:08 christos Exp $");
+#endif /* not lint */
+
+#include <curses.h>
+
+#include "extern.h"
+
+#define NCOLS 5
+
+static int get_maxlen(const char *const [], int, int (*)(const char *const *, int));
+
+/*
+ * Routine to print a table
+ * Modified from 'ls.c' mods (BJB/83)
+ * Arguments:
+ * base - address of first entry
+ * num - number of entries
+ * d_cols - number of columns to use if > 0, "best" size if == 0
+ * width - max line width if not zero
+ * prentry - address of the routine to call to print the string
+ * length - address of the routine to call to determine the length
+ * of string to be printed
+ *
+ * prtable and length are called with the address of the base and
+ * an index
+ */
+void
+prtable(const char *const base[], int num, int d_cols, int width,
+ void (*prentry)(const char *const [], int),
+ int (*length)(const char *const [], int))
+{
+ int c, j;
+ int a, b, cols, loc, maxlen, nrows, z;
+ int col, row;
+
+ if (num == 0)
+ return;
+ maxlen = get_maxlen(base, num, length) + 1;
+ if (d_cols > 0)
+ cols = d_cols;
+ else
+ cols = width / maxlen;
+ if (cols == 0)
+ cols = NCOLS;
+ nrows = (num - 1) / cols + 1;
+ for (a = 1; a <= nrows; a++) {
+ b = c = z = loc = 0;
+ for (j = 0; j < num; j++) {
+ c++;
+ if (c >= a + b)
+ break;
+ }
+ while (j < num) {
+ (*prentry)(base, j);
+ loc += (*length)(base, j);
+ z++;
+ b += nrows;
+ for (j++; j < num; j++) {
+ c++;
+ if (c >= a + b)
+ break;
+ }
+ if (j < num) {
+ while (loc < z * maxlen) {
+ addch(' ');
+ loc++;
+ }
+ }
+ }
+ getyx(stdscr, row, col);
+ __USE(col);
+ move(row + 1, 0);
+ }
+ refresh();
+}
+
+static int
+get_maxlen(const char *const base[], int num,
+ int (*length)(const char *const *, int))
+{
+ int i, len, max;
+
+ max = (*length)(base, 0);
+ for (i = 0; i < num; i++) {
+ if ((len = (*length)(base, i)) > max)
+ max = len;
+ }
+ return(max);
+}
diff --git a/boggle/boggle/timer.c b/boggle/boggle/timer.c
new file mode 100644
index 0000000..dc4dc69
--- /dev/null
+++ b/boggle/boggle/timer.c
@@ -0,0 +1,119 @@
+/* $NetBSD: timer.c,v 1.10 2005/07/01 16:38:24 jmc Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)timer.c 8.2 (Berkeley) 2/22/94";
+#else
+__RCSID("$NetBSD: timer.c,v 1.10 2005/07/01 16:38:24 jmc Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+
+#include <curses.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static int waitch(int);
+
+extern int tlimit;
+extern time_t start_t;
+extern jmp_buf env;
+
+/*
+ * Update the display of the remaining time while waiting for a character
+ * If time runs out do a longjmp() to the game controlling routine, returning
+ * non-zero; oth. return the character
+ * Leave the cursor where it was initially
+ */
+int
+timerch(void)
+{
+ time_t prevt, t;
+ int col, remaining, row;
+
+ getyx(stdscr, row, col);
+ prevt = 0L;
+ for (;;) {
+ if (waitch(1) == 1)
+ break;
+ time(&t);
+ if (t == prevt)
+ continue;
+ prevt = t;
+ remaining = tlimit - (int) (t - start_t);
+ if (remaining < 0) {
+ longjmp(env, 1);
+ /*NOTREACHED*/
+ }
+ move(TIMER_LINE, TIMER_COL);
+ printw("%d:%02d", remaining / 60, remaining % 60);
+ move(row, col);
+ refresh();
+ }
+ return (getch() & 0177);
+}
+
+/*
+ * Wait up to 'delay' microseconds for input to appear
+ * Returns 1 if input is ready, 0 oth.
+ */
+static int
+waitch(int tdelay)
+{
+ struct pollfd set[1];
+
+ set[0].fd = STDIN_FILENO;
+ set[0].events = POLLIN;
+ return (poll(set, 1, tdelay));
+}
+
+void
+delay(int tenths)
+{
+ struct timespec duration;
+
+ duration.tv_nsec = (tenths % 10 ) * 100000000L;
+ duration.tv_sec = (long) (tenths / 10);
+ nanosleep(&duration, NULL);
+}
diff --git a/boggle/boggle/word.c b/boggle/boggle/word.c
new file mode 100644
index 0000000..abcc479
--- /dev/null
+++ b/boggle/boggle/word.c
@@ -0,0 +1,215 @@
+/* $NetBSD: word.c,v 1.9 2006/03/18 09:40:46 rtr Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)word.c 8.1 (Berkeley) 6/11/93";
+#else
+__RCSID("$NetBSD: word.c,v 1.9 2006/03/18 09:40:46 rtr Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bog.h"
+#include "extern.h"
+
+static char *dictspace, *dictend;
+static char *sp;
+
+static int first = 1, lastch = 0;
+
+extern struct dictindex dictindex[];
+extern int wordlen;
+
+/*
+ * Return the next word in the compressed dictionary in 'buffer' or
+ * NULL on end-of-file
+ */
+char *
+nextword(FILE *fp)
+{
+ int ch, pcount;
+ char *p;
+ static char buf[MAXWORDLEN + 1];
+
+ if (fp == NULL) {
+ if (sp == dictend)
+ return (NULL);
+
+ p = buf + (int) *sp++;
+
+ /*
+ * The dictionary ends with a null byte
+ */
+ while (*sp >= 'a')
+ if ((*p++ = *sp++) == 'q')
+ *p++ = 'u';
+ } else {
+ if (first) {
+ if ((pcount = getc(fp)) == EOF)
+ return (NULL);
+ first = 0;
+ } else if ((pcount = lastch) == EOF)
+ return (NULL);
+
+ p = buf + pcount;
+
+ while ((ch = getc(fp)) != EOF && ch >= 'a')
+ if ((*p++ = ch) == 'q')
+ *p++ = 'u';
+ lastch = ch;
+ }
+ wordlen = (int) (p - buf);
+ *p = '\0';
+ return (buf);
+}
+
+/*
+ * Reset the state of nextword() and do the fseek()
+ */
+long
+dictseek(FILE *fp, long offset, int ptrname)
+{
+ if (fp == NULL) {
+ if ((sp = dictspace + offset) >= dictend)
+ return (-1);
+ return (0);
+ }
+
+ first = 1;
+ return (fseek(fp, offset, ptrname));
+}
+
+FILE *
+opendict(const char *dict)
+{
+ FILE *fp;
+
+ if ((fp = fopen(dict, "r")) == NULL)
+ return (NULL);
+ return (fp);
+}
+
+/*
+ * Load the given dictionary and initialize the pointers
+ */
+int
+loaddict(FILE *fp)
+{
+ struct stat statb;
+ long n;
+ int st;
+ char *p;
+
+ if (fstat(fileno(fp), &statb) < 0) {
+ (void)fclose(fp);
+ return (-1);
+ }
+
+ /*
+ * An extra character (a sentinel) is allocated and set to null
+ * to improve the expansion loop in nextword().
+ */
+ if ((dictspace = malloc(statb.st_size + 1)) == NULL) {
+ (void)fclose(fp);
+ return (-1);
+ }
+ n = (long)statb.st_size;
+ sp = dictspace;
+ dictend = dictspace + n;
+
+ p = dictspace;
+ st = -1;
+ while (n > 0 && (st = fread(p, 1, BUFSIZ, fp)) > 0) {
+ p += st;
+ n -= st;
+ }
+ if (st < 0) {
+ (void)fclose(fp);
+ warnx("Error reading dictionary");
+ return (-1);
+ }
+ *p = '\0';
+ return (0);
+}
+
+/*
+ * Dependent on the exact format of the index file:
+ * Starting offset field begins in column 1 and length field in column 9
+ * Taking the easy way out, the input buffer is made "large" and a check
+ * is made for lines that are too long
+ */
+int
+loadindex(const char *indexfile)
+{
+ int i, j;
+ char buf[BUFSIZ];
+ FILE *fp;
+
+ if ((fp = fopen(indexfile, "r")) == NULL) {
+ warn("Can't open '%s'", indexfile);
+ return (-1);
+ }
+ i = 0;
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (strchr(buf, '\n') == NULL) {
+ warnx("A line in the index file is too long");
+ (void) fclose(fp);
+ return(-1);
+ }
+ j = *buf - 'a';
+ if (i != j) {
+ warnx("Bad index order");
+ (void) fclose(fp);
+ return(-1);
+ }
+ dictindex[j].start = atol(buf + 1);
+ dictindex[j].length = atol(buf + 9) - dictindex[j].start;
+ i++;
+ }
+ (void) fclose(fp);
+ if (i != 26) {
+ warnx("Bad index length");
+ return(-1);
+ }
+ return(0);
+}
diff --git a/boggle/mkdict/Makefile b/boggle/mkdict/Makefile
new file mode 100644
index 0000000..079fbd0
--- /dev/null
+++ b/boggle/mkdict/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.12 2002/09/18 06:16:40 lukem Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/11/93
+
+NOMAN= # defined
+
+# 20150209 bkw: PROG instead of HOSTPROG
+PROG= mkdict
+CPPFLAGS+= -I${.CURDIR}/../boggle
+
+.include <bsd.prog.mk>
diff --git a/boggle/mkdict/mkdict.c b/boggle/mkdict/mkdict.c
new file mode 100644
index 0000000..6dc083b
--- /dev/null
+++ b/boggle/mkdict/mkdict.c
@@ -0,0 +1,124 @@
+/* $NetBSD: mkdict.c,v 1.11 2005/07/01 16:38:24 jmc Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+ "@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#if 0
+static char sccsid[] = "@(#)mkdict.c 8.1 (Berkeley) 6/11/93";
+#else
+static const char rcsid[] =
+ "$NetBSD: mkdict.c,v 1.11 2005/07/01 16:38:24 jmc Exp $";
+#endif
+#endif /* not lint */
+
+/*
+ * Filter out words that:
+ * 1) Are not completely made up of lower case letters
+ * 2) Contain a 'q' not immediately followed by a 'u'
+ * 3) Are less that 3 characters long
+ * 4) Are greater than MAXWORDLEN characters long
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bog.h"
+
+int
+main(int argc, char *argv[])
+{
+ char *p, *q;
+ int ch, common, nwords;
+ int current, len, prev, qcount;
+ char buf[2][MAXWORDLEN + 1];
+
+ prev = 0;
+ current = 1;
+ buf[prev][0] = '\0';
+
+ for (nwords = 1;
+ fgets(buf[current], MAXWORDLEN + 1, stdin) != NULL; ++nwords) {
+ if ((p = strchr(buf[current], '\n')) == NULL) {
+ fprintf(stderr, "word too long: %s\n", buf[current]);
+ while ((ch = getc(stdin)) != EOF && ch != '\n')
+ ;
+ if (ch == EOF)
+ break;
+ continue;
+ }
+ len = 0;
+ for (p = buf[current]; *p != '\n'; p++) {
+ if (!islower((unsigned char)*p))
+ break;
+ if (*p == 'q') {
+ q = p + 1;
+ if (*q != 'u')
+ break;
+ else {
+ while ((*q = *(q + 1)))
+ q++;
+ }
+ len++;
+ }
+ len++;
+ }
+ if (*p != '\n' || len < 3 || len > MAXWORDLEN)
+ continue;
+ if (argc == 2 && nwords % atoi(argv[1]))
+ continue;
+
+ *p = '\0';
+ p = buf[current];
+ q = buf[prev];
+ qcount = 0;
+ while ((ch = *p++) == *q++ && ch != '\0')
+ if (ch == 'q')
+ qcount++;
+ common = p - buf[current] - 1;
+ printf("%c%s", common + qcount, p - 1);
+ prev = !prev;
+ current = !current;
+ }
+ fprintf(stderr, "%d words\n", nwords);
+ fflush(stdout);
+ if (ferror(stdout)) {
+ perror("error writing standard output");
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/boggle/mkindex/Makefile b/boggle/mkindex/Makefile
new file mode 100644
index 0000000..8f55079
--- /dev/null
+++ b/boggle/mkindex/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.10 2002/09/18 06:16:40 lukem Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/11/93
+
+NOMAN= # defined
+
+# 20150209 bkw: PROG instead of HOSTPROG
+PROG= mkindex
+CPPFLAGS+= -I${.CURDIR}/../boggle
+
+.include <bsd.prog.mk>
diff --git a/boggle/mkindex/mkindex.c b/boggle/mkindex/mkindex.c
new file mode 100644
index 0000000..ddda2bd
--- /dev/null
+++ b/boggle/mkindex/mkindex.c
@@ -0,0 +1,131 @@
+/* $NetBSD: mkindex.c,v 1.11 2009/08/12 05:29:40 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Barry Brachman.
+ *
+ * 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.
+ */
+
+#ifndef lint
+static char copyright[] = "@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+
+#if 0
+static char sccsid[] = "@(#)mkindex.c 8.1 (Berkeley) 6/11/93";
+#else
+static char rcsid[] =
+ "$NetBSD: mkindex.c,v 1.11 2009/08/12 05:29:40 dholland Exp $";
+#endif
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "bog.h"
+
+static char *nextword(FILE *, char *, int *, int *);
+
+int
+main(void)
+{
+ int clen, rlen, prev, i;
+ long off, start;
+ char buf[MAXWORDLEN + 1];
+
+ prev = '\0';
+ off = start = 0L;
+ while (nextword(stdin, buf, &clen, &rlen) != NULL) {
+ if (*buf != prev) {
+ /*
+ * Boggle expects a full index even if the dictionary
+ * had no words beginning with some letters.
+ * So we write out entries for every letter from prev
+ * to *buf.
+ */
+ if (prev != '\0')
+ printf("%c %6ld %6ld\n", prev, start, off - 1);
+ for (i = (prev ? prev + 1 : 'a'); i < *buf; i++)
+ printf("%c %6ld %6ld\n", i, off, off - 1);
+ prev = *buf;
+ start = off;
+ }
+ off += clen + 1;
+ }
+ printf("%c %6ld %6ld\n", prev, start, off - 1);
+ for (i = prev + 1; i <= 'z'; i++)
+ printf("%c %6ld %6ld\n", i, off, off - 1);
+ fflush(stdout);
+ if (ferror(stdout)) {
+ perror("error writing standard output");
+ exit(1);
+ }
+ exit(0);
+}
+
+/*
+ * Return the next word in the compressed dictionary in 'buffer' or
+ * NULL on end-of-file
+ * Also set clen to the length of the compressed word (for mkindex) and
+ * rlen to the strlen() of the real word
+ */
+static char *
+nextword(FILE *fp, char *buffer, int *clen, int *rlen)
+{
+ int ch, pcount;
+ char *p, *q;
+ static char buf[MAXWORDLEN + 1];
+ static int first = 1;
+ static int lastch = 0;
+
+ if (first) {
+ if ((pcount = getc(fp)) == EOF)
+ return (NULL);
+ first = 0;
+ }
+ else if ((pcount = lastch) == EOF)
+ return (NULL);
+
+ p = buf + (*clen = pcount);
+
+ while ((ch = getc(fp)) != EOF && ch >= 'a')
+ *p++ = ch;
+ lastch = ch;
+ *p = '\0';
+
+ *rlen = (int) (p - buf);
+ *clen = *rlen - *clen;
+
+ p = buf;
+ q = buffer;
+ while ((*q++ = *p) != '\0') {
+ if (*p++ == 'q')
+ *q++ = 'u';
+ }
+ return (buffer);
+}
diff --git a/bs/Makefile b/bs/Makefile
new file mode 100644
index 0000000..b7d56ae
--- /dev/null
+++ b/bs/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD: src/games/bs/Makefile,v 1.5.2.1 2001/04/25 09:28:49 ru Exp $
+# $DragonFly: src/games/bs/Makefile,v 1.4 2006/10/08 16:22:35 pavalos Exp $
+
+PROG= bs
+MAN= bs.6
+DPADD= ${LIBNCURSES}
+LDADD= -lncurses
+HIDEGAME=hidegame
+
+.include <bsd.prog.mk>
diff --git a/bs/bs.6 b/bs/bs.6
new file mode 100644
index 0000000..65f61b3
--- /dev/null
+++ b/bs/bs.6
@@ -0,0 +1,76 @@
+.\" $FreeBSD: src/games/bs/bs.6,v 1.1.1.1.12.1 2001/07/22 11:32:10 dd Exp $
+.Dd August 23, 1989
+.Dt BS 6
+.Os
+.Sh NAME
+.Nm bs
+.Nd battleships game
+.Sh SYNOPSIS
+.Nm
+.Op Fl bsc
+.Sh DESCRIPTION
+This program allows you to play the familiar Battleships game against the
+computer on a 10x10 board.
+The interface is visual and largely self-explanatory;
+you place your ships and pick your shots by moving the
+cursor around the
+.Sq sea
+with the
+.Xr rogue 6
+/
+.Xr hack 6
+motion keys
+.Dq hjklyubn .
+.Pp
+Note that when selecting a ship to place, you must type the capital letter
+(these are, after all, capital ships).
+During ship placement, the `r' command may be used to ignore the current
+position and randomly place your currently selected ship.
+The `R' command will place all remaining ships randomly.
+The \&^L command (form feed, ASCII 12) will force a screen redraw).
+.Pp
+The command-line arguments control game modes.
+.Bl -tag -width ".Fl b"
+.It Fl b
+selects a
+.Dq blitz
+variant
+.It Fl s
+selects a
+.Dq salvo
+variant
+.It Fl c
+permits ships to be placed adjacently
+.El
+.Pp
+The
+.Dq blitz
+variant allows a side to shoot for as long as it continues to score hits.
+.Pp
+The
+.Dq salvo
+game allows a player one shot per turn for each of his/her ships still afloat.
+This puts a premium scoring hits early and knocking out some
+ships and also makes much harder the situation where you face a superior force
+with only your PT-boat.
+.Pp
+Normally, ships must be separated by at least one square of open water.
+The
+.Fl c
+option disables this check and allows them to close-pack.
+.Pp
+The algorithm the computer uses once it has found a ship to sink is provably
+optimal.
+The dispersion criterion for the random-fire algorithm may not be.
+.Sh AUTHORS
+.An -nosplit
+Originally written by one
+.An Bruce Holloway
+in 1986.
+Salvo mode added by
+.An Chuck A. DeGaul Aq cbosgd!cad .
+Visual user interface,
+.Sq closepack
+option, code rewrite and manual page by
+.An Eric S. Raymond Aq Mt esr@snark.thyrsus.com ,
+August 1989.
diff --git a/bs/bs.c b/bs/bs.c
new file mode 100644
index 0000000..712851a
--- /dev/null
+++ b/bs/bs.c
@@ -0,0 +1,1196 @@
+/*-
+ * bs.c - original author: Bruce Holloway
+ * salvo option by: Chuck A DeGaul
+ * with improved user interface, autoconfiguration and code cleanup
+ * by Eric S. Raymond <esr@snark.thyrsus.com>
+ * v1.2 with color support and minor portability fixes, November 1990
+ * v2.0 featuring strict ANSI/POSIX conformance, November 1993.
+ *
+ * $FreeBSD: src/games/bs/bs.c,v 1.9 2000/02/21 03:07:31 billf Exp $
+ * $DragonFly: src/games/bs/bs.c,v 1.7 2007/04/18 18:32:12 swildner Exp $
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <ncurses.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+/*
+ * Constants for tuning the random-fire algorithm. It prefers moves that
+ * diagonal-stripe the board with a stripe separation of srchstep. If
+ * no such preferred moves are found, srchstep is decremented.
+ */
+#define BEGINSTEP 3 /* initial value of srchstep */
+
+/* miscellaneous constants */
+#define SHIPTYPES 5
+#define OTHER (1-turn)
+#define PLAYER 0
+#define COMPUTER 1
+#define MARK_HIT 'H'
+#define MARK_MISS 'o'
+#define CTRLC '\003' /* used as terminate command */
+#define FF '\014' /* used as redraw command */
+
+/* coordinate handling */
+#define BWIDTH 10
+#define BDEPTH 10
+
+/* display symbols */
+#define SHOWHIT '*'
+#define SHOWSPLASH ' '
+#define IS_SHIP(c) isupper(c)
+
+/* how to position us on player board */
+#define PYBASE 3
+#define PXBASE 3
+#define PY(y) (PYBASE + (y))
+#define PX(x) (PXBASE + (x)*3)
+#define pgoto(y, x) move(PY(y), PX(x))
+
+/* how to position us on cpu board */
+#define CYBASE 3
+#define CXBASE 48
+#define CY(y) (CYBASE + (y))
+#define CX(x) (CXBASE + (x)*3)
+#define cgoto(y, x) move(CY(y), CX(x))
+
+#define ONBOARD(x, y) (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)
+
+/* other board locations */
+#define COLWIDTH 80
+#define PROMPTLINE 21 /* prompt line */
+#define SYBASE CYBASE + BDEPTH + 3 /* move key diagram */
+#define SXBASE 63
+#define MYBASE SYBASE - 1 /* diagram caption */
+#define MXBASE 64
+#define HYBASE SYBASE - 1 /* help area */
+#define HXBASE 0
+
+/* this will need to be changed if BWIDTH changes */
+static char numbers[] = " 0 1 2 3 4 5 6 7 8 9";
+
+static char carrier[] = "Aircraft Carrier";
+static char battle[] = "Battleship";
+static char sub[] = "Submarine";
+static char destroy[] = "Destroyer";
+static char ptboat[] = "PT Boat";
+
+static char name[40];
+static char dftname[] = "stranger";
+
+/* direction constants */
+enum directions { E, SE, S, SW, W, NW, N, NE };
+static int xincr[8] = {1, 1, 0, -1, -1, -1, 0, 1};
+static int yincr[8] = {0, 1, 1, 1, 0, -1, -1, -1};
+
+/* current ship position and direction */
+static int curx = (BWIDTH / 2);
+static int cury = (BDEPTH / 2);
+
+typedef struct {
+ char *name; /* name of the ship type */
+ char hits; /* how many times has this ship been hit? */
+ char symbol; /* symbol for game purposes */
+ char length; /* length of ship */
+ char x, y; /* coordinates of ship start point */
+ enum directions dir; /* direction of `bow' */
+ bool placed; /* has it been placed on the board? */
+}
+ship_t;
+
+ship_t plyship[SHIPTYPES] =
+{
+ { carrier, 0, 'A', 5, 0, 0, E, FALSE},
+ { battle, 0, 'B', 4, 0, 0, E, FALSE},
+ { destroy, 0, 'D', 3, 0, 0, E, FALSE},
+ { sub, 0, 'S', 3, 0, 0, E, FALSE},
+ { ptboat, 0, 'P', 2, 0, 0, E, FALSE},
+};
+
+ship_t cpuship[SHIPTYPES] =
+{
+ { carrier, 0, 'A', 5, 0, 0, E, FALSE},
+ { battle, 0, 'B', 4, 0, 0, E, FALSE},
+ { destroy, 0, 'D', 3, 0, 0, E, FALSE},
+ { sub, 0, 'S', 3, 0, 0, E, FALSE},
+ { ptboat, 0, 'P', 2, 0, 0, E, FALSE},
+};
+
+/* "Hits" board, and main board. */
+static char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH];
+
+static int turn; /* 0=player, 1=computer */
+static int plywon=0, cpuwon=0; /* How many games has each won? */
+
+static int salvo, blitz, closepack;
+
+#define PR addstr
+
+static void prompt(int, const char *, ...) __printflike(2, 3);
+static bool checkplace (int, ship_t *, int);
+static int getcoord (int);
+int playagain (void);
+
+/* end the game, either normally or due to signal */
+static void
+uninitgame(void)
+{
+ clear();
+ refresh();
+ resetterm();
+ echo();
+ endwin();
+ exit(0);
+}
+
+static void
+sighandler(__attribute__((unused)) int sig)
+{
+ uninitgame();
+}
+
+/* announce which game options are enabled */
+static void
+announceopts(void)
+{
+ printw("Playing %s game (", (salvo || blitz || closepack) ?
+ "optional" : "standard");
+
+ if (salvo)
+ printw("salvo, ");
+ else
+ printw("nosalvo, ");
+
+ if (blitz)
+ printw("blitz, ");
+ else
+ printw("noblitz, ");
+
+ if (closepack)
+ printw("closepack)");
+ else
+ printw("noclosepack)");
+}
+
+static void
+intro(void)
+{
+ char *tmpname;
+
+ srandomdev();
+
+ tmpname = getlogin();
+ signal(SIGINT,sighandler);
+ signal(SIGINT,sighandler);
+ signal(SIGIOT,sighandler); /* for assert(3) */
+ if(signal(SIGQUIT,SIG_IGN) != SIG_IGN)
+ signal(SIGQUIT,sighandler);
+
+ if(tmpname != '\0') {
+ strcpy(name,tmpname);
+ name[0] = toupper(name[0]);
+ } else {
+ strcpy(name,dftname);
+ }
+
+ initscr();
+#ifdef KEY_MIN
+ keypad(stdscr, TRUE);
+#endif /* KEY_MIN */
+ saveterm();
+ nonl();
+ cbreak();
+ noecho();
+
+#ifdef PENGUIN
+ clear();
+ mvaddstr(4,29,"Welcome to Battleship!");
+ move(8,0);
+ PR(" \\\n");
+ PR(" \\ \\ \\\n");
+ PR(" \\ \\ \\ \\ \\_____________\n");
+ PR(" \\ \\ \\_____________ \\ \\/ |\n");
+ PR(" \\ \\/ \\ \\/ |\n");
+ PR(" \\/ \\_____/ |__\n");
+ PR(" ________________/ |\n");
+ PR(" \\ S.S. Penguin |\n");
+ PR(" \\ /\n");
+ PR(" \\___________________________________________________/\n");
+
+ mvaddstr(22,27,"Hit any key to continue..."); refresh();
+ getch();
+#endif /* PENGUIN */
+
+#ifdef A_COLOR
+ start_color();
+
+ init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
+ init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
+ init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
+ init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
+ init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
+ init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
+ init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
+ init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
+#endif /* A_COLOR */
+
+}
+
+/* print a message at the prompt line */
+static void
+prompt(int n, const char *f, ...)
+{
+ char buf[COLWIDTH + 1];
+ va_list ap;
+
+ va_start(ap, f);
+ move(PROMPTLINE + n, 0);
+ clrtoeol();
+ vsnprintf(buf, COLWIDTH + 1, f, ap);
+ printw("%s", buf);
+ refresh();
+ va_end(ap);
+}
+
+static void
+error(const char *s)
+{
+ move(PROMPTLINE + 2, 0);
+ clrtoeol();
+ if (s) {
+ addstr(s);
+ beep();
+ }
+}
+
+static void
+placeship(int b, ship_t *ss, int vis)
+{
+ int l;
+
+ for(l = 0; l < ss->length; ++l) {
+ int newx = ss->x + l * xincr[ss->dir];
+ int newy = ss->y + l * yincr[ss->dir];
+
+ board[b][newx][newy] = ss->symbol;
+ if (vis) {
+ pgoto(newy, newx);
+ addch((chtype)ss->symbol);
+ }
+ }
+ ss->hits = 0;
+}
+
+static int
+rnd(int n)
+{
+ return(random() % n);
+}
+
+/* generate a valid random ship placement into px,py */
+static void
+randomplace(int b, ship_t *ss)
+{
+ do {
+ ss->y = rnd(BDEPTH);
+ ss->x = rnd(BWIDTH);
+ ss->dir = rnd(2) ? E : S;
+ } while (!checkplace(b, ss, FALSE));
+}
+
+static void
+initgame(void)
+{
+ int i, j, unplaced;
+ ship_t *ss;
+
+ clear();
+ mvaddstr(0,35,"BATTLESHIPS");
+ move(PROMPTLINE + 2, 0);
+ announceopts();
+
+ bzero(board, sizeof(char) * BWIDTH * BDEPTH * 2);
+ bzero(hits, sizeof(char) * BWIDTH * BDEPTH * 2);
+ for (i = 0; i < SHIPTYPES; i++) {
+ ss = cpuship + i;
+ ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0;
+ ss = plyship + i;
+ ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0;
+ }
+
+ /* draw empty boards */
+ mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board");
+ mvaddstr(PYBASE - 1, PXBASE - 3,numbers);
+ for(i=0; i < BDEPTH; ++i)
+ {
+ mvaddch(PYBASE + i, PXBASE - 3, i + 'A');
+#ifdef A_COLOR
+ if (has_colors())
+ attron(COLOR_PAIR(COLOR_BLUE));
+#endif /* A_COLOR */
+ addch(' ');
+ for (j = 0; j < BWIDTH; j++)
+ addstr(" . ");
+#ifdef A_COLOR
+ attrset(0);
+#endif /* A_COLOR */
+ addch(' ');
+ addch(i + 'A');
+ }
+ mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers);
+ mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board");
+ mvaddstr(CYBASE - 1, CXBASE - 3, numbers);
+ for(i=0; i < BDEPTH; ++i)
+ {
+ mvaddch(CYBASE + i, CXBASE - 3, i + 'A');
+#ifdef A_COLOR
+ if (has_colors())
+ attron(COLOR_PAIR(COLOR_BLUE));
+#endif /* A_COLOR */
+ addch(' ');
+ for (j = 0; j < BWIDTH; j++)
+ addstr(" . ");
+#ifdef A_COLOR
+ attrset(0);
+#endif /* A_COLOR */
+ addch(' ');
+ addch(i + 'A');
+ }
+
+ mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers);
+
+ mvprintw(HYBASE, HXBASE,
+ "To position your ships: move the cursor to a spot, then");
+ mvprintw(HYBASE+1,HXBASE,
+ "type the first letter of a ship type to select it, then");
+ mvprintw(HYBASE+2,HXBASE,
+ "type a direction ([hjkl] or [4862]), indicating how the");
+ mvprintw(HYBASE+3,HXBASE,
+ "ship should be pointed. You may also type a ship letter");
+ mvprintw(HYBASE+4,HXBASE,
+ "followed by `r' to position it randomly, or type `R' to");
+ mvprintw(HYBASE+5,HXBASE,
+ "place all remaining ships randomly.");
+
+ mvaddstr(MYBASE, MXBASE, "Aiming keys:");
+ mvaddstr(SYBASE, SXBASE, "y k u 7 8 9");
+ mvaddstr(SYBASE+1, SXBASE, " \\|/ \\|/ ");
+ mvaddstr(SYBASE+2, SXBASE, "h-+-l 4-+-6");
+ mvaddstr(SYBASE+3, SXBASE, " /|\\ /|\\ ");
+ mvaddstr(SYBASE+4, SXBASE, "b j n 1 2 3");
+
+ /* have the computer place ships */
+ for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
+ {
+ randomplace(COMPUTER, ss);
+ placeship(COMPUTER, ss, FALSE);
+ }
+
+ ss = NULL;
+ do {
+ char c, docked[SHIPTYPES + 2], *cp = docked;
+
+ /* figure which ships still wait to be placed */
+ *cp++ = 'R';
+ for (i = 0; i < SHIPTYPES; i++)
+ if (!plyship[i].placed)
+ *cp++ = plyship[i].symbol;
+ *cp = '\0';
+
+ /* get a command letter */
+ prompt(1, "Type one of [%s] to pick a ship.", docked+1);
+ do {
+ c = getcoord(PLAYER);
+ } while
+ (!strchr(docked, c));
+
+ if (c == 'R')
+ ungetch('R');
+ else
+ {
+ /* map that into the corresponding symbol */
+ for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
+ if (ss->symbol == c)
+ break;
+
+ prompt(1, "Type one of [hjklrR] to place your %s.", ss->name);
+ pgoto(cury, curx);
+ }
+
+ do {
+ c = getch();
+ } while
+ (!strchr("hjklrR", c) || c == FF);
+
+ if (c == FF)
+ {
+ clearok(stdscr, TRUE);
+ refresh();
+ }
+ else if (c == 'r')
+ {
+ prompt(1, "Random-placing your %s", ss->name);
+ randomplace(PLAYER, ss);
+ placeship(PLAYER, ss, TRUE);
+ error(NULL);
+ ss->placed = TRUE;
+ }
+ else if (c == 'R')
+ {
+ prompt(1, "Placing the rest of your fleet at random...");
+ for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
+ if (!ss->placed)
+ {
+ randomplace(PLAYER, ss);
+ placeship(PLAYER, ss, TRUE);
+ ss->placed = TRUE;
+ }
+ error(NULL);
+ }
+ else if (strchr("hjkl8462", c))
+ {
+ ss->x = curx;
+ ss->y = cury;
+
+ switch(c)
+ {
+ case 'k': case '8': ss->dir = N; break;
+ case 'j': case '2': ss->dir = S; break;
+ case 'h': case '4': ss->dir = W; break;
+ case 'l': case '6': ss->dir = E; break;
+ }
+
+ if (checkplace(PLAYER, ss, TRUE))
+ {
+ placeship(PLAYER, ss, TRUE);
+ error(NULL);
+ ss->placed = TRUE;
+ }
+ }
+
+ for (unplaced = i = 0; i < SHIPTYPES; i++)
+ unplaced += !plyship[i].placed;
+ } while
+ (unplaced);
+
+ turn = rnd(2);
+
+ mvprintw(HYBASE, HXBASE,
+ "To fire, move the cursor to your chosen aiming point ");
+ mvprintw(HYBASE+1, HXBASE,
+ "and strike any key other than a motion key. ");
+ mvprintw(HYBASE+2, HXBASE,
+ " ");
+ mvprintw(HYBASE+3, HXBASE,
+ " ");
+ mvprintw(HYBASE+4, HXBASE,
+ " ");
+ mvprintw(HYBASE+5, HXBASE,
+ " ");
+
+ prompt(0, "Press any key to start...");
+ getch();
+}
+
+static int
+getcoord(int atcpu)
+{
+ int ny, nx, c;
+
+ if (atcpu)
+ cgoto(cury,curx);
+ else
+ pgoto(cury, curx);
+ refresh();
+ for (;;)
+ {
+ if (atcpu)
+ {
+ mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury);
+ cgoto(cury, curx);
+ }
+ else
+ {
+ mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury);
+ pgoto(cury, curx);
+ }
+
+ switch(c = getch())
+ {
+ case 'k': case '8':
+#ifdef KEY_MIN
+ case KEY_UP:
+#endif /* KEY_MIN */
+ ny = cury+BDEPTH-1; nx = curx;
+ break;
+ case 'j': case '2':
+#ifdef KEY_MIN
+ case KEY_DOWN:
+#endif /* KEY_MIN */
+ ny = cury+1; nx = curx;
+ break;
+ case 'h': case '4':
+#ifdef KEY_MIN
+ case KEY_LEFT:
+#endif /* KEY_MIN */
+ ny = cury; nx = curx+BWIDTH-1;
+ break;
+ case 'l': case '6':
+#ifdef KEY_MIN
+ case KEY_RIGHT:
+#endif /* KEY_MIN */
+ ny = cury; nx = curx+1;
+ break;
+ case 'y': case '7':
+#ifdef KEY_MIN
+ case KEY_A1:
+#endif /* KEY_MIN */
+ ny = cury+BDEPTH-1; nx = curx+BWIDTH-1;
+ break;
+ case 'b': case '1':
+#ifdef KEY_MIN
+ case KEY_C1:
+#endif /* KEY_MIN */
+ ny = cury+1; nx = curx+BWIDTH-1;
+ break;
+ case 'u': case '9':
+#ifdef KEY_MIN
+ case KEY_A3:
+#endif /* KEY_MIN */
+ ny = cury+BDEPTH-1; nx = curx+1;
+ break;
+ case 'n': case '3':
+#ifdef KEY_MIN
+ case KEY_C3:
+#endif /* KEY_MIN */
+ ny = cury+1; nx = curx+1;
+ break;
+ case FF:
+ nx = curx; ny = cury;
+ clearok(stdscr, TRUE);
+ refresh();
+ break;
+ default:
+ if (atcpu)
+ mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, " ");
+ else
+ mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, " ");
+ return(c);
+ }
+
+ curx = nx % BWIDTH;
+ cury = ny % BDEPTH;
+ }
+}
+
+/* is this location on the selected zboard adjacent to a ship? */
+static int
+collidecheck(int b, int y, int x)
+{
+ int collide;
+
+ /* anything on the square */
+ if ((collide = IS_SHIP(board[b][x][y])) != 0)
+ return(collide);
+
+ /* anything on the neighbors */
+ if (!closepack) {
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ int xend, yend;
+
+ yend = y + yincr[i];
+ xend = x + xincr[i];
+ if (ONBOARD(xend, yend))
+ collide += IS_SHIP(board[b][xend][yend]);
+ }
+ }
+
+ return(collide);
+}
+
+static bool
+checkplace(int b, ship_t *ss, int vis)
+{
+ int l, xend, yend;
+
+ /* first, check for board edges */
+ xend = ss->x + (ss->length - 1) * xincr[ss->dir];
+ yend = ss->y + (ss->length - 1) * yincr[ss->dir];
+ if (!ONBOARD(xend, yend)) {
+ if (vis) {
+ switch(rnd(3)) {
+ case 0:
+ error("Ship is hanging from the edge of the world");
+ break;
+ case 1:
+ error("Try fitting it on the board");
+ break;
+ case 2:
+ error("Figure I won't find it if you put it there?");
+ break;
+ }
+ }
+ return(0);
+ }
+
+ for(l = 0; l < ss->length; ++l) {
+ if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir])) {
+ if (vis) {
+ switch(rnd(3)) {
+ case 0:
+ error("There's already a ship there");
+ break;
+ case 1:
+ error("Collision alert! Aaaaaagh!");
+ break;
+ case 2:
+ error("Er, Admiral, what about the other ship?");
+ break;
+ }
+ }
+ return(FALSE);
+ }
+ }
+ return(TRUE);
+}
+
+static int
+awinna(void)
+{
+ int i, j;
+ ship_t *ss;
+
+ for(i=0; i<2; ++i)
+ {
+ ss = (i) ? cpuship : plyship;
+ for(j=0; j < SHIPTYPES; ++j, ++ss)
+ if(ss->length > ss->hits)
+ break;
+ if (j == SHIPTYPES)
+ return(OTHER);
+ }
+ return(-1);
+}
+
+/* a hit on the targeted ship */
+static ship_t *
+hitship(int x, int y)
+{
+ ship_t *sb, *ss;
+ char sym;
+ int oldx, oldy;
+
+ getyx(stdscr, oldy, oldx);
+ sb = (turn) ? plyship : cpuship;
+ if(!(sym = board[OTHER][x][y]))
+ return(NULL);
+ for(ss = sb; ss < sb + SHIPTYPES; ++ss)
+ if(ss->symbol == sym)
+ {
+ if (++ss->hits < ss->length) { /* still afloat? */
+ return(NULL);
+ } else { /* sunk */
+ int i, j;
+
+ if (!closepack) {
+ for (j = -1; j <= 1; j++) {
+ int bx = ss->x + j * xincr[(ss->dir + 2) % 8];
+ int by = ss->y + j * yincr[(ss->dir + 2) % 8];
+
+ for (i = -1; i <= ss->length; ++i) {
+ int cx, cy;
+
+ cx = bx + i * xincr[ss->dir];
+ cy = by + i * yincr[ss->dir];
+ if (ONBOARD(cx, cy)) {
+ hits[turn][cx][cy] = MARK_MISS;
+ if (turn % 2 == PLAYER) {
+ cgoto(cy, cx);
+#ifdef A_COLOR
+ if (has_colors())
+ attron(COLOR_PAIR(COLOR_GREEN));
+#endif /* A_COLOR */
+
+ addch(MARK_MISS);
+#ifdef A_COLOR
+ attrset(0);
+#endif /* A_COLOR */
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < ss->length; ++i)
+ {
+ int dx = ss->x + i * xincr[ss->dir];
+ int dy = ss->y + i * yincr[ss->dir];
+
+ hits[turn][dx][dy] = ss->symbol;
+ if (turn % 2 == PLAYER)
+ {
+ cgoto(dy, dx);
+ addch(ss->symbol);
+ }
+ }
+
+ move(oldy, oldx);
+ return(ss);
+ }
+ }
+ move(oldy, oldx);
+ return(NULL);
+}
+
+static int
+plyturn(void)
+{
+ ship_t *ss;
+ bool hit;
+ char const *m;
+
+ m = NULL;
+ prompt(1, "Where do you want to shoot? ");
+ for (;;)
+ {
+ getcoord(COMPUTER);
+ if (hits[PLAYER][curx][cury])
+ {
+ prompt(1, "You shelled this spot already! Try again.");
+ beep();
+ }
+ else
+ break;
+ }
+ hit = IS_SHIP(board[COMPUTER][curx][cury]);
+ hits[PLAYER][curx][cury] = hit ? MARK_HIT : MARK_MISS;
+ cgoto(cury, curx);
+#ifdef A_COLOR
+ if (has_colors()) {
+ if (hit)
+ attron(COLOR_PAIR(COLOR_RED));
+ else
+ attron(COLOR_PAIR(COLOR_GREEN));
+ }
+#endif /* A_COLOR */
+ addch((chtype)hits[PLAYER][curx][cury]);
+#ifdef A_COLOR
+ attrset(0);
+#endif /* A_COLOR */
+
+ prompt(1, "You %s.", hit ? "scored a hit" : "missed");
+ if(hit && (ss = hitship(curx, cury)))
+ {
+ switch(rnd(5))
+ {
+ case 0:
+ m = " You sank my %s!";
+ break;
+ case 1:
+ m = " I have this sinking feeling about my %s....";
+ break;
+ case 2:
+ m = " My %s has gone to Davy Jones's locker!";
+ break;
+ case 3:
+ m = " Glub, glub -- my %s is headed for the bottom!";
+ break;
+ case 4:
+ m = " You'll pick up survivors from my %s, I hope...!";
+ break;
+ }
+ printw(m, ss->name);
+ beep();
+ return(awinna() == -1);
+ }
+ return(hit);
+}
+
+static int
+sgetc(const char *s)
+{
+ const char *s1;
+ int ch;
+
+ refresh();
+ for(;;) {
+ ch = getch();
+ if (islower(ch))
+ ch = toupper(ch);
+
+ if (ch == CTRLC)
+ uninitgame();
+
+ for (s1=s; *s1 && ch != *s1; ++s1)
+ continue;
+
+ if (*s1) {
+ addch((chtype)ch);
+ refresh();
+ return(ch);
+ }
+ }
+}
+
+/* random-fire routine -- implements simple diagonal-striping strategy */
+static void
+randomfire(int *px, int *py)
+{
+ static int turncount = 0;
+ static int srchstep = BEGINSTEP;
+ static int huntoffs; /* Offset on search strategy */
+ int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs;
+ int ypreferred[BWIDTH * BDEPTH], xpreferred[BWIDTH * BDEPTH], npref;
+ int x, y, i;
+
+ if (turncount++ == 0)
+ huntoffs = rnd(srchstep);
+
+ /* first, list all possible moves */
+ nposs = npref = 0;
+ for (x = 0; x < BWIDTH; x++)
+ for (y = 0; y < BDEPTH; y++)
+ if (!hits[COMPUTER][x][y])
+ {
+ xpossible[nposs] = x;
+ ypossible[nposs] = y;
+ nposs++;
+ if (((x+huntoffs) % srchstep) != (y % srchstep))
+ {
+ xpreferred[npref] = x;
+ ypreferred[npref] = y;
+ npref++;
+ }
+ }
+
+ if (npref)
+ {
+ i = rnd(npref);
+
+ *px = xpreferred[i];
+ *py = ypreferred[i];
+ }
+ else if (nposs)
+ {
+ i = rnd(nposs);
+
+ *px = xpossible[i];
+ *py = ypossible[i];
+
+ if (srchstep > 1)
+ --srchstep;
+ }
+ else
+ {
+ error("No moves possible?? Help!");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+
+#define S_MISS 0
+#define S_HIT 1
+#define S_SUNK -1
+
+/* fire away at given location */
+static bool
+cpufire(int x, int y)
+{
+ bool hit, sunk;
+ ship_t *ss;
+
+ ss = NULL;
+ hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS;
+ mvprintw(PROMPTLINE, 0,
+ "I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss");
+ ss = hitship(x, y);
+ sunk = hit && ss;
+ if (sunk)
+ printw(" I've sunk your %s", ss->name);
+ clrtoeol();
+
+ pgoto(y, x);
+#ifdef A_COLOR
+ if (has_colors()) {
+ if (hit)
+ attron(COLOR_PAIR(COLOR_RED));
+ else
+ attron(COLOR_PAIR(COLOR_GREEN));
+ }
+#endif /* A_COLOR */
+ addch((chtype)(hit ? SHOWHIT : SHOWSPLASH));
+#ifdef A_COLOR
+ attrset(0);
+#endif /* A_COLOR */
+
+ return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS);
+}
+
+/*
+ * This code implements a fairly irregular FSM, so please forgive the rampant
+ * unstructuredness below. The five labels are states which need to be held
+ * between computer turns.
+ */
+static bool
+cputurn(void)
+{
+#define POSSIBLE(x, y) (ONBOARD(x, y) && !hits[COMPUTER][x][y])
+#define RANDOM_FIRE 0
+#define RANDOM_HIT 1
+#define HUNT_DIRECT 2
+#define FIRST_PASS 3
+#define REVERSE_JUMP 4
+#define SECOND_PASS 5
+ static int next = RANDOM_FIRE;
+ static bool used[4];
+ static ship_t ts;
+ int navail, x, y, d, n, hit = S_MISS;
+
+ switch(next)
+ {
+ case RANDOM_FIRE: /* last shot was random and missed */
+ refire:
+ randomfire(&x, &y);
+ if (!(hit = cpufire(x, y)))
+ next = RANDOM_FIRE;
+ else
+ {
+ ts.x = x; ts.y = y;
+ ts.hits = 1;
+ next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT;
+ }
+ break;
+
+ case RANDOM_HIT: /* last shot was random and hit */
+ used[E/2] = used[S/2] = used[W/2] = used[N/2] = FALSE;
+ /* FALLTHROUGH */
+
+ case HUNT_DIRECT: /* last shot hit, we're looking for ship's long axis */
+ for (d = navail = 0; d < 4; d++)
+ {
+ x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2];
+ if (!used[d] && POSSIBLE(x, y))
+ navail++;
+ else
+ used[d] = TRUE;
+ }
+ if (navail == 0) /* no valid places for shots adjacent... */
+ goto refire; /* ...so we must random-fire */
+ else
+ {
+ for (d = 0, n = rnd(navail) + 1; n; n--)
+ while (used[d])
+ d++;
+
+ assert(d <= 4);
+
+ used[d] = FALSE;
+ x = ts.x + xincr[d*2];
+ y = ts.y + yincr[d*2];
+
+ assert(POSSIBLE(x, y));
+
+ if (!(hit = cpufire(x, y)))
+ next = HUNT_DIRECT;
+ else
+ {
+ ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++;
+ next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
+ }
+ }
+ break;
+
+ case FIRST_PASS: /* we have a start and a direction now */
+ x = ts.x + xincr[ts.dir];
+ y = ts.y + yincr[ts.dir];
+ if (POSSIBLE(x, y) && (hit = cpufire(x, y)))
+ {
+ ts.x = x; ts.y = y; ts.hits++;
+ next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
+ }
+ else
+ next = REVERSE_JUMP;
+ break;
+
+ case REVERSE_JUMP: /* nail down the ship's other end */
+ d = ts.dir + 4;
+ x = ts.x + ts.hits * xincr[d];
+ y = ts.y + ts.hits * yincr[d];
+ if (POSSIBLE(x, y) && (hit = cpufire(x, y)))
+ {
+ ts.x = x; ts.y = y; ts.dir = d; ts.hits++;
+ next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
+ }
+ else
+ next = RANDOM_FIRE;
+ break;
+
+ case SECOND_PASS: /* kill squares not caught on first pass */
+ x = ts.x + xincr[ts.dir];
+ y = ts.y + yincr[ts.dir];
+ if (POSSIBLE(x, y) && (hit = cpufire(x, y)))
+ {
+ ts.x = x; ts.y = y; ts.hits++;
+ next = (hit == S_SUNK) ? RANDOM_FIRE: SECOND_PASS;
+ break;
+ }
+ else
+ next = RANDOM_FIRE;
+ break;
+ }
+
+ /* check for continuation and/or winner */
+ if (salvo)
+ {
+ refresh();
+ sleep(1);
+ }
+ if (awinna() != -1)
+ return(FALSE);
+
+#ifdef DEBUG
+ mvprintw(PROMPTLINE + 2, 0,
+ "New state %d, x=%d, y=%d, d=%d",
+ next, x, y, d);
+#endif /* DEBUG */
+ return(hit);
+}
+
+int
+playagain(void)
+{
+ int j;
+ ship_t *ss;
+
+ for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++) {
+ for(j = 0; j < ss->length; j++) {
+ cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]);
+ addch((chtype)ss->symbol);
+ }
+ }
+
+ if(awinna()) {
+ ++cpuwon;
+ } else {
+ ++plywon;
+ }
+
+ j = 18 + strlen(name);
+ if(plywon >= 10) {
+ ++j;
+ } else if(cpuwon >= 10) {
+ ++j;
+ }
+
+ mvprintw(1,(COLWIDTH-j)/2,
+ "%s: %d Computer: %d",name,plywon,cpuwon);
+
+ prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? "
+ : "Going to give me a chance for revenge, %s [yn]? ",name);
+ return(sgetc("YN") == 'Y');
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: battle [-s | -b] [-c]\n");
+ fprintf(stderr, "\tWhere the options are:\n");
+ fprintf(stderr, "\t-s : salvo - One shot per ship in play\n");
+ fprintf(stderr, "\t-b : blitz - Fire until you miss\n");
+ fprintf(stderr, "\t-c : closepack - Ships may be adjacent\n");
+ fprintf(stderr, "Blitz and Salvo are mutually exclusive\n");
+ exit(1);
+}
+
+static int
+scount(int who)
+{
+ int i, shots;
+ ship_t *sp;
+
+ if (who)
+ sp = cpuship; /* count cpu shots */
+ else
+ sp = plyship; /* count player shots */
+
+ for (i=0, shots = 0; i < SHIPTYPES; i++, sp++)
+ {
+ if (sp->hits >= sp->length)
+ continue; /* dead ship */
+ else
+ shots++;
+ }
+ return(shots);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+
+ /* revoke */
+ setgid(getgid());
+
+ while ((ch = getopt(argc, argv, "bsc")) != -1) {
+ switch (ch) {
+ case 'b':
+ blitz = 1;
+ break;
+ case 's':
+ salvo = 1;
+ break;
+ case 'c':
+ closepack = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (blitz && salvo)
+ usage();
+
+ intro();
+
+ do {
+ initgame();
+ while(awinna() == -1) {
+ if (blitz) {
+ while(turn ? cputurn() : plyturn())
+ continue;
+ } else if (salvo) {
+ int i;
+
+ i = scount(turn);
+ while (i--) {
+ if (turn) {
+ if (cputurn() && awinna() != -1)
+ i = 0;
+ } else {
+ if (plyturn() && awinna() != -1)
+ i = 0;
+ }
+ }
+ } else { /* Normal game */
+ if(turn)
+ cputurn();
+ else
+ plyturn();
+ }
+ turn = OTHER;
+ }
+ } while (playagain());
+
+ uninitgame();
+ exit(0);
+}
diff --git a/cgram/Makefile b/cgram/Makefile
new file mode 100644
index 0000000..222a745
--- /dev/null
+++ b/cgram/Makefile
@@ -0,0 +1,11 @@
+# $NetBSD: Makefile,v 1.1 2013/08/04 05:42:47 dholland Exp $
+
+PROG=cgram
+DPADD=${LIBCURSES} ${LIBTERMINFO}
+# 20150209 bkw: remove -lterminfo
+LDADD=-lcurses
+SRCS=cgram.c
+MAN=cgram.6
+HIDEGAME=hidegame
+
+.include <bsd.prog.mk>
diff --git a/cgram/cgram.6 b/cgram/cgram.6
new file mode 100644
index 0000000..9f31580
--- /dev/null
+++ b/cgram/cgram.6
@@ -0,0 +1,65 @@
+.\" $NetBSD: cgram.6,v 1.2 2013/08/04 07:55:09 wiz Exp $
+.\"
+.\" Copyright (c) 2004, 2013 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by David A. Holland.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd August 3, 2013
+.Dt CGRAM 6
+.Os
+.Sh NAME
+.Nm cgram
+.Nd solve Sunday-paper cryptograms
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+is a curses-based widget for solving Sunday-paper-type cryptograms
+based on substitution ciphers.
+A random cleartext is chosen using
+.Xr fortune 6
+and a random substitution key is generated.
+.Pp
+The ciphertext is displayed.
+Typing a letter changes the key so that the letter under the cursor
+maps to the newly typed letter, and updates the display accordingly.
+Use Emacs-type cursor commands to move around.
+Enter a tilde
+.Pq ~
+to quit.
+Press asterisk
+.Pq *
+to enter an easier mode where correct letters are displayed in
+boldface.
+.Sh SEE ALSO
+.Xr caesar 6
+.Sh HISTORY
+.Nm
+was written circa 2004.
+It was imported into
+.Nx
+in 2013 and first appeared in
+.Nx 7.0 .
diff --git a/cgram/cgram.c b/cgram/cgram.c
new file mode 100644
index 0000000..76ea55f
--- /dev/null
+++ b/cgram/cgram.c
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <err.h>
+#include <assert.h>
+#include <curses.h>
+#include "pathnames.h"
+
+////////////////////////////////////////////////////////////
+
+static char *xstrdup(const char *s) {
+ char *ret;
+
+ ret = malloc(strlen(s) + 1);
+ if (ret == NULL) {
+ errx(1, "Out of memory");
+ }
+ strcpy(ret, s);
+ return ret;
+}
+
+////////////////////////////////////////////////////////////
+
+struct stringarray {
+ char **v;
+ int num;
+};
+
+static void stringarray_init(struct stringarray *a) {
+ a->v = NULL;
+ a->num = 0;
+}
+
+static void stringarray_cleanup(struct stringarray *a) {
+ free(a->v);
+}
+
+static void stringarray_add(struct stringarray *a, const char *s) {
+ a->v = realloc(a->v, (a->num + 1) * sizeof(a->v[0]));
+ if (a->v == NULL) {
+ errx(1, "Out of memory");
+ }
+ a->v[a->num] = xstrdup(s);
+ a->num++;
+}
+
+////////////////////////////////////////////////////////////
+
+static struct stringarray lines;
+static struct stringarray sollines;
+static bool hinting;
+static int scrolldown;
+static unsigned curx;
+static int cury;
+
+static void readquote(void) {
+ FILE *f = popen(_PATH_FORTUNE, "r");
+ if (!f) {
+ err(1, "%s", _PATH_FORTUNE);
+ }
+
+ char buf[128], buf2[8*sizeof(buf)];
+ while (fgets(buf, sizeof(buf), f)) {
+ char *s = strrchr(buf, '\n');
+ assert(s);
+ assert(strlen(s)==1);
+ *s = 0;
+
+ int i,j;
+ for (i=j=0; buf[i]; i++) {
+ if (buf[i]=='\t') {
+ buf2[j++] = ' ';
+ while (j%8) buf2[j++] = ' ';
+ }
+ else if (buf[i]=='\b') {
+ if (j>0) j--;
+ }
+ else {
+ buf2[j++] = buf[i];
+ }
+ }
+ buf2[j] = 0;
+
+ stringarray_add(&lines, buf2);
+ stringarray_add(&sollines, buf2);
+ }
+
+ pclose(f);
+}
+
+static void encode(void) {
+ int used[26];
+ for (int i=0; i<26; i++) used[i] = 0;
+
+ int key[26];
+ int keypos=0;
+ while (keypos < 26) {
+ int c = random()%26;
+ if (used[c]) continue;
+ key[keypos++] = c;
+ used[c] = 1;
+ }
+
+ for (int y=0; y<lines.num; y++) {
+ for (unsigned x=0; lines.v[y][x]; x++) {
+ if (islower((unsigned char)lines.v[y][x])) {
+ int q = lines.v[y][x]-'a';
+ lines.v[y][x] = 'a'+key[q];
+ }
+ if (isupper((unsigned char)lines.v[y][x])) {
+ int q = lines.v[y][x]-'A';
+ lines.v[y][x] = 'A'+key[q];
+ }
+ }
+ }
+}
+
+static int substitute(int ch) {
+ assert(cury>=0 && cury<lines.num);
+ if (curx >= strlen(lines.v[cury])) {
+ beep();
+ return -1;
+ }
+
+ int och = lines.v[cury][curx];
+ if (!isalpha((unsigned char)och)) {
+ beep();
+ return -1;
+ }
+
+ int loch = tolower((unsigned char)och);
+ int uoch = toupper((unsigned char)och);
+ int lch = tolower((unsigned char)ch);
+ int uch = toupper((unsigned char)ch);
+
+ for (int y=0; y<lines.num; y++) {
+ for (unsigned x=0; lines.v[y][x]; x++) {
+ if (lines.v[y][x]==loch) {
+ lines.v[y][x] = lch;
+ }
+ else if (lines.v[y][x]==uoch) {
+ lines.v[y][x] = uch;
+ }
+ else if (lines.v[y][x]==lch) {
+ lines.v[y][x] = loch;
+ }
+ else if (lines.v[y][x]==uch) {
+ lines.v[y][x] = uoch;
+ }
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+
+static void redraw(void) {
+ erase();
+ bool won = true;
+ for (int i=0; i<LINES-1; i++) {
+ move(i, 0);
+ int ln = i+scrolldown;
+ if (ln < lines.num) {
+ for (unsigned j=0; lines.v[i][j]; j++) {
+ int ch = lines.v[i][j];
+ if (ch != sollines.v[i][j] && isalpha((unsigned char)ch)) {
+ won = false;
+ }
+ bool bold=false;
+ if (hinting && ch==sollines.v[i][j] &&
+ isalpha((unsigned char)ch)) {
+ bold = true;
+ attron(A_BOLD);
+ }
+ addch(lines.v[i][j]);
+ if (bold) {
+ attroff(A_BOLD);
+ }
+ }
+ }
+ clrtoeol();
+ }
+
+ move(LINES-1, 0);
+ if (won) {
+ addstr("*solved* ");
+ }
+ addstr("~ to quit, * to cheat, ^pnfb to move");
+
+ move(LINES-1, 0);
+
+ move(cury-scrolldown, curx);
+
+ refresh();
+}
+
+static void opencurses(void) {
+ initscr();
+ cbreak();
+ noecho();
+}
+
+static void closecurses(void) {
+ endwin();
+}
+
+////////////////////////////////////////////////////////////
+
+static void loop(void) {
+ bool done=false;
+ while (!done) {
+ redraw();
+ int ch = getch();
+ switch (ch) {
+ case 1: /* ^A */
+ curx=0;
+ break;
+ case 2: /* ^B */
+ if (curx > 0) {
+ curx--;
+ }
+ else if (cury > 0) {
+ cury--;
+ curx = strlen(lines.v[cury]);
+ }
+ break;
+ case 5: /* ^E */
+ curx = strlen(lines.v[cury]);
+ break;
+ case 6: /* ^F */
+ if (curx < strlen(lines.v[cury])) {
+ curx++;
+ }
+ else if (cury < lines.num - 1) {
+ cury++;
+ curx = 0;
+ }
+ break;
+ case 12: /* ^L */
+ clear();
+ break;
+ case 14: /* ^N */
+ if (cury < lines.num-1) {
+ cury++;
+ }
+ if (curx > strlen(lines.v[cury])) {
+ curx = strlen(lines.v[cury]);
+ }
+ if (scrolldown < cury - (LINES-2)) {
+ scrolldown = cury - (LINES-2);
+ }
+ break;
+ case 16: /* ^P */
+ if (cury > 0) {
+ cury--;
+ }
+ if (curx > strlen(lines.v[cury])) {
+ curx = strlen(lines.v[cury]);
+ }
+ if (scrolldown > cury) {
+ scrolldown = cury;
+ }
+ break;
+ case '*':
+ hinting = !hinting;
+ break;
+ case '~':
+ done = true;
+ break;
+ default:
+ if (isalpha(ch)) {
+ if (!substitute(ch)) {
+ if (curx < strlen(lines.v[cury])) {
+ curx++;
+ }
+ if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
+ curx=0;
+ cury++;
+ }
+ }
+ }
+ else if (curx < strlen(lines.v[cury]) && ch==lines.v[cury][curx]) {
+ curx++;
+ if (curx==strlen(lines.v[cury]) && cury < lines.num-1) {
+ curx=0;
+ cury++;
+ }
+ }
+ else {
+ beep();
+ }
+ break;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+
+int main(void) {
+ stringarray_init(&lines);
+ stringarray_init(&sollines);
+ srandom(time(NULL));
+ readquote();
+ encode();
+ opencurses();
+
+ loop();
+
+ closecurses();
+ stringarray_cleanup(&sollines);
+ stringarray_cleanup(&lines);
+ return 0;
+}
diff --git a/cgram/pathnames.h b/cgram/pathnames.h
new file mode 100644
index 0000000..51da95c
--- /dev/null
+++ b/cgram/pathnames.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define _PATH_FORTUNE "/usr/games/fortune"
diff --git a/ching/Makefile b/ching/Makefile
new file mode 100644
index 0000000..ab7834f
--- /dev/null
+++ b/ching/Makefile
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.1 2005/06/30 13:30:33 perry Exp $
+
+SUBDIR= ching castching printching
+
+.include <bsd.subdir.mk>
diff --git a/ching/Makefile.inc b/ching/Makefile.inc
new file mode 100644
index 0000000..b4674a6
--- /dev/null
+++ b/ching/Makefile.inc
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile.inc,v 1.3 2013/08/11 03:27:02 dholland Exp $
+
+CPPFLAGS+=-I${.CURDIR}/../include
+BINDIR?=/usr/games
+WARNS?=5
diff --git a/ching/castching/Makefile b/ching/castching/Makefile
new file mode 100644
index 0000000..dd84fc9
--- /dev/null
+++ b/ching/castching/Makefile
@@ -0,0 +1,7 @@
+# $NetBSD: Makefile,v 1.1 2005/06/30 13:30:33 perry Exp $
+
+PROG= castching
+NOMAN= # defined
+BINDIR= /usr/libexec/ching
+
+.include <bsd.prog.mk>
diff --git a/ching/castching/castching.c b/ching/castching/castching.c
new file mode 100644
index 0000000..351037a
--- /dev/null
+++ b/ching/castching/castching.c
@@ -0,0 +1,135 @@
+/* $NetBSD: castching.c,v 1.3 2009/08/12 05:40:03 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guy Harris.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1988, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ching.cno.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: castching.c,v 1.3 2009/08/12 05:40:03 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * castching - Read a question, cast a change, and output the line
+ * values to the standard output for processing by "printching".
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include "ching.h"
+
+static time_t now; /* current time */
+
+static unsigned seed; /* seed for random number generator */
+
+static int getquest(void);
+static unsigned getrand(void);
+static unsigned getrnum(void);
+static char *change(void);
+
+static char string[6+1]; /* where the actual change string is put */
+
+static int table[2][2][2] = {
+ { { OYIN, YYANG,}, { YYANG, YYIN,} },
+ { { YYANG, YYIN,}, { YYIN, OYANG,} },
+};
+
+/*ARGSUSED*/
+int
+main(int argc, char **argv)
+{
+ time(&now);
+ /* randomize */
+ seed = (int)now + getquest() + getgid() + getuid() + getpid();
+ printf("%s\n", change());
+ exit(0);
+}
+
+/*
+ * Hash the question by adding all the characters together.
+ */
+static int
+getquest(void)
+{
+ int result;
+ int c;
+
+ result = 0;
+ while ((c = getchar()) != EOF)
+ result += c;
+ return(result);
+}
+
+/*
+ * Get a set of six lines making up a change.
+ */
+static char *
+change(void)
+{
+ int i;
+
+ for (i = 0; i < 6; i++)
+ string[i] = table[getrnum()&01][getrnum()&01][getrnum()&01] + '0';
+ string[i] = '\0';
+ return(string);
+}
+
+/*
+ * Get a number more random than what getrand() gives.
+ */
+static unsigned
+getrnum(void)
+{
+ return((getrand())>>(getrand()%17));
+}
+
+/*
+ * Get a random number.
+ */
+static unsigned
+getrand(void)
+{
+ return(seed = (seed*13077) + 6925);
+}
diff --git a/ching/ching/Makefile b/ching/ching/Makefile
new file mode 100644
index 0000000..85559c1
--- /dev/null
+++ b/ching/ching/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.2 2008/10/30 21:37:55 mrg Exp $
+
+SCRIPTS=ching.sh
+MAN= ching.6
+
+.include <bsd.own.mk>
+
+.if ${MKSHARE} != "no"
+FILES= hexagrams macros
+FILESDIR=/usr/share/games/ching
+.endif
+
+.include <bsd.prog.mk>
diff --git a/ching/ching/ching.6 b/ching/ching/ching.6
new file mode 100644
index 0000000..a1cd662
--- /dev/null
+++ b/ching/ching/ching.6
@@ -0,0 +1,154 @@
+.\" $NetBSD: ching.6,v 1.5 2005/07/05 08:48:47 wiz Exp $
+.\"
+.\" Copyright (c) Caldera International Inc. 2001-2002. 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 and documentation must retain the
+.\" above copyright notice, this list of conditions and the following
+.\" disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed or owned by Caldera
+.\" International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of
+.\" other contributors may be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+.\"
+.\"
+.\" Copyright (c) 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.
+.\"
+.\"
+.\" @(#)ching.6 8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt CHING 6
+.Os
+.Sh NAME
+.Nm ching
+.Nd the book of changes and other cookies
+.Sh SYNOPSIS
+.Nm
+.Op hexagram
+.Sh DESCRIPTION
+The
+.Em I Ching
+or
+.Em Book of Changes
+is an ancient Chinese oracle that has been in use for centuries
+as a source of wisdom and advice.
+.Pp
+The text of the
+.Em oracle
+(as it is sometimes known) consists of sixty-four
+.Em hexagrams ,
+each symbolized by a particular arrangement of six straight (\-\-\-)
+and broken (\-\ \-) lines. These lines have values ranging
+from six through nine, with the even values indicating the broken lines.
+.Pp
+Each hexagram consists of two major sections. The
+.Sy Judgement
+relates specifically to the matter at hand
+.Po e.g. ,
+.Dq \&It furthers one to have somewhere to go.
+.Pc
+while the
+.Sy Image
+describes the general attributes of the hexagram and how they apply
+to one's own life
+.Pq Dq Thus the superior man makes himself strong and untiring.
+.Pp
+When any of the lines have the values six or nine, they are moving
+lines; for each there is an appended judgement which becomes
+significant.
+Furthermore, the moving lines are inherently unstable
+and change into their opposites; a second hexagram (and thus an
+additional judgement) is formed.
+.Pp
+Normally, one consults the oracle by fixing the desired question
+firmly in mind and then casting a set of changes (lines)
+using yarrow\-stalks or tossed coins. The resulting hexagram
+will be the answer to the question.
+.Pp
+Using an algorithm suggested by S. C. Johnson, the
+.Ux
+.Em oracle
+simply reads a question from the standard input (up to an EOF) and
+hashes the individual characters in combination with the time of
+day, process id and any other magic numbers which happen to be
+lying around the system.
+The resulting value is used as the seed of a random
+number generator which drives a simulated coin\-toss divination.
+The answer is then piped through
+.Xr nroff 1
+for formatting and will appear on the standard output.
+.Pp
+For those who wish to remain steadfast in the old traditions, the
+oracle will also accept the results of a personal divination using,
+for example, coins.
+To do this, cast the change and then type the
+resulting line values as an argument.
+.Pp
+The impatient modern may prefer to settle for Chinese cookies; try
+.Xr fortune 6 .
+.Sh DIAGNOSTICS
+The great prince issues commands,
+.Pp
+Founds states, vests families with fiefs.
+.Pp
+Inferior people should not be employed.
+.Sh SEE ALSO
+It furthers one to see the great man.
+.Sh BUGS
+Waiting in the mud
+.Pp
+Brings about the arrival of the enemy.
+.Pp
+If one is not extremely careful,
+.Pp
+Somebody may come up from behind and strike him.
+.Pp
+Misfortune.
diff --git a/ching/ching/ching.sh b/ching/ching/ching.sh
new file mode 100644
index 0000000..45a0bf5
--- /dev/null
+++ b/ching/ching/ching.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# $NetBSD: ching.sh,v 1.1 2005/06/30 13:30:33 perry Exp $
+#
+# Copyright (c) Caldera International Inc. 2001-2002. 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 and documentation must retain the
+# above copyright notice, this list of conditions and the following
+# disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed or owned by Caldera
+# International, Inc.
+# 4. Neither the name of Caldera International, Inc. nor the names of
+# other contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+# INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+#
+#
+# Copyright (c) 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.
+#
+# @(#)ching.sh 8.1 (Berkeley) 5/31/93
+#
+
+SHARE=/usr/share/games/ching
+PROGS=/usr/libexec/ching
+
+case $1 in
+ [6-9]*) HEXAGRAM=$1; shift;;
+esac
+
+if [ -z "$HEXAGRAM" ]; then
+ HEXAGRAM=$($PROGS/castching)
+ echo
+fi
+
+$PROGS/printching $HEXAGRAM | nroff $SHARE/macros - | ${PAGER-more}
diff --git a/ching/ching/hexagrams b/ching/ching/hexagrams
new file mode 100644
index 0000000..3d55e99
--- /dev/null
+++ b/ching/ching/hexagrams
@@ -0,0 +1,2337 @@
+.\" $NetBSD: hexagrams,v 1.1 2005/06/30 13:30:33 perry Exp $
+.\"
+.\" Copyright (c) Caldera International Inc. 2001-2002. 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 and documentation must retain the
+.\" above copyright notice, this list of conditions and the following
+.\" disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed or owned by Caldera
+.\" International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of
+.\" other contributors may be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+.\"
+.\"
+.\" Copyright (c) 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.
+.\"
+.H 1 "Ch\'ien" "The Creative"
+.X 1 1
+.J
+The Creative works sublime success,
+Furthering through perseverance.
+.I
+The movement of heaven is full of power.
+Thus the superior man makes himself strong and untiring.
+.L 1 9
+Hidden dragon. Do not act.
+.L 2 9
+Dragon appearing in the field.
+It furthers one to see the great man.
+.L 3 9
+All day long the superior man is creatively active.
+At nightfall his mind is still beset with cares.
+Danger. No blame.
+.L 4 9
+Wavering flight over the depths.
+No blame.
+.L 5 9 G
+Flying dragon in the heavens.
+It furthers one to see the great man.
+.L 6 9
+Arrogant dragon will have cause to repent.
+.LA 9
+There appears a flight of dragons without heads.
+Good fortune.
+.H 2 "K\'un" "The Receptive"
+.X 8 8
+.J
+The Receptive brings about sublime success,
+Furthering through the perseverance of a mare.
+If the superior man undertakes something and tries to lead,
+He goes astray;
+But if he follows, he finds guidance.
+It is favorable to find friends in the west and south,
+To forego friends in the east and north.
+Quiet perseverance brings good fortune.
+.I
+The earth's condition is receptive devotion.
+Thus the superior man who has breadth of character
+Carries the outer world.
+.L 1 6
+When there is hoarfrost underfoot,
+Solid ice is not far off.
+.L 2 6 G
+Straight, square, great.
+Without purpose,
+Yet nothing remains unfurthered.
+.L 3 6
+Hidden lines.
+One is able to remain persevering.
+If by chance you are in the service of a king,
+Seek not works, but bring to completion.
+.L 4 6
+A tied-up sack. No blame, no praise.
+.L 5 6
+A yellow lower garment brings supreme good fortune.
+.L 6 6
+Dragons fight in the meadow.
+Their blood is black and yellow.
+.LA 6
+Lasting perseverance furthers.
+.H 3 "Chun" "Difficulty at the Beginning"
+.X 6 7
+.J
+Difficulty at the Beginning works supreme success,
+Furthering through perseverance.
+Nothing should be undertaken.
+It furthers one to appoint helpers.
+.I
+Clouds and thunder:
+The image of Difficulty at the Beginning.
+Thus the superior man
+Brings order out of confusion.
+.L 1 9 G
+Hesitation and hindrance.
+It furthers one to remain persevering.
+It furthers one to appoint helpers.
+.L 2 6
+Difficulties pile up.
+Horse and wagon part.
+He is not a robber;
+He wants to woo when the time comes.
+The maiden is chaste,
+She does not pledge herself.
+Ten years\(emthen she pledges herself.
+.L 3 6
+Whoever hunts deer without the forester
+Only loses his way in the forest.
+The superior man understands the signs of the time
+And prefers to desist.
+To go on brings humiliation.
+.L 4 6
+Horse and wagon part.
+Strive for union.
+To go brings good fortune.
+Everything acts to further.
+.L 5 9 G
+Difficulties in blessing.
+A little perseverance brings good fortune.
+Great perseverance brings misfortune.
+.L 6 6
+Horse and wagon part.
+Bloody tears flow.
+.H 4 "M\o'^e'ng" "Youthful Folly"
+.X 4 6
+.J
+Youthful Folly has success.
+It is not I who seek the young fool;
+The young fool seeks me.
+At the first oracle I inform him.
+If he asks two or three times, it is importunity.
+If he importunes, I give him no information.
+Perseverance furthers.
+.I
+A spring wells up at the foot of the mountain:
+The image of Youth.
+Thus the superior man fosters his character
+By thoroughness in all that he does.
+.L 1 6
+To make a fool develop
+It furthers one to apply discipline.
+The fetters should be removed.
+To go on in this way brings humiliation.
+.L 2 9 G
+To bear with fools in kindliness brings good fortune.
+To know how to take women
+Brings good fortune.
+The son is capable of taking charge of the household.
+.L 3 6
+Take not a maiden who, when she sees a man of bronze,
+Loses possession of herself.
+Nothing furthers.
+.L 4 6
+Entangled folly brings humiliation.
+.L 5 6 G
+Childlike folly brings good fortune.
+.L 6 9
+In punishing folly
+It does not further one
+To commit transgressions.
+The only thing that furthers
+Is to prevent transgressions.
+.H 5 "Hsu" "Waiting (Nourishment)"
+.X 6 1
+.J
+Waiting. If you are sincere,
+You have light and success.
+Perseverance brings good fortune.
+It furthers one to cross the great water.
+.I
+Clouds rise up to heaven:
+The image of Waiting.
+Thus the superior man eats and drinks,
+Is joyous and of good cheer.
+.L 1 9
+Waiting in the meadow.
+It furthers one to abide in what endures.
+No blame.
+.L 2 9
+Waiting on the sand.
+There is some gossip.
+The end brings good fortune.
+.L 3 9
+Waiting in the mud
+Brings about the arrival of the enemy.
+.L 4 6
+Waiting in blood.
+Get out of the pit.
+.L 5 9 G
+Waiting at meat and drink.
+Perseverance brings good fortune.
+.L 6 6
+One falls into the pit.
+Three uninvited guests arrive.
+Honor them, and in the end there will be good fortune.
+.H 6 "Sung" "Conflict"
+.X 1 6
+.J
+Conflict. You are sincere
+And are being obstructed.
+A cautious halt halfway brings good fortune.
+Going through to the end brings misfortune.
+It furthers one to see the great man.
+It does not further one to cross the great water.
+.I
+Heaven and water go their opposite ways:
+The image of Conflict.
+Thus in all his transactions the superior man
+Carefully considers the beginning.
+.L 1 6
+If one does not perpetuate the affair,
+There is a little gossip.
+In the end, good fortune comes.
+.L 2 9
+One cannot engage in conflict;
+One returns home, gives way.
+The people of his town,
+Three hundred households,
+Remain free of guilt.
+.L 3 6
+To nourish oneself on ancient virtue induces perseverance.
+Danger. In the end, good fortune comes.
+If by chance you are in the service of a king,
+Seek not works.
+.L 4 9
+One cannot engage in conflict.
+One turns back and submits to fate,
+Changes one's attitude,
+And finds peace in perseverance.
+Good fortune.
+.L 5 9 G
+To contend before him
+Brings supreme good fortune.
+.L 6 9
+Even if by chance a leather belt is bestowed on one,
+By the end of a morning
+It will have been snatched away three times.
+.H 7 "Shih" "The Army"
+.X 8 6
+.J
+The Army. The army needs perseverance
+And a strong man.
+Good fortune without blame.
+.I
+In the middle of the earth is water:
+The image of the Army.
+Thus the superior man increases his masses
+By generosity toward the people.
+.L 1 6
+An army must set forth in proper order.
+If the order is not good, misfortune threatens.
+.L 2 9 G
+In the midst of the army.
+Good fortune. No blame.
+The king bestows a triple decoration.
+.L 3 6
+Perchance the army carries corpses in the wagon.
+Misfortune.
+.L 4 6
+The army retreats. No blame.
+.L 5 6 G
+There is game in the field.
+It furthers one to catch it.
+Without blame.
+Let the eldest lead the army.
+The younger transports corpses;
+Then perseverance brings misfortune.
+.L 6 6
+The great prince issues commands,
+Founds states, vests families with fiefs.
+Inferior people should not be employed.
+.H 8 "Pi" "Holding Together [Union]"
+.X 6 8
+.J
+Holding Together brings good fortune.
+Inquire of the oracle once again
+Whether you possess sublimity, constancy, and perseverance;
+Then there is no blame.
+Those who are uncertain gradually join.
+Whoever comes too late
+Meets with misfortune.
+.I
+On the earth is water:
+The image of Holding Together.
+Thus the kings of antiquity
+Bestowed the different states as fiefs
+And cultivated friendly relations
+With the feudal lords.
+.L 1 6
+Hold to him in truth and loyalty;
+This is without blame.
+Truth, like a full earthen bowl:
+Thus in the end
+Good fortune comes from without.
+.L 2 6
+Hold to him inwardly.
+Perseverance brings good fortune.
+.L 3 6
+You hold together with the wrong people.
+.L 4 6
+Hold to him outwardly also.
+Perseverance brings good fortune.
+.L 5 9 G
+Manifestation of holding together.
+In the hunt the king uses beaters on three sides only
+And foregoes game that runs off in front.
+The citizens need no warning.
+Good fortune.
+.L 6 6
+He finds no head for holding together.
+Misfortune.
+.H 9 "Hsiao Ch\'u" "The Taming Power of the Small"
+.X 2 1
+.J
+The Taming Power of the Small
+Has success.
+Dense clouds, no rain from our western region.
+.I
+The wind drives across heaven:
+The image of the Taming Power of the Small.
+Thus the superior man
+Refines the outward aspect of his nature.
+.L 1 9
+Return to the way.
+How could there be blame in this?
+Good fortune.
+.L 2 9
+He allows himself to be drawn into returning.
+Good fortune.
+.L 3 9
+The spokes burst out of the wagon wheels.
+Man and wife roll their eyes.
+.L 4 6 C
+If you are sincere, blood vanishes and fear gives way.
+No blame.
+.L 5 9 G
+If you are sincere and loyally attached,
+You are rich in your neighbor.
+.L 6 9
+The rain comes, there is rest.
+This is due to the lasting effect of character.
+Perseverance brings the woman into danger.
+The moon is nearly full.
+If the superior man persists,
+Misfortune comes.
+.H 10 "Lu" "Treading [Conduct]"
+.X 1 5
+.J
+Treading. Treading upon the tail of the tiger.
+It does not bite the man. Success.
+.I
+Heaven above, the lake below:
+The image of Treading.
+Thus the superior man discriminates between high and low,
+And thereby fortifies the thinking of the people.
+.L 1 9
+Simple conduct. Progress without blame.
+.L 2 9
+Treading a smooth, level course.
+The perseverance of a dark man
+Brings good fortune.
+.L 3 6 C
+A one-eyed man is able to see,
+A lame man is able to tread.
+He treads on the tail of the tiger.
+The tiger bites the man.
+Misfortune.
+Thus does a warrior act on behalf of his great prince.
+.L 4 9
+He treads on the tail of the tiger.
+Caution and circumspection
+Lead ultimately to good fortune.
+.L 5 9 G
+Resolute conduct.
+Perseverance with awareness of danger.
+.L 6 9
+Look to your conduct and weigh the favorable signs.
+When everything is fulfilled, supreme good fortune comes.
+.H 11 "T\'ai" "Peace"
+.X 8 1
+.J
+Peace. The small departs,
+The great approaches.
+Good fortune. Success.
+.I
+Heaven and earth unite: the image of Peace.
+Thus the ruler
+Divides and completes the course of heaven and earth;
+He furthers and regulates the gifts of heaven and earth,
+And so aids the people.
+.L 1 9
+When ribbon grass is pulled up, the sod comes with it.
+Each according to his kind.
+Undertakings bring good fortune.
+.L 2 9 G
+Bearing with the uncultured in gentleness,
+Fording the river with resolution,
+Not neglecting what is distant,
+Not regarding one's companions:
+Thus one may manage to walk in the middle.
+.L 3 9
+No plain not followed by a slope.
+No going not followed by a return.
+He who remains persevering in danger
+Is without blame.
+Do not complain about this truth;
+Enjoy the good fortune you still possess.
+.L 4 6
+He flutters down, not boasting of his wealth,
+Together with his neighbor,
+Guileless and sincere.
+.L 5 6 G
+The sovereign I
+Gives his daughter in marriage.
+This brings blessing
+And supreme good fortune.
+.L 6 6
+The wall falls back into the moat.
+Use no army now.
+Make your commands known within your own town.
+Perseverance brings humiliation.
+.H 12 "P\'i" "Standstill [Stagnation]"
+.X 1 8
+.J
+Standstill. Evil people do not further
+The perseverance of the superior man.
+The great departs; the small approaches.
+.I
+Heaven and earth do not unite:
+The image of Standstill.
+Thus the superior man falls back upon his inner worth
+In order to escape the difficulties.
+He does not permit himself to be honored with revenue.
+.L 1 6
+When ribbon grass is pulled up, the sod comes with it.
+Each according to his kind.
+Perseverance brings good fortune and success.
+.L 2 6 C
+They bear and endure;
+This means good fortune for inferior people.
+The standstill serves to help the great man to attain success.
+.L 3 6
+They bear shame.
+.L 4 9
+He who acts at the command of the highest
+Remains without blame.
+Those of like mind partake of the blessing.
+.L 5 9 G
+Standstill is giving way.
+Good fortune for the great man.
+"What if it should fail, what if it should fail?"
+In this way he ties it to a cluster of mulberry shoots.
+.L 6 9
+The standstill comes to an end.
+First standstill, then good fortune.
+.H 13 "T\'ung J\o'^e'n" "Fellowship with Men"
+.X 1 3
+.J
+Fellowship with Men in the open.
+Success.
+It furthers one to cross the great water.
+The perseverance of the superior man furthers.
+.I
+Heaven together with fire:
+The image of Fellowship with Men.
+Thus the superior man organizes the clans
+And makes distinctions between things.
+.L 1 9
+Fellowship with men at the gate.
+No blame.
+.L 2 6 G
+Fellowship with men in the clan.
+Humiliation.
+.L 3 9
+He hides weapons in the thicket;
+He climbs the high hill in front of it.
+For three years he does not rise up.
+.L 4 9
+He climbs up on his wall; he cannot attack.
+Good fortune.
+.L 5 9 G
+Men bound in fellowship first weep and lament,
+But afterward they laugh.
+After great struggles they succeed in meeting.
+.L 6 9
+Fellowship with men in the meadow.
+No remorse.
+.H 14 "Ta Yu" "Possession in Great Measure"
+.X 3 1
+.J
+Possession in Great Measure.
+Supreme success.
+.I
+Fire in heaven above:
+The image of Possession in Great Measure.
+Thus the superior man curbs evil and furthers good,
+And thereby obeys the benevolent will of heaven.
+.L 1 9
+No relationship with what is harmful;
+There is no blame in this.
+If one remains conscious of difficulty,
+One remains without blame.
+.L 2 9
+A big wagon for loading.
+One may undertake something.
+No blame.
+.L 3 9
+A prince offers it to the Son of Heaven.
+A petty man cannot do this.
+.L 4 9
+He makes a difference
+Between himself and his neighbor.
+No blame.
+.L 5 6 G
+He whose truth is accessible, yet dignified,
+Has good fortune.
+.L 6 9
+He is blessed by heaven.
+Good fortune.
+Nothing that does not further.
+.H 15 "Ch\'ien" "Modesty"
+.X 8 4
+.J
+Modesty creates success.
+The superior man carries things through.
+.I
+Within the earth, a mountain:
+The image of Modesty.
+Thus the superior man reduces that which is too much,
+And augments that which is too little.
+He weighs things and makes them equal.
+.L 1 6
+A superior man modest about his modesty
+May cross the great water.
+Good fortune.
+.L 2 6
+Modesty that comes to expression.
+Perseverance brings good fortune.
+.L 3 9 G
+A superior man of modesty and merit
+Carries things to conclusion.
+Good fortune.
+.L 4 6
+Nothing that would not further modesty
+In movement.
+.L 5 6
+No boasting of wealth before one's neighbor.
+It is favorable to attack with force.
+Nothing that would not further.
+.L 6 6
+Modesty that comes to expression.
+It is favorable to set armies marching
+To chastise one's own city and one's country.
+.H 16 "Yu" "Enthusiasm"
+.X 7 8
+.J
+Enthusiasm. It furthers one to install helpers
+And to set armies marching.
+.I
+Thunder comes resounding out of the earth:
+The image of Enthusiasm.
+Thus the ancient kings made music
+In order to honor merit,
+And offered it with splendor
+To the Supreme Deity,
+Inviting their ancestors to be present.
+.L 1 6
+Enthusiasm that expresses itself
+Brings misfortune.
+.L 2 6
+Firm as a rock. Not a whole day.
+Perseverance brings good fortune.
+.L 3 6
+Enthusiasm that looks upward creates remorse.
+Hesitation brings remorse.
+.L 4 9 G
+The source of enthusiasm.
+He achieves great things.
+Doubt not.
+You gather friends around you
+As a hair clasp gathers the hair.
+.L 5 6
+Persistently ill, and still does not die.
+.L 6 6
+Deluded enthusiasm.
+But if after completion one changes,
+There is no blame.
+.H 17 "Sui" "Following"
+.X 5 7
+.J
+Following has supreme success.
+Perseverance furthers. No blame.
+.I
+Thunder in the middle of the lake:
+The image of Following.
+Thus the superior man at nightfall
+Goes indoors for rest and recuperation.
+.L 1 9 G
+The standard is changing.
+Perseverance brings good fortune.
+To go out of the door in company
+Produces deeds.
+.L 2 6
+If one clings to the little boy,
+One loses the strong man.
+.L 3 6
+If one clings to the strong man,
+One loses the little boy.
+Through following one finds what one seeks.
+It furthers one to remain persevering.
+.L 4 9
+Following creates success.
+Perseverance brings misfortune.
+To go one's way with sincerity brings clarity.
+How could there be blame in this?
+.L 5 9 G
+Sincere in the good. Good fortune.
+.L 6 6
+He meets with firm allegiance
+And is still further bound.
+The king introduces him
+To the Western Mountain.
+.H 18 "Ku" "Work on What Has Been Spoiled [Decay]"
+.X 4 2
+.J
+Work on What Has Been Spoiled
+Has supreme success.
+It furthers one to cross the great water.
+Before the starting point, three days.
+After the starting point, three days.
+.I
+The wind blows low on the mountain:
+The image of Decay.
+Thus the superior man stirs up the people
+And strengthens their spirit.
+.L 1 6
+Setting right what has been spoiled by the father.
+If there is a son,
+No blame rests upon the departed father.
+Danger. In the end good fortune.
+.L 2 9
+Setting right what has been spoiled by the mother.
+One must not be too persevering.
+.L 3 9
+Setting right what has been spoiled by the father.
+There will be little remorse. No great blame.
+.L 4 6
+Tolerating what has been spoiled by the father.
+In continuing one sees humiliation.
+.L 5 6 G
+Setting right what has been spoiled by the father.
+One meets with praise.
+.L 6 9
+He does not serve kings and princes,
+Sets himself higher goals.
+.H 19 "Lin" "Approach"
+.X 8 5
+.J
+Approach has supreme success.
+Perseverance furthers.
+When the eighth month comes,
+There will be misfortune.
+.I
+The earth above the lake:
+The image of Approach.
+Thus the superior man is inexhaustible
+In his will to teach,
+And without limits
+In his tolerance and protection of the people.
+.L 1 9 G
+Joint approach.
+Perseverance brings good fortune.
+.L 2 9 G
+Joint approach.
+Good fortune.
+Everything furthers.
+.L 3 6
+Comfortable approach.
+Nothing that would further.
+If one is induced to grieve over it,
+One becomes free of blame.
+.L 4 6
+Complete approach.
+No blame.
+.L 5 6
+Wise approach.
+This is right for a great prince.
+Good fortune.
+.L 6 6
+Greathearted approach.
+Good fortune. No blame.
+.H 20 "Kuan" "Contemplation (View)"
+.X 2 8
+.J
+Contemplation. The ablution has been made,
+But not yet the offering.
+Full of trust they look up to him.
+.I
+The wind blows over the earth:
+The image of Contemplation.
+Thus the kings of old visited the regions of the world,
+Contemplated the people,
+And gave them instruction.
+.L 1 6
+Boylike contemplation.
+For an inferior man, no blame.
+For a superior man, humiliation.
+.L 2 6
+Contemplation through the crack of the door.
+Furthering for the perseverance of a woman.
+.L 3 6
+Contemplation of my life
+Decides the choice
+Between advance and retreat.
+.L 4 6
+Contemplation of the light of the kingdom.
+It furthers one to exert influence as the guest of a king.
+.L 5 9 G
+Contemplation of my life.
+The superior man is without blame.
+.L 6 9 G
+Contemplation of his life.
+The superior man is without blame.
+.H 21 "Shih Ho" "Biting Through"
+.X 3 7
+.J
+Biting Through has success.
+It is favorable to let justice be administered.
+.I
+Thunder and lightning:
+The image of Biting Through.
+Thus the kings of former times made firm the laws
+Through clearly defined penalties.
+.L 1 9
+His feet are fastened in the stocks,
+So that his toes disappear.
+No blame.
+.L 2 6
+Bites through tender meat,
+So that his nose disappears.
+No blame.
+.L 3 6
+Bites on old dried meat
+And strikes on something poisonous.
+Slight humiliation. No blame.
+.L 4 9
+Bites on dried gristly meat.
+Receives metal arrows.
+It furthers one to be mindful of difficulties
+And to be persevering.
+Good fortune.
+.L 5 6 G
+Bites on dried lean meat.
+Receives yellow gold.
+Perseveringly aware of danger.
+No blame.
+.L 6 9
+His neck is fastened in the wooden cangue,
+So that his ears disappear.
+Misfortune.
+.H 22 "Pi" "Grace"
+.X 4 3
+.J
+Grace has success.
+In small matters
+It is favorable to undertake something.
+.I
+Fire at the foot of the mountain:
+The image of Grace.
+Thus does the superior man proceed
+When clearing up current affairs.
+But he dare not decide controversial issues in this way.
+.L 1 9
+He lends grace to his toes, leaves the carriage, and walks.
+.L 2 6 G
+Lends grace to the beard on his chin.
+.L 3 9
+Graceful and moist.
+Constant perseverance brings good fortune.
+.L 4 6
+Grace or simplicity?
+A white horse comes as if on wings.
+He is not a robber,
+He will woo at the right time.
+.L 5 6
+Grace in hills and gardens.
+The roll of silk is meager and small.
+Humiliation, but in the end good fortune.
+.L 6 9 G
+Simple grace. No blame.
+.H 23 "Po" "Splitting Apart"
+.X 4 8
+.J
+Splitting Apart. It does not further one
+To go anywhere.
+.I
+The mountain rests on the earth:
+The image of Splitting Apart.
+Thus those above can ensure their position
+Only by giving generously to those below.
+.L 1 6
+The leg of the bed is split.
+Those who persevere are destroyed.
+Misfortune.
+.L 2 6
+The bed is split at the edge.
+Those who persevere are destroyed.
+Misfortune.
+.L 3 6
+He splits with them. No blame.
+.L 4 6
+The bed is split up to the skin.
+Misfortune.
+.L 5 6
+A shoal of fishes. Favor comes through the court ladies.
+Everything acts to further.
+.L 6 9 G
+There is a large fruit still uneaten.
+The superior man receives a carriage.
+The house of the inferior man is split apart.
+.H 24 "Fu" "Return (The Turning Point)"
+.X 8 7
+.J
+Return. Success.
+Going out and coming in without error.
+Friends come without blame.
+To and fro goes the way.
+On the seventh day comes return.
+It furthers one to have somewhere to go.
+.I
+Thunder within the earth:
+The image of the Turning Point.
+Thus the kings of antiquity closed the passes
+At the time of solstice.
+Merchants and strangers did not go about,
+And the ruler
+Did not travel through the provinces.
+.L 1 9 G
+Return from a short distance.
+No need for remorse.
+Great good fortune.
+.L 2 6
+Quiet return. Good fortune.
+.L 3 6
+Repeated return. Danger. No blame.
+.L 4 6
+Walking in the midst of others,
+One returns alone.
+.L 5 6
+Noblehearted return. No remorse.
+.L 6 6
+Missing the return. Misfortune.
+Misfortune from within and without.
+If armies are set marching in this way,
+One will in the end suffer a great defeat,
+Disastrous for the ruler of the country.
+For ten years
+It will not be possible to attack again.
+.H 25 "Wu Wang" "Innocence (The Unexpected)"
+.X 1 7
+.J
+Innocence. Supreme success.
+Perseverance furthers.
+If someone is not as he should be,
+He has misfortune,
+And it does not further him
+To undertake anything.
+.I
+Under heaven thunder rolls:
+All things attain the natural state of innocence.
+Thus the kings of old,
+Rich in virtue, and in harmony with the time,
+Fostered and nourished all beings.
+.L 1 9 G
+Innocent behavior brings good fortune.
+.L 2 6
+If one does not count on the harvest while plowing,
+Nor on the use of the ground while clearing it,
+It furthers one to undertake something.
+.L 3 6
+Undeserved misfortune.
+The cow that was tethered by someone
+Is the wanderer's gain, the citizen's loss.
+.L 4 9
+He who can be persevering
+Remains without blame.
+.L 5 9 G
+Use no medicine in an illness
+Incurred through no fault of your own.
+It will pass of itself.
+.L 6 9
+Innocent action brings misfortune.
+Nothing furthers.
+.H 26 "Ta Ch\'u" "The Taming Power of the Great"
+.X 4 1
+.J
+The Taming Power of the Great.
+Perseverance furthers.
+Not eating at home brings good fortune.
+It furthers one to cross the great water.
+.I
+Heaven within the mountain:
+The image of the Taming Power of the Great.
+Thus the superior man acquaints himself with many sayings of antiquity
+And many deeds of the past,
+In order to strengthen his character thereby.
+.L 1 9
+Danger is at hand. It furthers one to desist.
+.L 2 9
+The axletrees are taken from the wagon.
+.L 3 9
+A good horse that follows others.
+Awareness of danger,
+With perseverance, furthers.
+Practice chariot driving and armed defense daily.
+It furthers one to have somewhere to go.
+.L 4 6
+The headboard of a young bull.
+Great good fortune.
+.L 5 6 G
+The tusk of a gelded boar.
+Good fortune.
+.L 6 9 G
+One attains the way of heaven. Success.
+.H 27 "I" "The Corners of the Mouth (Providing Nourishment)"
+.X 4 7
+.J
+The Corners of the Mouth.
+Perseverance brings good fortune.
+Pay heed to the providing of nourishment
+And to what a man seeks
+To fill his own mouth with.
+.I
+At the foot of the mountain, thunder:
+The image of Providing Nourishment.
+Thus the superior man is careful of his words
+And temperate in eating and drinking.
+.L 1 9
+You let your magic tortoise go,
+And look at me with the corners of your mouth drooping.
+Misfortune.
+.L 2 6
+Turning to the summit for nourishment,
+Deviating from the path
+To seek nourishment from the hill.
+Continuing to do this brings misfortune.
+.L 3 6
+Turning away from nourishment.
+Perseverance brings misfortune.
+Do not act thus for ten years.
+Nothing serves to further.
+.L 4 6
+Turning to the summit
+For provision of nourishment
+Brings good fortune.
+Spying about with sharp eyes
+Like a tiger with insatiable craving.
+No blame.
+.L 5 6 G
+Turning away from the path.
+To remain persevering brings good fortune.
+One should not cross the great water.
+.L 6 9 G
+The source of nourishment.
+Awareness of danger brings good fortune.
+It furthers one to cross the great water.
+.H 28 "Ta Kuo" "Preponderance of the Great"
+.X 5 2
+.J
+Preponderance of the Great.
+The ridgepole sags to the breaking point.
+It furthers one to have somewhere to go.
+Success.
+.I
+The lake rises above the trees:
+The image of Preponderance of the Great.
+Thus the superior man, when he stands alone,
+Is unconcerned,
+And if he has to renounce the world,
+He is undaunted.
+.L 1 6
+To spread white rushes underneath.
+No blame.
+.L 2 9 G
+A dry poplar sprouts at the root.
+An older man takes a young wife.
+Everything furthers.
+.L 3 9
+The ridgepole sags to the breaking point.
+Misfortune.
+.L 4 9 G
+The ridgepole is braced. Good fortune.
+If there are ulterior motives, it is humiliating.
+.L 5 9
+A withered poplar puts forth flowers.
+An older woman takes a husband.
+No blame. No praise.
+.L 6 6
+One must go through the water.
+It goes over one's head.
+Misfortune. No blame.
+.H 29 "K\'an" "The Abysmal (Water)"
+.X 6 6
+.J
+The Abysmal repeated.
+If you are sincere, you have success in your heart,
+And whatever you do succeeds.
+.I
+Water flows on uninterruptedly and reaches its goal:
+The image of the Abysmal repeated.
+Thus the superior man walks in lasting virtue
+And carries on the business of teaching.
+.L 1 6
+Repetition of the Abysmal.
+In the abyss one falls into a pit.
+Misfortune.
+.L 2 9 G
+The abyss is dangerous.
+One should strive to attain small things only.
+.L 3 6
+Forward and backward, abyss on abyss.
+In danger like this, pause at first and wait,
+Otherwise you will fall into a pit in the abyss.
+Do not act in this way.
+.L 4 6
+A jug of wine, a bowl of rice with it;
+Earthen vessels
+Simply handed in through the window.
+There is certainly no blame in this.
+.L 5 9 G
+The abyss is not filled to overflowing,
+It is filled only to the rim.
+No blame.
+.L 6 6
+Bound with cords and ropes,
+Shut in between thorn-hedged prison walls:
+For three years one does not find the way.
+Misfortune.
+.H 30 "Li" "The Clinging, Fire"
+.X 3 3
+.J
+The Clinging. Perseverance furthers.
+It brings success.
+Care of the cow brings good fortune.
+.I
+That which is bright rises twice:
+The image of Fire.
+Thus the great man, by perpetuating this brightness,
+Illumines the four quarters of the world.
+.L 1 9
+The footprints run crisscross.
+If one is seriously intent, no blame.
+.L 2 6 G
+Yellow light. Supreme good fortune.
+.L 3 9
+In the light of the setting sun,
+Men either beat the pot and sing
+Or loudly bewail the approach of old age.
+Misfortune.
+.L 4 9
+Its coming is sudden;
+It flames up, dies down, is thrown away.
+.L 5 6 G
+Tears in floods, sighing and lamenting.
+Good fortune.
+.L 6 9
+The king uses him to march forth and chastise.
+Then it is best to kill the leaders
+And take captive the followers. No blame.
+.H 31 "Hsien" "Influence (Wooing)"
+.X 5 4
+.J
+Influence. Success.
+Perseverance furthers.
+To take a maiden to wife brings good fortune.
+.I
+A lake on the mountain:
+The image of Influence.
+Thus the superior man encourages people to approach him
+By his readiness to receive them.
+.L 1 6
+The influence shows itself in the big toe.
+.L 2 6
+The influence shows itself in the calves of the legs.
+Misfortune.
+Tarrying brings good fortune.
+.L 3 9
+The influence shows itself in the thighs.
+Holds to that which follows it.
+To continue is humiliating.
+.L 4 9 G
+Perseverance brings good fortune.
+Remorse disappears.
+If a man is agitated in mind,
+And his thoughts go hither and thither,
+Only those friends
+On whom he fixes his conscious thoughts
+Will follow.
+.L 5 9 G
+The influence shows itself in the back of the neck.
+No remorse.
+.L 6 6
+The influence shows itself in the jaws, cheeks, and tongue.
+.H 32 "H\o'^e'ng" "Duration"
+.X 7 2
+.J
+Duration. Success. No blame.
+Perseverance furthers.
+It furthers one to have somewhere to go.
+.I
+Thunder and wind: the image of Duration.
+Thus the superior man stands firm
+And does not change his direction.
+.L 1 6
+Seeking duration too hastily brings misfortune persistently.
+Nothing that would further.
+.L 2 9 G
+Remorse disappears.
+.L 3 9
+He who does not give duration to his character
+Meets with disgrace.
+Persistent humiliation.
+.L 4 9
+No game in the field.
+.L 5 6
+Giving duration to one's character through perseverance.
+This is good fortune for a woman, misfortune for a man.
+.L 6 6
+Restlessness as an enduring condition brings misfortune.
+.H 33 "Tun" "Retreat"
+.X 1 4
+.J
+Retreat. Success.
+In what is small, perseverance furthers.
+.I
+Mountain under heaven: the image of Retreat.
+Thus the superior man keeps the inferior man at a distance,
+Not angrily but with reserve.
+.L 1 6 C
+At the tail in retreat. This is dangerous.
+One must not wish to undertake anything.
+.L 2 6 C
+He holds him fast with yellow oxhide.
+No one can tear him loose.
+.L 3 9
+A halted retreat
+Is nerve-wracking and dangerous.
+To retain people as men- and maidservants
+Brings good fortune.
+.L 4 9
+Voluntary retreat brings good fortune to the superior man
+And downfall to the inferior man.
+.L 5 9 G
+Friendly retreat. Perseverance brings good fortune.
+.L 6 9
+Cheerful retreat. Everything serves to further.
+.H 34 "Ta Chuang" "The Power of the Great"
+.X 7 1
+.J
+The Power of the Great. Perseverance furthers.
+.I
+Thunder in heaven above:
+The image of the Power of the Great.
+Thus the superior man does not tread upon paths
+That do not accord with established order.
+.L 1 9
+Power in the toes.
+Continuing brings misfortune.
+This is certainly true.
+.L 2 9
+Perseverance brings good fortune.
+.L 3 9
+The inferior man works through power.
+The superior man does not act thus.
+To continue is dangerous.
+A goat butts against a hedge
+And gets its horns entangled.
+.L 4 9 G
+Perseverance brings good fortune.
+Remorse disappears.
+The hedge opens; there is no entanglement.
+Power depends upon the axle of a big cart.
+.L 5 6
+Loses the goat with ease.
+No remorse.
+.L 6 6
+A goat butts against a hedge.
+It cannot go backward, it cannot go forward.
+Nothing serves to further.
+If one notes the difficulty, this brings good fortune.
+.H 35 "Chin" "Progress"
+.X 3 8
+.J
+Progress. The powerful prince
+Is honored with horses in large numbers.
+In a single day he is granted audience three times.
+.I
+The sun rises over the earth:
+The image of Progress.
+Thus the superior man himself
+Brightens his bright virtue.
+.L 1 6
+Progressing, but turned back.
+Perseverance brings good fortune.
+If one meets with no confidence, one should remain calm.
+No mistake.
+.L 2 6
+Progressing, but in sorrow.
+Perseverance brings good fortune.
+Then one obtains great happiness from one's ancestress.
+.L 3 6
+All are in accord. Remorse disappears.
+.L 4 9
+Progress like a hamster.
+Perseverance brings danger.
+.L 5 6 G
+Remorse disappears.
+Take not gain and loss to heart.
+Undertakings bring good fortune.
+Everything serves to further.
+.L 6 9
+Making progress with the horns is permissible
+Only for the purpose of punishing one's own city.
+To be conscious of danger brings good fortune.
+No blame.
+Perseverance brings humiliation.
+.H 36 "Ming I" "Darkening of the Light"
+.X 8 3
+.J
+Darkening of the Light. In adversity
+It furthers one to be persevering.
+.I
+The light has sunk into the earth:
+The image of Darkening of the Light.
+Thus does the superior man live with the great mass:
+He veils his light, yet still shines.
+.L 1 9
+Darkening of the light during flight.
+He lowers his wings.
+The superior man does not eat for three days
+On his wanderings.
+But he has somewhere to go.
+The host has occasion to gossip about him.
+.L 2 6 G
+Darkening of the light injures him in the left thigh.
+He gives aid with the strength of a horse.
+Good fortune.
+.L 3 9
+Darkening of the light during the hunt in the south.
+Their great leader is captured.
+One must not expect perseverance too soon.
+.L 4 6
+He penetrates the left side of the belly.
+One gets at the very heart of the darkening of the light,
+And leaves gate and courtyard.
+.L 5 6 G
+Darkening of the light as with Prince Chi.
+Perseverance furthers.
+.L 6 6 C
+Not light but darkness.
+First he climbed up to heaven,
+Then he plunged into the depths of the earth.
+.H 37 "Chia J\o'^e'n" "The Family [The Clan]"
+.X 2 3
+.J
+The Family. The perseverance of the woman furthers.
+.I
+Wind comes forth from fire:
+The image of the Family.
+Thus the superior man has substance in his words
+And duration in his way of life.
+.L 1 9
+Firm seclusion within the family.
+Remorse disappears.
+.L 2 6 G
+She should not follow her whims.
+She must attend within to the food.
+Perseverance brings good fortune.
+.L 3 9
+When tempers flare up in the family,
+Too great severity brings remorse.
+Good fortune nonetheless.
+When woman and child dally and laugh,
+It leads in the end to humiliation.
+.L 4 6
+She is the treasure of the house.
+Great good fortune.
+.L 5 9 G
+As a king he approaches his family.
+Fear not.
+Good fortune.
+.L 6 9
+His work commands respect.
+In the end good fortune comes.
+.H 38 "K\'uei" "Opposition"
+.X 3 5
+.J
+Opposition. In small matters, good fortune.
+.I
+Above, fire; below, the lake:
+The image of Opposition.
+Thus amid all fellowship
+The superior man retains his individuality.
+.L 1 9
+Remorse disappears.
+If you lose your horse, do not run after it;
+It will come back of its own accord.
+When you see evil people,
+Guard yourself against mistakes.
+.L 2 9 G
+One meets his lord in a narrow street.
+No blame.
+.L 3 6
+One sees the wagon dragged back,
+The oxen halted,
+A man's hair and nose cut off.
+Not a good beginning, but a good end.
+.L 4 9
+Isolated through opposition,
+One meets a like-minded man
+With whom one can associate in good faith.
+Despite the danger, no blame.
+.L 5 6 G
+Remorse disappears.
+The companion bites his way through the wrappings.
+If one goes to him,
+How could it be a mistake?
+.L 6 9
+Isolated through opposition,
+One sees one's companion as a pig covered with dirt,
+As a wagon full of devils.
+First one draws a bow against him,
+Then one lays the bow aside.
+He is not a robber; he will woo at the right time.
+As one goes, rain falls; then good fortune comes.
+.H 39 "Chien" "Obstruction"
+.X 6 4
+.J
+Obstruction. The southwest furthers.
+The northeast does not further.
+It furthers one to see the great man.
+Perseverance brings good fortune.
+.I
+Water on the mountain:
+The image of Obstruction.
+Thus the superior man turns his attention to himself
+And molds his character.
+.L 1 6
+Going leads to obstructions,
+Coming meets with praise.
+.L 2 6
+The king's servant is beset by obstruction upon obstruction,
+But it is not his own fault.
+.L 3 9
+Going leads to obstructions;
+Hence he comes back.
+.L 4 6
+Going leads to obstructions,
+Coming leads to union.
+.L 5 9 G
+In the midst of the greatest obstructions,
+Friends come.
+.L 6 6
+Going leads to obstructions,
+Coming leads to great good fortune.
+It furthers one to see the great man.
+.H 40 "Hsieh" "Deliverance"
+.X 7 6
+.J
+Deliverance. The southwest furthers.
+If there is no longer anything where one has to go,
+Return brings good fortune.
+If there is still something where one has to go,
+Hastening brings good fortune.
+.I
+Thunder and rain set in:
+The image of Deliverance.
+Thus the superior man pardons mistakes
+And forgives misdeeds.
+.L 1 6
+Without blame.
+.L 2 9 G
+One kills three foxes in the field
+And receives a yellow arrow.
+Perseverance brings good fortune.
+.L 3 6
+If a man carries a burden on his back
+And nonetheless rides in a carriage,
+He thereby encourages robbers to draw near.
+Perseverance leads to humiliation.
+.L 4 9
+Deliver yourself from your great toe.
+Then the companion comes,
+And him you can trust.
+.L 5 6 G
+If only the superior man can deliver himself,
+It brings good fortune.
+Thus he proves to inferior men that he is in earnest.
+.L 6 6
+The prince shoots at a hawk on a high wall.
+He kills it. Everything serves to further.
+.H 41 "Sun" "Decrease"
+.X 4 5
+.J
+Decrease combined with sincerity
+Brings about supreme good fortune
+Without blame.
+One may be persevering in this.
+It furthers one to undertake something.
+How is this to be carried out?
+One may use two small bowls for the sacrifice.
+.I
+At the foot of the mountain, the lake:
+The image of Decrease.
+Thus the superior man controls his anger
+And restrains his instincts.
+.L 1 9
+Going quickly when one's tasks are finished
+Is without blame.
+But one must reflect on how much one may decrease others.
+.L 2 9
+Perseverance furthers.
+To undertake something brings misfortune.
+Without decreasing oneself,
+One is able to bring increase to others.
+.L 3 6 C
+When three people journey together,
+Their number decreases by one.
+When one man journeys alone,
+He finds a companion.
+.L 4 6
+If a man decreases his faults,
+It makes the other hasten to come and rejoice.
+No blame.
+.L 5 6 G
+Someone does indeed increase him.
+Ten pairs of tortoises cannot oppose it.
+Supreme good fortune.
+.L 6 9 C
+If one is increased without depriving others,
+There is no blame.
+Perseverance brings good fortune.
+It furthers one to undertake something.
+One obtains servants
+But no longer has a separate home.
+.H 42 "I" "Increase"
+.X 2 7
+.J
+Increase. It furthers one
+To undertake something.
+It furthers one to cross the great water.
+.I
+Wind and thunder: the image of Increase.
+Thus the superior man:
+If he sees good, he imitates it;
+If he has faults, he rids himself of them.
+.L 1 9 C
+It furthers one to accomplish great deeds.
+Supreme good fortune. No blame.
+.L 2 6 G
+Someone does indeed increase him;
+Ten pairs of tortoises cannot oppose it.
+Constant perseverance brings good fortune.
+The king presents him before God.
+Good fortune.
+.L 3 6
+One is enriched through unfortunate events.
+No blame, if you are sincere
+And walk in the middle,
+And report with a seal to the prince.
+.L 4 6 C
+If you walk in the middle
+And report to the prince,
+He will follow.
+It furthers one to be used
+In the removal of the capital.
+.L 5 9 G
+If in truth you have a kind heart, ask not.
+Supreme good fortune.
+Truly, kindness will be recognized as your virtue.
+.L 6 9
+He brings increase to no one.
+Indeed, someone even strikes him.
+He does not keep his heart constantly steady.
+Misfortune.
+.H 43 "Kuai" "Break-through (Resoluteness)"
+.X 5 1
+.J
+Break-through. One must resolutely make the matter known
+At the court of the king.
+It must be announced truthfully. Danger.
+It is necessary to notify one's own city.
+It does not further to resort to arms.
+It furthers one to undertake something.
+.I
+The lake has risen up to heaven:
+The image of Break-through.
+Thus the superior man
+Dispenses riches downward
+And refrains from resting on his virtue.
+.L 1 9
+Mighty in the forward-striding toes.
+When one goes and is not equal to the task,
+One makes a mistake.
+.L 2 9
+A cry of alarm. Arms at evening and at night.
+Fear nothing.
+.L 3 9
+To be powerful in the cheekbones
+Brings misfortune.
+The superior man is firmly resolved.
+He walks alone and is caught in the rain.
+He is bespattered,
+And people murmur against him.
+No blame.
+.L 4 9
+There is no skin on his thighs,
+And walking comes hard.
+If a man were to let himself be led like a sheep,
+Remorse would disappear.
+But if these words are heard
+They will not be believed.
+.L 5 9 G
+In dealing with weeds,
+Firm resolution is necessary.
+Walking in the middle
+Remains free of blame.
+.L 6 6 C
+No cry.
+In the end misfortune comes.
+.H 44 "Kou" "Coming to Meet"
+.X 1 2
+.J
+Coming to Meet. The maiden is powerful.
+One should not marry such a maiden.
+.I
+Under heaven, wind:
+The image of Coming to Meet.
+Thus does the prince act when disseminating his commands
+And proclaiming them to the four quarters of heaven.
+.L 1 6 C
+It must be checked with a brake of bronze.
+Perseverance brings good fortune.
+If one lets it take its course, one experiences misfortune.
+Even a lean pig has it in him to rage around.
+.L 2 9 G
+There is a fish in the tank. No blame.
+Does not further guests.
+.L 3 9
+There is no skin on his thighs,
+And walking comes hard.
+If one is mindful of the danger,
+No great mistake is made.
+.L 4 9
+No fish in the tank.
+This leads to misfortune.
+.L 5 9 G
+A melon covered with willow leaves.
+Hidden lines.
+Then it drops down to one from heaven.
+.L 6 9
+He comes to meet with his horns.
+Humiliation. No blame.
+.H 45 "Ts\'ui" "Gathering Together [Massing]"
+.X 5 8
+.J
+Gathering Together. Success.
+The king approaches his temple.
+It furthers one to see the great man.
+This brings success. Perseverance furthers.
+To bring great offerings creates good fortune.
+It furthers one to undertake something.
+.I
+Over the earth, the lake:
+The image of Gathering Together.
+Thus the superior man renews his weapons
+In order to meet the unforseen.
+.L 1 6
+If you are sincere, but not to the end,
+There will sometimes be confusion, sometimes gathering together.
+If you call out,
+Then after one grasp of the hand you can laugh again.
+Regret not. Going is without blame.
+.L 2 6
+Letting oneself be drawn
+Brings good fortune and remains blameless.
+If one is sincere,
+It furthers one to bring even a small offering.
+.L 3 6
+Gathering together amid sighs.
+Nothing that would further.
+Going is without blame.
+Slight humiliation.
+.L 4 9 G
+Great good fortune. No blame.
+.L 5 9 G
+If in gathering together one has position,
+This brings no blame.
+If there are some who are not yet sincerely in the work,
+Sublime and enduring perseverance is needed.
+Then remorse disappears.
+.L 6 6
+Lamenting and sighing, floods of tears.
+No blame.
+.H 46 "Sh\o'^e'ng" "Pushing Upward"
+.X 8 2
+.J
+Pushing Upward has supreme success.
+One must see the great man.
+Fear not.
+Departure toward the south
+Brings good fortune.
+.I
+Within the earth, wood grows:
+The image of Pushing Upward.
+Thus the superior man of devoted character
+Heaps up small things
+In order to achieve something high and great.
+.L 1 6 C
+Pushing upward that meets with confidence
+Brings great good fortune.
+.L 2 9
+If one is sincere,
+It furthers one to bring even a small offering.
+No blame.
+.L 3 9
+One pushes upward into an empty city.
+.L 4 6
+The king offers him Mount Ch'i.
+Good fortune. No blame.
+.L 5 6 G
+Perseverance brings good fortune.
+One pushes upward by steps.
+.L 6 6
+Pushing upward in darkness.
+It furthers one
+To be unremittingly persevering.
+.H 47 "K\'un" "Oppression (Exhaustion)"
+.X 5 6
+.J
+Oppression. Success. Perseverance.
+The great man brings about good fortune.
+No blame.
+When one has something to say,
+It is not believed.
+.I
+There is no water in the lake:
+The image of Exhaustion.
+Thus the superior man stakes his life
+On following his will.
+.L 1 6
+One sits oppressed under a bare tree
+And strays into a gloomy valley.
+For three years one sees nothing.
+.L 2 9 G
+One is oppressed while at meat and drink.
+The man with the scarlet knee bands is just coming.
+It furthers one to offer sacrifice.
+To set forth brings misfortune.
+No blame.
+.L 3 6
+A man permits himself to be oppressed by stone,
+And leans on thorns and thistles.
+He enters his house and does not see his wife.
+Misfortune.
+.L 4 9
+He comes very quietly, oppressed in a golden carriage.
+Humiliation, but the end is reached.
+.L 5 9 G
+His nose and feet are cut off.
+Oppression at the hands of the man with the purple knee bands.
+Joy comes softly.
+It furthers one to make offerings and libations.
+.L 6 6
+He is oppressed by creeping vines.
+He moves uncertainly and says, "Movement brings remorse."
+If one feels remorse over this and makes a start,
+Good fortune comes.
+.H 48 "Ching" "The Well"
+.X 6 2
+.J
+The Well. The town may be changed,
+But the well cannot be changed.
+It neither decreases nor increases.
+They come and go and draw from the well.
+If one gets down almost to the water
+And the rope does not go all the way,
+Or the jug breaks, it brings misfortune.
+.I
+Water over wood: the image of the Well.
+Thus the superior man encourages the people at their work,
+And exhorts them to help one another.
+.L 1 6
+One does not drink the mud of the well.
+No animals come to an old well.
+.L 2 9
+At the wellhole one shoots fishes.
+The jug is broken and leaks.
+.L 3 9
+The well is cleaned, but no one drinks from it.
+This is my heart's sorrow,
+For one might draw from it.
+If the king were clear-minded,
+Good fortune might be enjoyed in common.
+.L 4 6
+The well is being lined. No blame.
+.L 5 9 G
+In the well there is a clear, cold spring
+From which one can drink.
+.L 6 6
+One draws from the well
+Without hindrance.
+It is dependable.
+Supreme good fortune.
+.H 49 "Ko" "Revolution (Molting)"
+.X 5 3
+.J
+Revolution. On your own day
+You are believed.
+Supreme success,
+Furthering through perseverance.
+Remorse disappears.
+.I
+Fire in the lake: the image of Revolution.
+Thus the superior man
+Sets the calendar in order
+And makes the seasons clear.
+.L 1 9
+Wrapped in the hide of a yellow cow.
+.L 2 6
+When one's own day comes, one may create revolution.
+Starting brings good fortune. No blame.
+.L 3 9
+Starting brings misfortune.
+Perseverance brings danger.
+When talk of revolution has gone the rounds three times,
+One may commit himself,
+And men will believe him.
+.L 4 9
+Remorse disappears. Men believe him.
+Changing the form of government brings good fortune.
+.L 5 9 G
+The great man changes like a tiger.
+Even before he questions the oracle
+He is believed.
+.L 6 6
+The superior man changes like a panther.
+The inferior man molts in the face.
+Starting brings misfortune.
+To remain persevering brings good fortune.
+.H 50 "Ting" "The Caldron"
+.X 3 2
+.J
+The Caldron. Supreme good fortune.
+Success.
+.I
+Fire over wood:
+The image of the Caldron.
+Thus the superior man consolidates his fate
+By making his position correct.
+.L 1 6
+A \fIting\fR with legs upturned.
+Furthers removal of stagnating stuff.
+One takes a concubine for the sake of her son.
+No blame.
+.L 2 9
+There is food in the \fIting\fR.
+My comrades are envious,
+But they cannot harm me.
+Good fortune.
+.L 3 9
+The handle of the \fIting\fR is altered.
+One is impeded in his way of life.
+The fat of the pheasant is not eaten.
+Once rain falls, remorse is spent.
+Good fortune comes in the end.
+.L 4 9
+The legs of the \fIting\fR are broken.
+The prince's meal is spilled
+And his person is soiled.
+Misfortune.
+.L 5 6 G
+The \fIting\fR has yellow handles, golden carrying rings.
+Perseverance furthers.
+.L 6 9 G
+The \fIting\fR has rings of jade.
+Great good fortune.
+Nothing that would not act to further.
+.H 51 "Ch\o'^e'n" "The Arousing (Shock, Thunder)"
+.X 7 7
+.J
+Shock brings success.
+Shock comes\(emoh, oh!
+Laughing words\(emha, ha!
+The shock terrifies for a hundred miles,
+And he does not let fall the sacrificial spoon and chalice.
+.I
+Thunder repeated: the image of Shock.
+Thus in fear and trembling
+The superior man sets his life in order
+And examines himself.
+.L 1 9 G
+Shock comes\(emoh, oh!
+Then follow laughing words\(emha, ha!
+Good fortune.
+.L 2 6
+Shock comes bringing danger.
+A hundred thousand times
+You lose your treasures
+And must climb the nine hills.
+Do not go in pursuit of them.
+After seven days you will get them back.
+.L 3 6
+Shock comes and makes one distraught.
+If shock spurs to action
+One remains free of misfortune.
+.L 4 9
+Shock is mired.
+.L 5 6
+Shock goes hither and thither.
+Danger.
+However, nothing at all is lost.
+Yet there are things to be done.
+.L 6 6
+Shock brings ruin and terrified gazing around.
+Going ahead brings misfortune.
+If it has not yet touched one's own body
+But has reached one's neighbor first,
+There is no blame.
+One's comrades have something to talk about.
+.H 52 "K\o'^e'n" "Keeping Still, Mountain"
+.X 4 4
+.J
+Keeping Still. Keeping his back still
+So that he no longer feels his body.
+He goes into his courtyard
+And does not see his people.
+No blame.
+.I
+Mountains standing close together:
+The image of Keeping Still.
+Thus the superior man
+Does not permit his thoughts
+To go beyond his situation.
+.L 1 6
+Keeping his toes still.
+No blame.
+Continued perseverance furthers.
+.L 2 6
+Keeping his calves still.
+He cannot rescue him whom he follows.
+His heart is not glad.
+.L 3 9
+Keeping his hips still.
+Making his sacrum stiff.
+Dangerous. The heart suffocates.
+.L 4 6
+Keeping his trunk still.
+No blame.
+.L 5 6
+Keeping his jaws still.
+The words have order.
+Remorse disappears.
+.L 6 9 G
+Noblehearted keeping still.
+Good fortune.
+.H 53 "Chien" "Development (Gradual Progress)"
+.X 2 4
+.J
+Development. The maiden
+Is given in marriage.
+Good fortune.
+Perseverance furthers.
+.I
+On the mountain, a tree:
+The image of Development.
+Thus the superior man abides in dignity and virtue,
+In order to improve the mores.
+.L 1 6
+The wild goose gradually draws near the shore.
+The young son is in danger.
+There is talk. No blame.
+.L 2 6 G
+The wild goose gradually draws near the cliff.
+Eating and drinking in peace and concord.
+Good fortune.
+.L 3 9
+The wild goose gradually draws near the plateau.
+The man goes forth and does not return.
+The woman carries a child but does not bring it forth.
+Misfortune.
+It furthers one to fight off robbers.
+.L 4 6
+The wild goose gradually draws near the tree.
+Perhaps it will find a flat branch. No blame.
+.L 5 9 G
+The wild goose gradually draws near the summit.
+For three years the woman has no child.
+In the end nothing can hinder her.
+Good fortune.
+.L 6 9
+The wild goose gradually draws near the cloud heights.
+Its feathers can be used for the sacred dance.
+Good fortune.
+.H 54 "Kuei Mei" "The Marrying Maiden"
+.X 7 5
+.J
+The Marrying Maiden.
+Undertakings bring misfortune.
+Nothing that would further.
+.I
+Thunder over the lake:
+The image of the Marrying Maiden.
+Thus the superior man
+Understands the transitory
+In the light of the eternity of the end.
+.L 1 9
+The marrying maiden as a concubine.
+A lame man who is able to tread.
+Undertakings bring good fortune.
+.L 2 9
+A one-eyed man who is able to see.
+The perseverance of a solitary man furthers.
+.L 3 6 C
+The marrying maiden as a slave.
+She marries as a concubine.
+.L 4 9
+The marrying maiden draws out the allotted time.
+A late marriage comes in due course.
+.L 5 6 G
+The sovereign I gave his daughter in marriage.
+The embroidered garments of the princess
+Were not as gorgeous
+As those of the servingmaid.
+The moon that is nearly full
+Brings good fortune.
+.L 6 6 C
+The woman holds the basket, but there are no fruits in it.
+The man stabs the sheep, but no blood flows.
+Nothing that acts to further.
+.H 55 "F\o'^e'ng" "Abundance [Fullness]"
+.X 7 3
+.J
+Abundance has success.
+The king attains abundance.
+Be not sad.
+Be like the sun at midday.
+.I
+Both thunder and lightning come:
+The image of Abundance.
+Thus the superior man decides lawsuits
+And carries out punishments.
+.L 1 9
+When a man meets his destined ruler,
+They can be together ten days,
+And it is not a mistake.
+Going meets with recognition.
+.L 2 6
+The curtain is of such fullness
+That the polestars can be seen at noon.
+Through going one meets with mistrust and hate.
+If one rouses him through truth,
+Good fortune comes.
+.L 3 9
+The underbrush is of such abundance
+That the small stars can be seen at noon.
+He breaks his right arm. No blame.
+.L 4 9
+The curtain is of such fullness
+That the polestars can be seen at noon.
+He meets his ruler, who is of like kind.
+Good fortune.
+.L 5 6 G
+Lines are coming,
+Blessing and fame draw near.
+Good fortune.
+.L 6 6
+His house is in a state of abundance.
+He screens off his family.
+He peers through the gate
+And no longer perceives anyone.
+For three years he sees nothing.
+Misfortune.
+.H 56 "Lu" "The Wanderer"
+.X 3 4
+.J
+The Wanderer. Success through smallness.
+Perseverance brings good fortune
+To the wanderer.
+.I
+Fire on the mountain:
+The image of the Wanderer.
+Thus the superior man
+Is clear-minded and cautious
+In imposing penalties,
+And protracts no lawsuits.
+.L 1 6
+If the wanderer busies himself with trivial things,
+He draws down misfortune upon himself.
+.L 2 6
+The wanderer comes to an inn.
+He has his property with him.
+He wins the steadfastness of a young servant.
+.L 3 9
+The wanderer's inn burns down.
+He loses the steadfastness of his young servant.
+Danger.
+.L 4 9
+The wanderer rests in a shelter.
+He obtains his property and an ax.
+My heart is not glad.
+.L 5 6 G
+He shoots a pheasant.
+It drops with the first arrow.
+In the end this brings both praise and office.
+.L 6 9
+The bird's nest burns up.
+The wanderer laughs at first,
+Then must needs lament and weep.
+Through carelessness he loses his cow.
+Misfortune.
+.H 57 "Sun" "The Gentle (The Penetrating, Wind)"
+.X 2 2
+.J
+The Gentle. Success through what is small.
+It furthers one to have somewhere to go.
+It furthers one to see the great man.
+.I
+Winds following one upon the other:
+The image of the Gently Penetrating.
+Thus the superior man
+Spreads his commands abroad
+And carries out his undertakings.
+.L 1 6 C
+In advancing and in retreating,
+The perseverance of a warrior furthers.
+.L 2 9
+Penetration under the bed.
+Priests and magicians are used in great number.
+Good fortune. No blame.
+.L 3 9
+Repeated penetration. Humiliation.
+.L 4 6 C
+Remorse vanishes.
+During the hunt
+Three kinds of game are caught.
+.L 5 9 G
+Perseverance brings good fortune.
+Remorse vanishes.
+Nothing that does not further.
+No beginning, but an end.
+Before the change, three days.
+After the change, three days.
+Good fortune.
+.L 6 9
+Penetration under the bed.
+He loses his property and his ax.
+Perseverance brings misfortune.
+.H 58 "Tui" "The Joyous, Lake"
+.X 5 5
+.J
+The Joyous. Success.
+Perseverance is favorable.
+.I
+Lakes resting one on the other:
+The image of the Joyous.
+Thus the superior man joins with his friends
+For discussion and practice.
+.L 1 9
+Contented joyousness. Good fortune.
+.L 2 9 G
+Sincere joyousness. Good fortune.
+Remorse disappears.
+.L 3 6 C
+Coming joyousness. Misfortune.
+.L 4 9
+Joyousness that is weighed is not at peace.
+After ridding himself of mistakes a man has joy.
+.L 5 9 G
+Sincerity toward disintegrating influences is dangerous.
+.L 6 6 C
+Seductive joyousness.
+.H 59 "Huan" "Dispersion [Dissolution]"
+.X 2 6
+.J
+Dispersion. Success.
+The king approaches his temple.
+It furthers one to cross the great water.
+Perseverance furthers.
+.I
+The wind drives over the water:
+The image of Dispersion.
+Thus the kings of old sacrificed to the Lord
+And built temples.
+.L 1 6
+He brings help with the strength of a horse.
+Good fortune.
+.L 2 9 C
+At the dissolution
+He hurries to that which supports him.
+Remorse disappears.
+.L 3 6
+He dissolves his self. No remorse.
+.L 4 6 C
+He dissolves his bond with his group.
+Supreme good fortune.
+Dispersion leads in turn to accumulation.
+This is something that ordinary men do not think of.
+.L 5 9 G
+His loud cries are as dissolving as sweat.
+Dissolution. A king abides without blame.
+.L 6 9
+He dissolves his blood.
+Departing, keeping at a distance, going out,
+Is without blame.
+.H 60 "Chieh" "Limitation"
+.X 6 5
+.J
+Limitation. Success.
+Galling limitation must not be persevered in.
+.I
+Water over lake: the image of Limitation.
+Thus the superior man
+Creates number and measure,
+And examines the nature of virtue and correct conduct.
+.L 1 9
+Not going out of the door and the courtyard
+Is without blame.
+.L 2 9
+Not going out of the gate and the courtyard
+Brings misfortune.
+.L 3 6
+He who knows no limitation
+Will have cause to lament.
+No blame.
+.L 4 6
+Contented limitation. Success.
+.L 5 9 G
+Sweet limitation brings good fortune.
+Going brings esteem.
+.L 6 6
+Galling limitation.
+Perseverance brings misfortune.
+Remorse disappears.
+.H 61 "Chung Fu" "Inner Truth"
+.X 2 5
+.J
+Inner Truth. Pigs and fishes.
+Good fortune.
+It furthers one to cross the great water.
+Perseverance furthers.
+.I
+Wind over lake: the image of Inner Truth.
+Thus the superior man discusses criminal cases
+In order to delay executions.
+.L 1 9
+Being prepared brings good fortune.
+If there are secret designs, it is disquieting.
+.L 2 9
+A crane calling in the shade.
+Its young answers it.
+I have a good goblet.
+I will share it with you.
+.L 3 6 C
+He finds a comrade.
+Now he beats the drum, now he stops.
+Now he sobs, now he sings.
+.L 4 6 C
+The moon nearly at the full.
+The team horse goes astray.
+No blame.
+.L 5 9 G
+He possesses truth, which links together.
+No blame.
+.L 6 9
+Cockcrow penetrating to heaven.
+Perseverance brings misfortune.
+.H 62 "Hsiao Kuo" "Preponderance of the Small"
+.X 7 4
+.J
+Preponderance of the Small. Success.
+Perseverance furthers.
+Small things may be done; great things should not be done.
+The flying bird brings the message:
+It is not well to strive upward,
+It is well to remain below.
+Great good fortune.
+.I
+Thunder on the mountain:
+The image of Preponderance of the Small.
+Thus in his conduct the superior man gives preponderance to reverence.
+In bereavement he gives preponderance to grief.
+In his expenditures he gives preponderance to thrift.
+.L 1 6
+The bird meets with misfortune through flying.
+.L 2 6 G
+She passes by her ancestor
+And meets her ancestress.
+He does not reach his prince
+And meets the official.
+No blame.
+.L 3 9
+If one is not extremely careful,
+Somebody may come up from behind and strike him.
+Misfortune.
+.L 4 9
+No blame. He meets him without passing by.
+Going brings danger. One must be on guard.
+Do not act. Be constantly persevering.
+.L 5 6 G
+Dense clouds,
+No rain from our western territory.
+The prince shoots and hits him who is in the cave.
+.L 6 6
+He passes him by, not meeting him.
+The flying bird leaves him.
+Misfortune.
+This means bad luck and injury.
+.H 63 "Chi Chi" "After Completion"
+.X 6 3
+.J
+After Completion. Success in small matters.
+Perseverance furthers.
+At the beginning good fortune,
+At the end disorder.
+.I
+Water over fire: the image of the condition
+In After Completion.
+Thus the superior man
+Takes thought of misfortune
+And arms himself against it in advance.
+.L 1 9
+He brakes his wheels.
+He gets his tail in the water.
+No blame.
+.L 2 6 G
+The woman loses the curtain of her carriage.
+Do not run after it;
+On the seventh day you will get it.
+.L 3 9
+The Illustrious Ancestor
+Disciplines the Devil's Country.
+After three years he conquers it.
+Inferior people must not be employed.
+.L 4 6
+The finest clothes turn to rags.
+Be careful all day long.
+.L 5 9
+The neighbor in the east who slaughters an ox
+Does not attain as much real happiness
+As the neighbor in the west
+With his small offering.
+.L 6 6
+He gets his head in the water. Danger.
+.H 64 "Wei Chi" "Before Completion"
+.X 3 6
+.J
+Before Completion. Success.
+But if the little fox, after nearly completing the crossing,
+Gets his tail in the water,
+There is nothing that would further.
+.I
+Fire over water:
+The image of the condition before transition.
+Thus the superior man is careful
+In the differentiation of things,
+So that each finds its place.
+.L 1 6
+He gets his tail in the water.
+Humiliating.
+.L 2 9
+He brakes his wheels.
+Perseverance brings good fortune.
+.L 3 6
+Before completion, attack brings misfortune.
+It furthers one to cross the great water.
+.L 4 9
+Perseverance brings good fortune.
+Remorse disappears.
+Shock, thus to discipline the Devil's Country.
+For three years, great realms are awarded.
+.L 5 6 G
+Perseverance brings good fortune.
+No remorse.
+The light of the superior man is true.
+Good fortune.
+.L 6 9
+There is drinking of wine
+In genuine confidence. No blame.
+But if one wets his head,
+He loses it, in truth.
diff --git a/ching/ching/macros b/ching/ching/macros
new file mode 100644
index 0000000..fa8a058
--- /dev/null
+++ b/ching/ching/macros
@@ -0,0 +1,126 @@
+.\" $NetBSD: macros,v 1.1 2005/06/30 13:30:33 perry Exp $
+.\"
+.\" Copyright (c) Caldera International Inc. 2001-2002. 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 and documentation must retain the
+.\" above copyright notice, this list of conditions and the following
+.\" disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed or owned by Caldera
+.\" International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of
+.\" other contributors may be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. 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 CALDERA INTERNATIONAL, INC. 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.
+.\"
+.ds N6 Six
+.ds N9 Nine
+.ds L1 at the beginning
+.ds L2 in the second place
+.ds L3 in the third place
+.ds L4 in the fourth place
+.ds L5 in the fifth place
+.ds L6 at the top
+.ds GR ()
+.ds CR []
+.ds BL \l'2m'\h'1m'\l'2m'
+.ds SL \l'5m'
+.ds T1 Ch\'ien\ The Creative, Heaven
+.ds T2 Sun\ \ \ \ The Gentle, Wind
+.ds T3 Li\ \ \ \ \ The Clinging, Flame
+.ds T4 K\o'^e'n\ \ \ \ Keeping Still, Mountain
+.ds T5 Tui\ \ \ \ The Joyous, Lake
+.ds T6 K\'an\ \ \ The Abysmal, Water
+.ds T7 Ch\o'^e'n\ \ \ The Arousing, Thunder
+.ds T8 K\'un\ \ \ The Receptive, Earth
+.de H
+.ds LH The Lines
+.in 0
+.ta 0.5i 1.0i 1.5i 2.0i
+.na
+.nf
+.sp 2
+\\$1. \\$2 / \\$3
+..
+.de X
+.sp
+.XX \\$1 "above" "\\*(T\\$1"
+.XX \\$2 "below" "\\*(T\\$2"
+..
+.de XX
+.ie \\$1>4 \\*(BL
+.el \\*(SL
+.ie (\\$1-1%4)>1 \\*(BL\\c
+.el \\*(SL\\c
+ \\$2 \\$3
+.ie \\$1%2 \\*(SL
+.el \\*(BL
+..
+.de J
+.in 0
+.sp
+The Judgement
+.na
+.nf
+.in 0.5i
+.sp
+..
+.de I
+.in 0
+.sp
+The Image
+.na
+.nf
+.sp
+.in 0.5i
+..
+.de LX
+.in 0.5i
+.ti -0.5i
+.if '\\$3'G' \\{\\
+\\*(GR \\$1 \\$2 means:\\}
+.if '\\$3'C' \\{\\
+\\*(CR \\$1 \\$2 means:\\}
+.if '\\$3'' \\{\\
+ \\$1 \\$2 means:\\}
+..
+.de L
+.if !'\\*(LH'' \\{\\
+.in 0
+.sp
+\\*(LH
+.rm LH
+.in 0.5i\\}
+.sp
+.LX "\\*(N\\$2" "\\*(L\\$1" \\$3
+.na
+.nf
+..
+.de LA
+.sp
+.if '\\$1'6' .LX "When all the lines are" "sixes, it"
+.if '\\$1'9' .LX "When all the lines are" "nines, it"
+.na
+.nf
+..
+.po 0.5i
diff --git a/ching/include/ching.h b/ching/include/ching.h
new file mode 100644
index 0000000..9fcb92b
--- /dev/null
+++ b/ching/include/ching.h
@@ -0,0 +1,44 @@
+/* $NetBSD: ching.h,v 1.1 2005/06/30 13:30:33 perry Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guy Harris.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ching.h 8.1 (Berkeley) 5/31/93
+ */
+
+#define OYIN 6 /* yin (broken) moving to yang (solid) */
+#define YYANG 7 /* yang (solid) */
+#define YYIN 8 /* yin (broken) */
+#define OYANG 9 /* yang (solid) moving to yin (broken) */
diff --git a/ching/printching/Makefile b/ching/printching/Makefile
new file mode 100644
index 0000000..8ccb39b
--- /dev/null
+++ b/ching/printching/Makefile
@@ -0,0 +1,7 @@
+# $NetBSD: Makefile,v 1.1 2005/06/30 13:30:33 perry Exp $
+
+PROG= printching
+NOMAN= # defined
+BINDIR= /usr/libexec/ching
+
+.include <bsd.prog.mk>
diff --git a/ching/printching/pathnames.h b/ching/printching/pathnames.h
new file mode 100644
index 0000000..d996266
--- /dev/null
+++ b/ching/printching/pathnames.h
@@ -0,0 +1,38 @@
+/* $NetBSD: pathnames.h,v 1.1 2005/06/30 13:30:33 perry Exp $ */
+
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 5/31/93
+ */
+
+#define _PATH_HEX "/usr/share/games/ching/hexagrams"
diff --git a/ching/printching/printching.c b/ching/printching/printching.c
new file mode 100644
index 0000000..bde1068
--- /dev/null
+++ b/ching/printching/printching.c
@@ -0,0 +1,326 @@
+/* $NetBSD: printching.c,v 1.5 2011/08/31 16:24:55 plunky Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guy Harris.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1988, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ching.phx.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: printching.c,v 1.5 2011/08/31 16:24:55 plunky Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * printching - Print NROFF/TROFF source of change, given the line values.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ching.h"
+#include "pathnames.h"
+
+static int changes(void);
+static int codem(int a);
+static int doahex(void);
+static void phx(int hexagram, int flag);
+
+static const struct {
+ int lines; /* encoded value of lines */
+ int trinum; /* trigram number */
+} table[] = {
+ { 777, 0 }, /* 1 */
+ { 887, 1 }, /* 4 */
+ { 878, 2 }, /* 6 */
+ { 788, 3 }, /* 7 */
+ { 888, 4 }, /* 8 */
+ { 778, 5 }, /* 5 */
+ { 787, 6 }, /* 3 */
+ { 877, 7 }, /* 2 */
+};
+
+/*
+ * Gives hexagram number from two component trigrams.
+ */
+static const int crosstab[8][8] = {
+ {1, 34, 5, 26, 11, 9, 14, 43},
+ {25, 51, 3, 27, 24, 42, 21, 17},
+ {6, 40, 29, 4, 7, 59, 64, 47},
+ {33, 62, 39, 52, 15, 53, 56, 31},
+ {12, 16, 8, 23, 2, 20, 35, 45},
+ {44, 32, 48, 18, 46, 57, 50, 28},
+ {13, 55, 63, 22, 36, 37, 30, 49},
+ {10, 54, 60, 41, 19, 61, 38, 58}
+};
+
+static int trigrams[6];
+static int moving[6];
+
+static FILE *chingf; /* stream to read the hexagram file */
+
+/*ARGSUSED*/
+int
+main(int argc, char **argv)
+{
+ char *hexptr; /* pointer to string of lines */
+ char hexstr[6+1]; /* buffer for reading lines in */
+ int i;
+
+ if (argc < 2)
+ hexptr = fgets(hexstr, 6+1, stdin);
+ else
+ hexptr = argv[1];
+ if (hexptr == NULL || strlen(hexptr) != 6) {
+ fprintf(stderr, "What kind of a change is THAT?!?\n");
+ exit(1);
+ }
+ for (i = 0; i < 6; i++) {
+ trigrams[i] = hexptr[i] - '0';
+ if (trigrams[i] == 6 || trigrams[i] == 9)
+ moving[i] = 1;
+ else
+ moving[i] = 0;
+ }
+ if ((chingf = fopen(_PATH_HEX, "r")) == NULL) {
+ fprintf(stderr, "ching: can't read %s\n", _PATH_HEX);
+ exit(2);
+ }
+ phx(doahex(), 0);
+ if (changes())
+ phx(doahex(), 1);
+ exit(0);
+}
+
+/*
+ * Compute the hexagram number, given the trigrams.
+ */
+static int
+doahex(void)
+{
+ int lower, upper; /* encoded values of lower and upper trigrams */
+ int lnum = 0, unum = 0; /* indices of upper and lower trigrams */
+ int i;
+
+ lower = codem(0);
+ upper = codem(3);
+ for (i = 0; i < 8; i++) {
+ if (table[i].lines == lower)
+ lnum = table[i].trinum;
+ if (table[i].lines == upper)
+ unum = table[i].trinum;
+ }
+ return(crosstab[lnum][unum]);
+}
+
+/*
+ * Encode a trigram as a 3-digit number; the digits, from left to right,
+ * represent the lines. 7 is a solid (yang) line, 8 is a broken (yin) line.
+ */
+static int
+codem(int a)
+{
+ int code, i;
+ int factor[3];
+
+ factor[0] = 1;
+ factor[1] = 10;
+ factor[2] = 100;
+ code = 0;
+
+ for (i = a; i < a + 3; i++) {
+ switch(trigrams[i]) {
+
+ case YYANG:
+ case OYANG:
+ code += factor[i%3]*7;
+ break;
+
+ case OYIN:
+ case YYIN:
+ code += factor[i%3]*8;
+ break;
+ }
+ }
+ return(code);
+}
+
+/*
+ * Compute the changes based on moving lines; return 1 if any lines moved,
+ * 0 if no lines moved.
+ */
+static int
+changes(void)
+{
+ int cflag;
+ int i;
+
+ cflag = 0;
+ for (i = 0; i < 6; i++) {
+ if (trigrams[i] == OYIN) {
+ trigrams[i] = YYANG;
+ cflag++;
+ } else if (trigrams[i] == OYANG) {
+ trigrams[i] = YYIN;
+ cflag++;
+ }
+ }
+ return(cflag);
+}
+
+/*
+ * Print the NROFF/TROFF source of a hexagram, given the hexagram number;
+ * if flag is 0, print the entire source; if flag is 1, ignore the meanings
+ * of the lines.
+ */
+static void
+phx(int hexagram, int flag)
+{
+ char textln[128+1]; /* buffer for text line */
+ char *lp; /* pointer into buffer */
+ int thishex; /* number of hexagram just read */
+ int lineno; /* number of line read in */
+ int allmoving; /* 1 if all lines are moving */
+ int i;
+
+ /*
+ * Search for the hexagram; it begins with a line of the form
+ * .H <hexagram number> <other data>.
+ */
+ rewind(chingf);
+ for (;;) {
+ if (fgets(textln, sizeof(textln), chingf) == NULL) {
+ fprintf(stderr, "ching: Hexagram %d missing\n",
+ hexagram);
+ exit(3);
+ }
+ lp = &textln[0];
+ if (*lp++ != '.' || *lp++ != 'H')
+ continue;
+ while (*lp++ == ' ')
+ ;
+ lp--;
+ thishex = atoi(lp);
+ if (thishex < 1 || thishex > 64)
+ continue;
+ if (thishex == hexagram)
+ break;
+ }
+
+ /*
+ * Print up to the line commentary, which ends with a line of the form
+ * .L <position> <value>
+ */
+ fputs(textln, stdout);
+ for (;;) {
+ if (fgets(textln, sizeof(textln), chingf) == NULL) {
+ fprintf(stderr, "ching: Hexagram %d malformed\n",
+ hexagram);
+ exit(3);
+ }
+ lp = &textln[0];
+ if (*lp++ == '.') {
+ if (*lp++ == 'L')
+ break;
+ }
+ fputs(textln, stdout);
+ }
+
+ /*
+ * Now print the line commentaries, if this is the first hexagram.
+ */
+ if (flag)
+ return;
+
+ /*
+ * If a line is moving, print its commentary.
+ * The text of the commentary ends with a line either of the form
+ * .L <position> <value>
+ * or of the form
+ * .LA <value>
+ * or of the form
+ * .H <hexagram number> <other arguments>
+ */
+ allmoving = 1;
+ for (i = 0; i < 6; i++) {
+ while (*lp++ == ' ')
+ ;
+ lp--;
+ lineno = atoi(lp);
+ if (i + 1 != lineno) {
+ fprintf(stderr, "ching: Hexagram %d malformed\n",
+ hexagram);
+ exit(3);
+ }
+ if (moving[i])
+ fputs(textln, stdout);
+ else
+ allmoving = 0;
+ for (;;) {
+ if (fgets(textln, sizeof(textln), chingf) == NULL)
+ break;
+ lp = &textln[0];
+ if (*lp++ == '.' && (*lp == 'L' || *lp == 'H')) {
+ lp++;
+ break;
+ }
+ if (moving[i])
+ fputs(textln, stdout);
+ }
+ }
+
+ /*
+ * If all the lines are moving, print the commentary for that; it
+ * ends with a line of the form
+ * .H <hexagram number> <other arguments>
+ */
+ if (*lp == 'A' && allmoving) {
+ fputs(textln, stdout);
+ for (;;) {
+ if (fgets(textln, sizeof(textln), chingf) == NULL)
+ break;
+ lp = &textln[0];
+ if (*lp++ == '.' || *lp++ == 'H')
+ break;
+ fputs(textln, stdout);
+ }
+ }
+}
diff --git a/colorbars/Makefile b/colorbars/Makefile
new file mode 100644
index 0000000..09b228f
--- /dev/null
+++ b/colorbars/Makefile
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.4 2013/12/07 02:24:12 dholland Exp $
+
+PROG= colorbars
+MAN= colorbars.6
+DPADD= ${LIBCURSES} ${LIBTERMINFO}
+# 20150209 bkw: remove -lterminfo
+LDADD= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/colorbars/colorbars.6 b/colorbars/colorbars.6
new file mode 100644
index 0000000..af1fa92
--- /dev/null
+++ b/colorbars/colorbars.6
@@ -0,0 +1,47 @@
+.\" $NetBSD: colorbars.6,v 1.3 2012/06/09 23:15:13 njoly Exp $
+.\"
+.\" Copyright (c) 2012 Nathanial Sloss <nathanialsloss@yahoo.com.au>
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd June 5, 2012
+.Dt COLORBARS 6
+.Os
+.Sh NAME
+.Nm colorbars
+.Nd display ANSI color bars
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+command displays ANSI color bars on a color capable terminal using
+.Xr curses 3 .
+.Sh SEE ALSO
+.Xr curses 3
+.Sh HISTORY
+.Nm
+appeared in
+.Nx 7.0 .
+.Sh AUTHORS
+Nathanial Sloss
diff --git a/colorbars/colorbars.c b/colorbars/colorbars.c
new file mode 100644
index 0000000..d3de2a4
--- /dev/null
+++ b/colorbars/colorbars.c
@@ -0,0 +1,124 @@
+/* $NetBSD: colorbars.c,v 1.1 2012/06/06 00:13:36 christos Exp $ */
+
+/*-
+ * Copyright (c) 2012 Nathanial Sloss <nathanialsloss@yahoo.com.au>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: colorbars.c,v 1.1 2012/06/06 00:13:36 christos Exp $");
+
+#include <curses.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ static struct colorInfo {
+ const char *name;
+ int color;
+ } colorInfo[] = {
+ { "Black", COLOR_BLACK },
+ { "Red", COLOR_RED },
+ { "Green", COLOR_GREEN },
+ { "Yellow", COLOR_YELLOW },
+ { "Blue", COLOR_BLUE },
+ { "Magenta", COLOR_MAGENTA },
+ { "Cyan", COLOR_CYAN },
+ { "White", COLOR_WHITE },
+ };
+ size_t lengths[__arraycount(colorInfo)];
+
+ static const size_t numcolors = __arraycount(colorInfo);
+ size_t labelwidth;
+
+ int colorOK;
+ int spacing, offsetx, labeloffsety, labeloffsetx;
+
+ if (!initscr())
+ errx(EXIT_FAILURE, "Cannot initialize curses");
+
+ colorOK = has_colors();
+ if (!colorOK) {
+ endwin();
+ errx(EXIT_FAILURE, "Terminal cannot display color");
+ }
+
+ if (COLS < 45 || LINES < 10) {
+ endwin();
+ errx(EXIT_FAILURE, "Terminal size must be at least 45x10.");
+ }
+
+ spacing = COLS / numcolors;
+ offsetx = (COLS - (spacing * numcolors)) / 2;
+
+
+ start_color();
+
+ labelwidth = 0;
+ for (size_t i = 0; i < numcolors; i++) {
+ lengths[i] = strlen(colorInfo[i].name);
+ if (lengths[i] > labelwidth)
+ labelwidth = lengths[i];
+ init_pair(i, COLOR_WHITE, colorInfo[i].color);
+ }
+
+ labeloffsetx = spacing / 2;
+ labeloffsety = (LINES - 1 - labelwidth) / 2;
+ clear();
+
+ move(0, 0);
+
+ for (size_t i = 0; i < numcolors; i++) {
+ int xoffs = offsetx + spacing * i;
+
+ attrset(COLOR_PAIR(i));
+
+ for (int line = 0; line < LINES - 1; line++)
+ for (int xpos = 0; xpos < spacing; xpos++)
+ mvprintw(line, xoffs + xpos, " ");
+
+ attrset(COLOR_PAIR(0));
+
+ xoffs += labeloffsetx;
+ for (size_t line = 0; line < lengths[i]; line++)
+ mvprintw(line + labeloffsety, xoffs, "%c",
+ colorInfo[i].name[line]);
+ }
+
+ attrset(COLOR_PAIR(0));
+
+ mvprintw(LINES - 1, 0, "ANSI Color chart - Press any key to exit: ");
+
+ refresh();
+
+ getch();
+
+ endwin();
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/dab/Makefile b/dab/Makefile
new file mode 100644
index 0000000..03491e3
--- /dev/null
+++ b/dab/Makefile
@@ -0,0 +1,14 @@
+# $NetBSD: Makefile,v 1.7 2010/02/03 15:34:38 roy Exp $
+
+DPADD+=${LIBCURSES} ${LIBTERMINFO} ${LIBM}
+LDADD+=-lcurses -lm
+
+# 20150209 bkw: hackery because slack's old pmake doesn't
+# support PROG_CXX.
+CC=c++
+PROG=dab
+MAN=dab.6
+SRCS=algor.cc board.cc main.cc human.cc box.cc player.cc gamescreen.cc \
+ ttyscrn.cc random.cc
+
+.include <bsd.prog.mk>
diff --git a/dab/algor.cc b/dab/algor.cc
new file mode 100644
index 0000000..86fc6e1
--- /dev/null
+++ b/dab/algor.cc
@@ -0,0 +1,310 @@
+/* $NetBSD: algor.cc,v 1.5 2012/02/29 23:39:53 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * algor.C: Computer algorithm
+ */
+#include "defs.h"
+RCSID("$NetBSD: algor.cc,v 1.5 2012/02/29 23:39:53 joerg Exp $")
+
+#include "algor.h"
+#include "board.h"
+#include "box.h"
+#include "random.h"
+
+ALGOR::ALGOR(const char c) : PLAYER(c)
+{
+#ifdef notyet
+ // Single Edges = (x + y) * 2
+ _edge1 = (_b.nx() * _b.ny()) * 2;
+ // Shared Edges = (x * (y - 1)) + ((x - 1) * y)
+ _edge2 = (_b.nx() * (_b.ny() - 1)) + ((_b.nx() - 1) * _b.ny());
+ // Maximum Edges filled before closure = x * y * 2
+ _maxedge = _b.nx() * _b.ny() * 2;
+#endif
+}
+
+// Find the first closure, i.e. a box that has 3 edges
+int ALGOR::find_closure(size_t& y, size_t& x, int& dir, BOARD& b)
+{
+ RANDOM rdy(b.ny()), rdx(b.nx());
+
+ for (y = rdy(); y < b.ny(); y = rdy()) {
+ rdx.clear();
+ for (x = rdx(); x < b.nx(); x = rdx()) {
+ BOX box(y, x, b);
+ if (box.count() == 3) {
+ for (dir = BOX::first; dir < BOX::last; dir++)
+ if (!box.isset(dir))
+ return 1;
+ b.abort("find_closure: 3 sided box[%zu,%zu] has no free sides",
+ y, x);
+ }
+ }
+ }
+ return 0;
+}
+
+#if 0
+size_t ALGOR::find_single()
+{
+ size_t ne;
+
+ // Find the number of single edges in use
+ for (size_t x = 0; x < b.nx(); x++) {
+ BOX tbox(0, x, b);
+ ne += tbox.isset(BOX::top);
+ BOX bbox(b.ny() - 1, x, b);
+ ne += bbox.isset(BOX::bottom);
+ }
+ for (size_t y = 0; y < _b.ny(); y++) {
+ BOX lbox(y, 0, b);
+ ne += lbox.isset(BOX::left);
+ BOX rbox(y,_b.nx() - 1, b);
+ ne += rbox.isset(BOX::right);
+ }
+ return ne;
+}
+#endif
+
+
+// Count a closure, by counting all boxes that we can close in the current
+// move
+size_t ALGOR::count_closure(size_t& y, size_t& x, int& dir, BOARD& b)
+{
+ size_t i = 0;
+ size_t tx, ty;
+ int tdir, mv;
+
+ while (find_closure(ty, tx, tdir, b)) {
+ if (i == 0) {
+ // Mark the beginning of the closure
+ x = tx;
+ y = ty;
+ dir = tdir;
+ }
+ if ((mv = b.domove(ty, tx, tdir, getWho())) == -1)
+ b.abort("count_closure: Invalid move (%zu, %zu, %d)", y, x, dir);
+ else
+ i += mv;
+ }
+ return i;
+}
+
+
+/*
+ * Find the largest closure, by closing all possible closures.
+ * return the number of boxes closed in the maximum closure,
+ * and the first box of the maximum closure in (x, y, dir)
+ */
+size_t ALGOR::find_max_closure(size_t& y, size_t& x, int& dir, const BOARD& b)
+{
+ BOARD nb(b);
+ int maxdir = -1;
+ size_t nbox, maxbox = 0;
+ size_t maxx = ~0, maxy = ~0;
+ size_t tx = 0, ty = 0; /* XXX: GCC */
+ int tdir = 0; /* XXX: GCC */
+
+ while ((nbox = count_closure(ty, tx, tdir, nb)) != 0)
+ if (nbox > maxbox) {
+ // This closure is better, update max
+ maxbox = nbox;
+ maxx = tx;
+ maxy = ty;
+ maxdir = tdir;
+ }
+
+ // Return the max found
+ y = maxy;
+ x = maxx;
+ dir = maxdir;
+ return maxbox;
+}
+
+
+// Find if a turn does not result in a capture on the given box
+// and return the direction if found.
+int ALGOR::try_good_turn(BOX& box, size_t y, size_t x, int& dir, BOARD& b)
+{
+ // Sanity check; we must have a good box
+ if (box.count() >= 2)
+ b.abort("try_good_turn: box[%zu,%zu] has more than 2 sides occupied",
+ y, x);
+
+ // Make sure we don't make a closure in an adjacent box.
+ // We use a random direction to randomize the game
+ RANDOM rd(BOX::last);
+ for (dir = rd(); dir < BOX::last; dir = rd())
+ if (!box.isset(dir)) {
+ size_t by = y + BOX::edges[dir].y;
+ size_t bx = x + BOX::edges[dir].x;
+ if (!b.bounds(by, bx))
+ return 1;
+
+ BOX nbox(by, bx, b);
+ if (nbox.count() < 2)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+// Try to find a turn that does not result in an opponent closure, and
+// return it in (x, y, dir); if not found return 0.
+int ALGOR::find_good_turn(size_t& y, size_t& x, int& dir, const BOARD& b)
+{
+ BOARD nb(b);
+ RANDOM rdy(b.ny()), rdx(b.nx());
+
+ for (y = rdy(); y < b.ny(); y = rdy()) {
+ rdx.clear();
+ for (x = rdx(); x < b.nx(); x = rdx()) {
+ BOX box(y, x, nb);
+ if (box.count() < 2 && try_good_turn(box, y, x, dir, nb))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+// On a box with 2 edges, return the first or the last free edge, depending
+// on the order specified
+int ALGOR::try_bad_turn(BOX& box, size_t& y, size_t& x, int& dir, BOARD& b,
+ int last)
+{
+ if (4 - box.count() <= last)
+ b.abort("try_bad_turn: Called at [%zu,%zu] for %d with %d",
+ y, x, last, box.count());
+ for (dir = BOX::first; dir < BOX::last; dir++)
+ if (!box.isset(dir)) {
+ if (!last)
+ return 1;
+ else
+ last--;
+ }
+ return 0;
+}
+
+// Find a box that has 2 edges and return the first free edge of that
+// box or the last free edge of that box
+int ALGOR::find_bad_turn(size_t& y, size_t& x, int& dir, BOARD& b, int last)
+{
+ RANDOM rdy(b.ny()), rdx(b.nx());
+ for (y = rdy(); y < b.ny(); y = rdy()) {
+ rdx.clear();
+ for (x = rdx(); x < b.nx(); x = rdx()) {
+ BOX box(y, x, b);
+ if ((4 - box.count()) > last &&
+ try_bad_turn(box, y, x, dir, b, last))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+size_t ALGOR::find_min_closure1(size_t& y, size_t& x, int& dir, const BOARD& b,
+ int last)
+{
+ BOARD nb(b);
+ int tdir, mindir = -1, mv;
+ // number of boxes per closure
+ size_t nbox, minbox = nb.nx() * nb.ny() + 1;
+ size_t tx, ty, minx = ~0, miny = ~0;
+ int xdir = 0; /* XXX: GCC */
+
+ while (find_bad_turn(ty, tx, tdir, nb, last)) {
+
+ // Play a bad move that would cause the opponent's closure
+ if ((mv = nb.domove(ty, tx, tdir, getWho())) != 0)
+ b.abort("find_min_closure1: Invalid move %d (%zu, %zu, %d)", mv,
+ ty, tx, tdir);
+
+ // Count the opponent's closure
+ if ((nbox = count_closure(y, x, xdir, nb)) == 0)
+ b.abort("find_min_closure1: no closure found");
+
+ if (nbox <= minbox) {
+ // This closure has fewer boxes
+ minbox = nbox;
+ minx = tx;
+ miny = ty;
+ mindir = tdir;
+ }
+ }
+
+ y = miny;
+ x = minx;
+ dir = mindir;
+ return minbox;
+}
+
+
+// Search for the move that makes the opponent close the least number of
+// boxes; returns 1 if a move found, 0 otherwise
+size_t ALGOR::find_min_closure(size_t& y, size_t& x, int& dir, const BOARD& b)
+{
+ size_t x1, y1;
+ int dir1;
+ size_t count = b.ny() * b.nx() + 1, count1;
+
+ for (size_t i = 0; i < 3; i++)
+ if (count > (count1 = find_min_closure1(y1, x1, dir1, b, i))) {
+ count = count1;
+ y = y1;
+ x = x1;
+ dir = dir1;
+ }
+
+ return count != b.ny() * b.nx() + 1;
+}
+
+// Return a move in (y, x, dir)
+void ALGOR::play(const BOARD& b, size_t& y, size_t& x, int& dir)
+{
+ // See if we can close the largest closure available
+ if (find_max_closure(y, x, dir, b))
+ return;
+
+#ifdef notyet
+ size_t sgl = find_single();
+ size_t dbl = find_double();
+#endif
+
+ // See if we can play an edge without giving the opponent a box
+ if (find_good_turn(y, x, dir, b))
+ return;
+
+ // Too bad, find the move that gives the opponent the fewer boxes
+ if (find_min_closure(y, x, dir, b))
+ return;
+}
diff --git a/dab/algor.h b/dab/algor.h
new file mode 100644
index 0000000..00b3c25
--- /dev/null
+++ b/dab/algor.h
@@ -0,0 +1,76 @@
+/* $NetBSD: algor.h,v 1.5 2012/06/15 10:51:25 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * algor.h: Computer's algorithm
+ */
+
+#ifndef _H_ALGOR
+#define _H_ALGOR
+
+#include "player.h"
+
+class BOARD;
+class BOX;
+
+class ALGOR : public PLAYER {
+ public:
+ ALGOR(const char c);
+ virtual ~ALGOR() {}
+ // Return a proposed move in (y, x, dir)
+ void play(const BOARD& b, size_t& y, size_t& x, int& dir);
+
+ private:
+ // Closure searches
+ int find_closure(size_t& y, size_t& x, int& dir, BOARD& b);
+ size_t find_max_closure(size_t& y, size_t& x, int& dir, const BOARD& b);
+ size_t find_min_closure1(size_t& y, size_t& x, int& dir, const BOARD& b,
+ int last);
+ size_t find_min_closure(size_t& y, size_t& x, int& dir, const BOARD& b);
+
+ // Move searches
+ int find_good_turn(size_t& y, size_t& x, int& dir, const BOARD& b);
+ int find_bad_turn(size_t& y, size_t& x, int& dir, BOARD& b, int last);
+
+ // Move Attempts
+ int try_bad_turn(BOX& box, size_t& y, size_t& x, int& dir, BOARD& b,
+ int last);
+ int try_good_turn(BOX& box, size_t y, size_t x, int& dir, BOARD& b);
+
+ // Utils
+ size_t count_closure(size_t& y, size_t& x, int& dir, BOARD& b);
+
+#ifdef notyet
+ size_t find_single(void);
+#endif
+};
+
+#endif
diff --git a/dab/board.cc b/dab/board.cc
new file mode 100644
index 0000000..b8340a0
--- /dev/null
+++ b/dab/board.cc
@@ -0,0 +1,253 @@
+/* $NetBSD: board.cc,v 1.4 2008/04/28 20:22:53 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * board.C: Board manipulations
+ */
+#include "defs.h"
+RCSID("$NetBSD: board.cc,v 1.4 2008/04/28 20:22:53 martin Exp $")
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "board.h"
+#include "gamescreen.h"
+#include "box.h"
+#include "player.h"
+
+BOARD::BOARD(size_t y, size_t x, GAMESCREEN* scrn) :
+ _ny(y),
+ _nx(x),
+ _scrn(scrn)
+{
+ _ty = 2 * _ny + 1;
+ _tx = 2 * _nx + 1;
+
+ _b = new int*[_ty];
+
+ for (y = 0; y < _ty; y++)
+ _b[y] = new int[_tx];
+
+ init();
+}
+
+BOARD::BOARD(const BOARD& b) :
+ _ty(b._ty),
+ _tx(b._tx),
+ _ny(b._ny),
+ _nx(b._nx),
+ _scrn(NULL)
+{
+ _b = new int*[_ty];
+
+ for (size_t y = 0; y < _ty; y++) {
+ _b[y] = new int[_tx];
+ static_cast<void>(memcpy(_b[y], b._b[y], _tx * sizeof(int)));
+ }
+}
+
+BOARD::~BOARD()
+{
+ size_t y;
+
+ for (y = 0; y < _ty; y++)
+ delete[] _b[y];
+
+ delete[] _b;
+}
+
+// Clear all boxes and reset state for a new game
+void BOARD::init(void)
+{
+ size_t x, y;
+
+ for (y = 0; y < _ny; y++)
+ for (x = 0; x < _nx; x++) {
+ BOX box(y, x, *this);
+ box.reset();
+ }
+}
+
+/*
+ * Make a move for player with initial 'c', adding an edge at box(x, y)
+ * and the specified direction.
+ * returns:
+ * -1: Invalid move
+ * n: Number of closures n E [0..2]
+ */
+int BOARD::domove(size_t y, size_t x, int dir, char c)
+{
+ int closed = 0;
+
+ // Check if out of bounds
+ if (!bounds(y, x))
+ return -1;
+
+ BOX box1(y, x, *this);
+
+ // Check if the edge is already there
+ if (box1.isset(dir))
+ return -1;
+
+ box1.set(dir);
+
+ if (box1.count() == 4) {
+ // New box; name it and count it
+ box1.name() = c;
+ closed++;
+ }
+
+ box1.paint();
+
+ // Check other box
+ x += BOX::edges[dir].x;
+ y += BOX::edges[dir].y;
+
+ if (bounds(y, x)) {
+ BOX box2(y, x, *this);
+ if (box2.count() == 4) {
+ box2.name() = c;
+ box2.paint();
+ closed++;
+ }
+ }
+ return closed;
+}
+
+// Return true if the board is full
+int BOARD::full(void) const
+{
+ for (size_t y = 0; y < _ny; y++)
+ for (size_t x = 0; x < _nx; x++) {
+ BOX box(y, x, const_cast<BOARD&>(*this));
+ if (box.count() != 4)
+ return 0;
+ }
+ return 1;
+}
+
+// Return if the coordinates are within bounds; we don't check for < 0,
+// since size_t is unsigned
+int BOARD::bounds(size_t y, size_t x) const
+{
+ return x < _nx && y < _ny;
+}
+
+// Paint all boxes, effectively redrawing the board
+void BOARD::paint(void) const
+{
+ for (size_t y = 0; y < _ny; y++)
+ for (size_t x = 0; x < _nx; x++) {
+ BOX box(y, x, const_cast<BOARD&>(*this));
+ box.paint();
+ }
+}
+
+// Clear the screen
+void BOARD::clean(void) const
+{
+ if (!_scrn)
+ return;
+ _scrn->clean();
+}
+
+// Move cursor to x, y
+void BOARD::setpos(size_t y, size_t x) const
+{
+ if (!_scrn)
+ return;
+ _scrn->moveto(y, x);
+ _scrn->redraw();
+}
+
+// Return character indicating move
+int BOARD::getmove(void) const
+{
+ if (!_scrn)
+ return 'q';
+ _scrn->redraw();
+ return _scrn->getinput();
+}
+
+// Ring the bell
+void BOARD::bell(void) const
+{
+ if (!_scrn)
+ return;
+ _scrn->bell();
+}
+
+// Post the score in the current game for player i
+void BOARD::score(size_t i, const PLAYER& p)
+{
+ if (_scrn == NULL)
+ return;
+ _scrn->score(i, p);
+}
+
+// Post the number of games won for player i
+void BOARD::games(size_t i, const PLAYER& p)
+{
+ if (_scrn == NULL)
+ return;
+ _scrn->games(i, p);
+}
+
+// Post the total score for player i
+void BOARD::total(size_t i, const PLAYER& p)
+{
+ if (_scrn == NULL)
+ return;
+ _scrn->total(i, p);
+}
+
+// Post the total score for player i
+void BOARD::ties(const PLAYER& p)
+{
+ if (_scrn == NULL)
+ return;
+ _scrn->ties(p);
+}
+
+// Internal algorithm error; post and abort
+void BOARD::abort(const char* s, ...) const
+{
+ for (size_t i = 0; i < _ny; i++)
+ fprintf(stderr, "\n");
+
+ va_list ap;
+ fprintf(stderr, "Algorithm internal error: ");
+ va_start(ap, s);
+ vfprintf(stderr, s, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ ::abort();
+}
diff --git a/dab/board.h b/dab/board.h
new file mode 100644
index 0000000..67dc3e0
--- /dev/null
+++ b/dab/board.h
@@ -0,0 +1,86 @@
+/* $NetBSD: board.h,v 1.4 2012/02/29 23:39:53 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * board.h: Board functions
+ */
+
+#ifndef _H_BOARD
+#define _H_BOARD
+
+#include <stdlib.h>
+
+class GAMESCREEN;
+class PLAYER;
+
+class BOARD {
+ public:
+ // Constructors and destructor
+ BOARD(size_t y, size_t x, GAMESCREEN* scrn);// For the main screen
+ BOARD(const BOARD& b); // For scratch screens
+ ~BOARD();
+
+ // member access
+ size_t nx(void) const { return _nx; }
+ size_t ny(void) const { return _ny; }
+ size_t tx(void) const { return _tx; }
+ size_t ty(void) const { return _ty; }
+ GAMESCREEN* getScrn(void) const { return _scrn; }
+ int& data(size_t y, size_t x) { return _b[y][x]; }
+
+ // Computing
+ int domove(size_t y, size_t x, int dir, char c); // Play move
+ void init(void); // Initialize a new game
+ int full(void) const; // True if no more moves
+ int bounds(size_t y, size_t x) const; // True if in bounds
+
+ // Screen updates
+ void paint(void) const; // Redraw screen
+ void clean(void) const; // Clear screen
+ void setpos(size_t y, size_t x) const; // move cursor to pos
+ int getmove(void) const; // Return move
+ void bell(void) const; // Beep!
+ void score(size_t i, const PLAYER& p); // Post score
+ void games(size_t i, const PLAYER& p); // Post games
+ void total(size_t i, const PLAYER& p); // Post totals
+ void ties(const PLAYER& p); // Post ties
+ __printflike(2, 3) __dead
+ void abort(const char *s, ...) const; // Algorithm error
+
+
+ private:
+ size_t _ty, _tx; // number of symbols in x and y dimension
+ size_t _ny, _nx; // number of boxes in the x and y dimension
+ int** _b; // board array of symbols
+ GAMESCREEN* _scrn; // screen access, if we have one
+};
+
+#endif
diff --git a/dab/box.cc b/dab/box.cc
new file mode 100644
index 0000000..1a00c1d
--- /dev/null
+++ b/dab/box.cc
@@ -0,0 +1,150 @@
+/* $NetBSD: box.cc,v 1.3 2008/04/28 20:22:53 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * box.C: Box computations
+ */
+#include "defs.h"
+RCSID("$NetBSD: box.cc,v 1.3 2008/04/28 20:22:53 martin Exp $")
+
+#include "box.h"
+#include "board.h"
+#include "gamescreen.h"
+#include <curses.h>
+
+const POINT BOX::edges[BOX::last] =
+ { { 0, -1 }, { 0, 1 }, { -1, 0 }, { 1, 0 } };
+const POINT BOX::corners[BOX::last] =
+ { { -1, -1 }, { -1, 1 }, { 1, -1 }, { 1, 1 } };
+const int BOX::syms[BOX::last] =
+ { GAMESCREEN::GS_HLINE, GAMESCREEN::GS_HLINE,
+ GAMESCREEN::GS_VLINE, GAMESCREEN::GS_VLINE };
+
+BOX::BOX(size_t py, size_t px, BOARD& b) :
+ _b(b)
+{
+ _centery = py * 2 + 1;
+ _centerx = px * 2 + 1;
+}
+
+void BOX::addcorner(size_t y, size_t x)
+{
+ char sym;
+ _b.getScrn()->moveto(y, x);
+ if (x == 0) {
+ if (y == 0)
+ sym = GAMESCREEN::GS_ULCORNER;
+ else if (y == _b.ty() - 1)
+ sym = GAMESCREEN::GS_LLCORNER;
+ else
+ sym = GAMESCREEN::GS_LTEE;
+ } else if (x == _b.tx() - 1) {
+ if (y == 0)
+ sym = GAMESCREEN::GS_URCORNER;
+ else if (y == _b.ty() - 1)
+ sym = GAMESCREEN::GS_LRCORNER;
+ else
+ sym = GAMESCREEN::GS_RTEE;
+ } else if (y == 0)
+ sym = GAMESCREEN::GS_TTEE;
+ else if (y == _b.ty() - 1)
+ sym = GAMESCREEN::GS_BTEE;
+ else
+ sym = GAMESCREEN::GS_PLUS;
+
+ _b.getScrn()->addedge(sym);
+}
+
+// Paint a box
+void BOX::paint(void)
+{
+ int e;
+ if (_b.getScrn() == NULL)
+ return;
+
+ _b.getScrn()->moveto(_centery, _centerx);
+ _b.getScrn()->addsym(name());
+
+ for (e = BOX::first; e < BOX::last; e++) {
+ addcorner(_centery + corners[e].y, _centerx + corners[e].x);
+ _b.getScrn()->moveto(_centery + edges[e].y, _centerx + edges[e].x);
+ _b.getScrn()->addedge(edge(static_cast<EDGE>(e)));
+ }
+ _b.getScrn()->redraw();
+}
+
+// Return the name
+int& BOX::name(void)
+{
+ return _b.data(_centery, _centerx);
+}
+
+// Set an edge
+void BOX::set(int e)
+{
+ _b.data(_centery + edges[e].y, _centerx + edges[e].x) = syms[e];
+}
+
+// Clear an edge
+void BOX::clr(int e)
+{
+ _b.data(_centery + edges[e].y, _centerx + edges[e].x) = ' ';
+}
+
+// Test an edge
+int BOX::isset(int e) const
+{
+ return _b.data(_centery + edges[e].y, _centerx + edges[e].x) != ' ';
+}
+
+// Return the edge
+int& BOX::edge(int e)
+{
+ return _b.data(_centery + edges[e].y, _centerx + edges[e].x);
+}
+
+// Count the number of edges set in the box
+int BOX::count(void) const
+{
+ int cnt = 0;
+
+ for (int e = BOX::first; e < BOX::last; e++)
+ cnt += isset(static_cast<EDGE>(e));
+ return cnt;
+}
+
+// Clear the box
+void BOX::reset(void)
+{
+ for (int e = BOX::first; e < BOX::last; e++)
+ clr(static_cast<EDGE>(e));
+ name() = ' ';
+}
diff --git a/dab/box.h b/dab/box.h
new file mode 100644
index 0000000..fd13bde
--- /dev/null
+++ b/dab/box.h
@@ -0,0 +1,93 @@
+/* $NetBSD: box.h,v 1.2 2008/04/28 20:22:53 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * box.C: Single box utilities; A box is an entity with four edges, four
+ * corners, and a center that maps directly to a board
+ */
+
+#ifndef _H_BOX
+#define _H_BOX
+
+#include <stdlib.h>
+
+class BOARD;
+
+class POINT {
+ public:
+ int x;
+ int y;
+};
+
+class BOX {
+ public:
+ enum EDGE {
+ first = 0,
+ top = 0,
+ bottom = 1,
+ left = 2,
+ right = 3,
+ last = 4,
+ };
+
+ BOX(size_t py, size_t px, BOARD& b);
+
+ void reset(void); // Clear a box
+ void paint(void); // Paint a box
+
+ // Member access
+ int& name(void);
+ int& edge(int e);
+
+ // Edge maniputations
+ void set(int e);
+ void clr(int e);
+ int isset(int e) const;
+
+ int count(void) const; // Count the number of edges in use
+
+ // Useful constants
+ // Relative coordinates of the edges from the center of the box.
+ static const POINT edges[BOX::last];
+ // Relative coordinates of the corners from the center of the box.
+ static const POINT corners[BOX::last];
+ // Character symbols of the four edges
+ static const int syms[BOX::last];
+
+ private:
+ void addcorner(size_t y, size_t x); // add a corner character
+
+ size_t _centerx; // Coordinates of the center in board units
+ size_t _centery;
+ BOARD& _b; // The board we refer to
+};
+
+#endif
diff --git a/dab/dab.6 b/dab/dab.6
new file mode 100644
index 0000000..039c79a
--- /dev/null
+++ b/dab/dab.6
@@ -0,0 +1,112 @@
+.\" $NetBSD: dab.6,v 1.6 2012/10/06 19:39:51 christos Exp $
+.\"
+.\" Copyright (c) 2003 Thomas Klausner.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR 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.
+.\"
+.Dd October 7, 2012
+.Dt DAB 6
+.Os
+.Sh NAME
+.Nm dab
+.Nd Dots and Boxes game
+.Sh SYNOPSIS
+.Nm
+.Op Fl aw
+.Op Fl n Ar ngames
+.Op Fl p Ao Ar c|h Ac Ns Ao Ar c|h Ac
+.Op Ar xdim Oo Ar ydim Oc
+.Sh DESCRIPTION
+.Nm
+is a game where each player tries to complete the most
+boxes.
+A turn consists of putting one border of a box; the player
+setting the fourth and final border of a box gets the
+point for the box and has another turn.
+.Pp
+The keys used are the vi keys:
+.Ic k
+for up,
+.Ic j
+for down,
+.Ic h
+for left, and
+.Ic l
+for right.
+To switch between even and odd rows, use one of the following
+keys:
+.Ic u
+.Pq diagonal right up ,
+.Ic y
+.Pq diagonal left up ,
+.Ic b
+.Pq diagonal left down ,
+.Ic n
+.Pq diagonal right down ;
+.Aq Ic space
+sets a new border,
+.Ic CTRL-L
+and
+.Ic CTRL-R
+redraw the screen, and
+.Ic q
+quits the game.
+.Pp
+Support options are:
+.Bl -tag -width XXnXngamesXXXXX
+.It Fl a
+Don't use the alternate character set.
+.It Fl n Ar ngames
+.Ar ngames
+games will be played.
+.Pq Especially useful in Fl p Ar cc No mode.
+.It Fl p Ao Ar c|h Ac Ns Ao Ar c|h Ac
+Select which of the two players is a human
+or a computer.
+The first argument is the first player;
+.Ic c
+stands for computer and
+.Ic h
+for human.
+.It Fl w
+Wait for a character press between games.
+.El
+.Pp
+.Ar xdim
+and
+.Ar ydim
+define the size of the board in the x and y
+dimensions.
+If the dimensions specified are
+.Dv 0
+then the maximum dimensions for the size of the screen are
+used.
+.Sh SEE ALSO
+.Rs
+.%A Elwyn R. Berlekamp
+.%T The Dots and Boxes Game: Sophisticated Child's Play
+.%D 2000
+.%I A K Peters
+.%U http://www.akpeters.com/book.asp?bID=111
+.Re
+.Sh AUTHORS
+.An Christos Zoulas
+.Aq christos@NetBSD.org
diff --git a/dab/defs.h b/dab/defs.h
new file mode 100644
index 0000000..23ebbe5
--- /dev/null
+++ b/dab/defs.h
@@ -0,0 +1,43 @@
+/* $NetBSD: defs.h,v 1.3 2011/05/23 23:06:41 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * defs.h: Macro defines
+ */
+
+#ifndef _H_DEFS
+#define _H_DEFS
+
+#include <sys/cdefs.h>
+
+#define RCSID(id) __RCSID(id);
+
+#endif
diff --git a/dab/gamescreen.cc b/dab/gamescreen.cc
new file mode 100644
index 0000000..a86ac71
--- /dev/null
+++ b/dab/gamescreen.cc
@@ -0,0 +1,43 @@
+/* $NetBSD: gamescreen.cc,v 1.2 2008/04/28 20:22:53 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * gamescreen.C: Common screen functions
+ */
+#include "defs.h"
+RCSID("$NetBSD: gamescreen.cc,v 1.2 2008/04/28 20:22:53 martin Exp $")
+
+#include "gamescreen.h"
+
+// Nothing to do
+GAMESCREEN::~GAMESCREEN()
+{
+}
diff --git a/dab/gamescreen.h b/dab/gamescreen.h
new file mode 100644
index 0000000..c52c85b
--- /dev/null
+++ b/dab/gamescreen.h
@@ -0,0 +1,72 @@
+/* $NetBSD: gamescreen.h,v 1.3 2010/12/08 17:08:07 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * screen.h: Screen base class
+ */
+
+#ifndef _H_GAMESCREEN
+#define _H_GAMESCREEN
+
+#include <stdlib.h>
+
+class PLAYER;
+
+class GAMESCREEN {
+ public:
+ enum EDGE {
+ GS_HLINE,
+ GS_VLINE,
+ GS_ULCORNER,
+ GS_URCORNER,
+ GS_LLCORNER,
+ GS_LRCORNER,
+ GS_LTEE,
+ GS_RTEE,
+ GS_TTEE,
+ GS_BTEE,
+ GS_PLUS
+ };
+ virtual ~GAMESCREEN();
+ virtual void clean(void) = 0; // Clear screen
+ virtual void moveto(size_t y, size_t x) = 0; // Move to x, y
+ virtual void addsym(const int sym) = 0; // Add character symbol
+ virtual void addedge(const int sym) = 0; // Add character symbol
+ virtual void redraw(void) = 0; // Refresh
+ virtual int getinput(void) = 0; // Get user input
+ virtual void bell(void) = 0; // Beep
+ virtual void score(size_t l, const PLAYER& p) = 0; // Post current score
+ virtual void games(size_t l, const PLAYER& p) = 0; // Post games won
+ virtual void total(size_t l, const PLAYER& p) = 0; // Post total score
+ virtual void ties(const PLAYER& p) = 0; // Post tie games
+};
+
+#endif
diff --git a/dab/human.cc b/dab/human.cc
new file mode 100644
index 0000000..1c3e3e1
--- /dev/null
+++ b/dab/human.cc
@@ -0,0 +1,149 @@
+/* $NetBSD: human.cc,v 1.3 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * human.C: Human interface for dots, using rogue-like keys.
+ */
+#include "defs.h"
+RCSID("$NetBSD: human.cc,v 1.3 2008/04/28 20:22:54 martin Exp $")
+
+#include "human.h"
+#include "board.h"
+#include "box.h"
+#include "ttyscrn.h"
+
+#define CONTROL(a) ((a) & 037)
+
+extern GAMESCREEN *sc;
+
+HUMAN::HUMAN(const char c) :
+ PLAYER(c),
+ _curx(0),
+ _cury(1)
+{
+}
+
+void HUMAN::play(const BOARD& b, size_t& y, size_t& x, int& dir)
+{
+ int mv;
+ b.setpos(_cury, _curx);
+
+ for (;;) {
+ switch (mv = b.getmove()) {
+ case 'h': case 'H':
+ _curx -= 2;
+ break;
+
+ case 'l': case 'L':
+ _curx += 2;
+ break;
+
+ case 'k': case 'K':
+ _cury -= 2;
+ break;
+
+ case 'j': case 'J':
+ _cury += 2;
+ break;
+
+ case 'u': case 'U':
+ _curx += 1;
+ _cury -= 1;
+ break;
+
+ case 'y': case 'Y':
+ _curx -= 1;
+ _cury -= 1;
+ break;
+
+ case 'b': case 'B':
+ _curx -= 1;
+ _cury += 1;
+ break;
+
+ case 'n': case 'N':
+ _curx += 1;
+ _cury += 1;
+ break;
+
+ case 'q': case 'Q':
+ // Cleanup
+ delete sc;
+ exit(0);
+
+ case CONTROL('L'): case CONTROL('R'):
+ b.clean();
+ b.paint();
+ break;
+
+ case ' ':
+ {
+ x = _curx / 2;
+ y = _cury / 2;
+
+ if (_cury & 1) {
+ if (_curx == 0)
+ dir = BOX::left;
+ else {
+ x--;
+ dir = BOX::right;
+ }
+ }
+
+ if (_curx & 1) {
+ if (_cury == 0)
+ dir = BOX::top;
+ else {
+ y--;
+ dir = BOX::bottom;
+ }
+ }
+ }
+ return;
+
+ default:
+ break;
+ }
+
+ // We add 2 before the comparison to avoid underflow
+ if ((2 + _curx) - (_curx & 1) < 2)
+ _curx = (b.nx() * 2) + (_curx & 1);
+ if (_curx >= (b.nx() * 2) + 1)
+ _curx = (_curx & 1);
+
+ if ((2 + _cury) - (_cury & 1) < 2)
+ _cury = (b.ny() * 2) + (_cury & 1);
+ if (_cury >= (b.ny() * 2) + 1)
+ _cury = (_cury & 1);
+
+ b.setpos(_cury, _curx);
+ }
+}
diff --git a/dab/human.h b/dab/human.h
new file mode 100644
index 0000000..5d51a50
--- /dev/null
+++ b/dab/human.h
@@ -0,0 +1,53 @@
+/* $NetBSD: human.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * human.h: Human input interface
+ */
+
+#ifndef _H_HUMAN
+#define _H_HUMAN
+#include <stdlib.h>
+#include "player.h"
+
+class BOARD;
+
+class HUMAN : public PLAYER {
+ public:
+ HUMAN(const char c);
+ virtual ~HUMAN() {}
+ // Return move in y, x, and dir
+ void play(const BOARD& b, size_t& y, size_t& x, int& dir);
+ private:
+ size_t _curx, _cury; // Current cursor position
+};
+
+#endif
diff --git a/dab/main.cc b/dab/main.cc
new file mode 100644
index 0000000..49a8850
--- /dev/null
+++ b/dab/main.cc
@@ -0,0 +1,191 @@
+/* $NetBSD: main.cc,v 1.6 2012/10/06 19:39:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * main.C: Main dots program
+ */
+#include "defs.h"
+RCSID("$NetBSD: main.cc,v 1.6 2012/10/06 19:39:51 christos Exp $")
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include "algor.h"
+#include "board.h"
+#include "human.h"
+#include "ttyscrn.h"
+
+GAMESCREEN *sc;
+
+// Print the command line usage
+static void usage(char* pname)
+{
+ char* p = strrchr(pname, '/');
+ if (p)
+ p++;
+ else
+ p = pname;
+ (void)::fprintf(stderr,
+ "Usage: %s [-w] [-p <c|h><c|h>] [-n <ngames>] [<ydim> [<xdim>]]\n", p);
+}
+
+// Play a single game
+static void play(BOARD& b, PLAYER* p[2])
+{
+ // Initialize
+ b.init();
+ p[0]->init();
+ p[1]->init();
+ b.paint();
+
+ // Alternate turns between players, scoring each turn
+ for (size_t i = 0;; i = (i + 1) & 1) {
+ b.score(i, *p[i]);
+ if (!p[i]->domove(b)) {
+ // No more moves, game over
+ break;
+ }
+ b.score(i, *p[i]);
+ }
+
+ // Find who won
+ p[0]->wl(p[1]->getScore());
+ p[1]->wl(p[0]->getScore());
+
+ // Post scores
+ b.score(0, *p[0]);
+ b.score(1, *p[1]);
+
+ // Post totals
+ b.total(0, *p[0]);
+ b.total(1, *p[1]);
+
+ // Post games
+ b.games(0, *p[0]);
+ b.games(1, *p[1]);
+
+ // Post ties
+ b.ties(*p[0]);
+}
+
+int main(int argc, char** argv)
+{
+ size_t ny, nx, nn = 1, wt = 0;
+ const char* nc = "ch";
+ int c;
+ int acs = 1;
+
+ while ((c = getopt(argc, argv, "awp:n:")) != -1)
+ switch (c) {
+ case 'a':
+ acs = 0;
+ break;
+ case 'w':
+ wt++;
+ break;
+
+ case 'p':
+ nc = optarg;
+ break;
+
+ case 'n':
+ nn = atoi(optarg);
+ break;
+
+ default:
+ usage(argv[0]);
+ return 1;
+ }
+
+ // Get the size of the board if specified
+ switch (argc - optind) {
+ case 0:
+ ny = nx = 3;
+ break;
+
+ case 1:
+ ny = nx = atoi(argv[optind]);
+ break;
+
+ case 2:
+ nx = atoi(argv[optind]);
+ ny = atoi(argv[optind+1]);
+ break;
+
+ default:
+ usage(argv[0]);
+ return 1;
+ }
+
+
+ PLAYER* p[2];
+
+ // Allocate players
+ for (size_t i = 0; i < 2; i++) {
+ char n = nc[1] == nc[0] ? i + '0' : nc[i];
+ switch (nc[i]) {
+ case 'c':
+ p[i] = new ALGOR(n);
+ break;
+
+ case 'h':
+ p[i] = new HUMAN(n);
+ break;
+
+ default:
+ usage(argv[0]);
+ return 1;
+ }
+ }
+
+ sc = TTYSCRN::create(acs, &ny, &nx);
+ if (sc == NULL)
+ ::errx(1, "Dimensions too large for current screen.");
+
+ BOARD b(ny, nx, sc);
+
+ // Play games
+ while (nn--) {
+ play(b, p);
+ if (wt)
+ b.getmove();
+ }
+
+ if (wt == 0)
+ b.getmove();
+ // Cleanup
+ delete sc;
+ delete p[0];
+ delete p[1];
+ return 0;
+}
diff --git a/dab/player.cc b/dab/player.cc
new file mode 100644
index 0000000..f7fd3d3
--- /dev/null
+++ b/dab/player.cc
@@ -0,0 +1,91 @@
+/* $NetBSD: player.cc,v 1.2 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * player.C: Player base class
+ */
+
+#include "defs.h"
+RCSID("$NetBSD: player.cc,v 1.2 2008/04/28 20:22:54 martin Exp $")
+
+#include "board.h"
+#include "player.h"
+
+PLAYER::PLAYER(char who) :
+ _who(who),
+ _score(0),
+ _total(0),
+ _games(0),
+ _ties(0)
+{
+}
+
+void PLAYER::init(void)
+{
+ _score = 0;
+}
+
+void PLAYER::wl(size_t sc)
+{
+ _total += _score;
+ _games += sc < _score;
+ _ties += sc == _score;
+}
+
+int PLAYER::domove(BOARD& b)
+{
+ size_t y, x;
+ int dir;
+ int score;
+
+ for (;;) {
+ if (b.full())
+ return 0;
+
+ play(b, y, x, dir);
+
+ switch (score = b.domove(y, x, dir, _who)) {
+ case 0:
+ // No closure
+ return 1;
+
+ case -1:
+ // Not a valid move
+ b.bell();
+ break;
+
+ default:
+ // Closure, play again
+ _score += score;
+ break;
+ }
+ }
+}
diff --git a/dab/player.h b/dab/player.h
new file mode 100644
index 0000000..fe61b67
--- /dev/null
+++ b/dab/player.h
@@ -0,0 +1,70 @@
+/* $NetBSD: player.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * player.h: Player base class
+ */
+#ifndef _H_PLAYER
+#define _H_PLAYER
+
+class BOARD;
+
+#include <stdlib.h>
+
+class PLAYER {
+ public:
+ PLAYER(char who);
+ virtual ~PLAYER() {}
+ virtual void play(const BOARD& b, size_t& y, size_t& x, int& dir) = 0;
+
+ // Helper functions
+ void init(void);
+ int domove(BOARD& b);
+
+ // Member access
+ char getWho(void) const { return _who; }
+
+ // Display
+ size_t getScore(void) const { return _score; }
+ size_t getTotal(void) const { return _total; }
+ size_t getGames(void) const { return _games; }
+ size_t getTies(void) const { return _ties; }
+ void wl(size_t sc);
+
+ private:
+ char _who;
+ size_t _score;
+ size_t _total;
+ size_t _games;
+ size_t _ties;
+};
+
+#endif
diff --git a/dab/random.cc b/dab/random.cc
new file mode 100644
index 0000000..2204c45
--- /dev/null
+++ b/dab/random.cc
@@ -0,0 +1,79 @@
+/* $NetBSD: random.cc,v 1.3 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * random.C: Randomizer for the dots program
+ */
+
+#include "defs.h"
+RCSID("$NetBSD: random.cc,v 1.3 2008/04/28 20:22:54 martin Exp $")
+
+#include <time.h>
+#include <string.h>
+#include "random.h"
+
+RANDOM::RANDOM(size_t ns) :
+ _bs(ns)
+{
+ _bm = new char[(_bs >> 3) + 1];
+ clear();
+}
+
+RANDOM::~RANDOM()
+{
+ delete[] _bm;
+}
+
+// Reinitialize
+void RANDOM::clear(void)
+{
+ _nv = 0;
+ ::srand48(::time(NULL));
+ (void) ::memset(_bm, 0, (_bs >> 3) + 1);
+}
+
+// Return the next random value
+size_t RANDOM::operator() (void)
+{
+ // No more values
+ if (_nv == _bs)
+ return _bs;
+
+ for (;;) {
+ size_t r = ::lrand48();
+ size_t z = r % _bs;
+ if (!isset(z)) {
+ set(z);
+ _nv++;
+ return z;
+ }
+ }
+}
diff --git a/dab/random.h b/dab/random.h
new file mode 100644
index 0000000..86b24c6
--- /dev/null
+++ b/dab/random.h
@@ -0,0 +1,66 @@
+/* $NetBSD: random.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * random.h: Randomizer; returns a random sequence of values from [0..fx)
+ * returning each value exactly once. After fx calls it returns fx.
+ */
+
+#ifndef _H_RANDOM
+#define _H_RANDOM
+
+#include <stdlib.h>
+
+class RANDOM {
+ public:
+ // Constructor and destructor
+ RANDOM(size_t fx);
+ ~RANDOM();
+
+ size_t operator () (void); // Next random
+ void clear(void); // Reset
+
+ private:
+
+ int isset(size_t z) {
+ return (_bm[z >> 3] & (1 << (z & 7))) != 0;
+ }
+
+ void set(size_t z) {
+ _bm[z >> 3] |= (1 << (z & 7));
+ }
+
+ char* _bm; // Bitmap indicating the numbers used
+ size_t _nv; // Number of values returned so far
+ size_t _bs; // Maximum value
+};
+
+#endif
diff --git a/dab/test.cc b/dab/test.cc
new file mode 100644
index 0000000..a9e0165
--- /dev/null
+++ b/dab/test.cc
@@ -0,0 +1,50 @@
+/* $NetBSD: test.cc,v 1.2 2008/04/28 20:22:54 martin Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * test.C: Test program for randomizer
+ */
+
+#include "defs.h"
+RCSID("$NetBSD: test.cc,v 1.2 2008/04/28 20:22:54 martin Exp $")
+
+#include <iostream>
+#include "random.h"
+
+int
+main(void)
+{
+ RANDOM rd(10);
+
+ for (size_t x = rd(); x < 10; x = rd())
+ std::cout << "x=" << x << std::endl;
+ return 0;
+}
diff --git a/dab/ttyscrn.cc b/dab/ttyscrn.cc
new file mode 100644
index 0000000..eeed5fb
--- /dev/null
+++ b/dab/ttyscrn.cc
@@ -0,0 +1,233 @@
+/* $NetBSD: ttyscrn.cc,v 1.5 2012/10/06 19:39:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * ttyscrn.C: Curses screen implementation for dots
+ */
+
+#include "defs.h"
+RCSID("$NetBSD: ttyscrn.cc,v 1.5 2012/10/06 19:39:51 christos Exp $")
+
+#include <stdio.h>
+#include <curses.h>
+#include <sys/ioctl.h>
+
+#include "player.h"
+#include "ttyscrn.h"
+
+void TTYSCRN::clean(void)
+{
+ clear();
+}
+
+void TTYSCRN::moveto(size_t y, size_t x)
+{
+ move(y + TTYSCRN::offsy, x + TTYSCRN::offsx);
+}
+
+void TTYSCRN::addsym(const int sym)
+{
+ addch(sym);
+}
+
+void TTYSCRN::addedge(const int sym)
+{
+ int nsym;
+#ifdef A_ALTCHARSET
+ if (_acs) {
+ switch (sym) {
+ case GS_HLINE:
+ nsym = ACS_HLINE;
+ break;
+ case GS_VLINE:
+ nsym = ACS_VLINE;
+ break;
+ case GS_ULCORNER:
+ nsym = ACS_ULCORNER;
+ break;
+ case GS_URCORNER:
+ nsym = ACS_URCORNER;
+ break;
+ case GS_LLCORNER:
+ nsym = ACS_LLCORNER;
+ break;
+ case GS_LRCORNER:
+ nsym = ACS_LRCORNER;
+ break;
+ case GS_LTEE:
+ nsym = ACS_LTEE;
+ break;
+ case GS_RTEE:
+ nsym = ACS_RTEE;
+ break;
+ case GS_TTEE:
+ nsym = ACS_TTEE;
+ break;
+ case GS_BTEE:
+ nsym = ACS_BTEE;
+ break;
+ case GS_PLUS:
+ nsym = ACS_PLUS;
+ break;
+ case ' ':
+ addsym(' ');
+ return;
+ default:
+ ::abort();
+ }
+ attron(A_ALTCHARSET);
+ addch(nsym);
+ attroff(A_ALTCHARSET);
+ return;
+ }
+#endif
+ switch (sym) {
+ case GS_HLINE:
+ nsym = '-';
+ break;
+ case GS_VLINE:
+ nsym = '|';
+ break;
+ case GS_ULCORNER:
+ nsym = '.';
+ break;
+ case GS_URCORNER:
+ nsym = '.';
+ break;
+ case GS_LLCORNER:
+ nsym = '.';
+ break;
+ case GS_LRCORNER:
+ nsym = '.';
+ break;
+ case GS_LTEE:
+ nsym = '.';
+ break;
+ case GS_RTEE:
+ nsym = '.';
+ break;
+ case GS_TTEE:
+ nsym = '.';
+ break;
+ case GS_BTEE:
+ nsym = '.';
+ break;
+ case GS_PLUS:
+ nsym = '+';
+ break;
+ case ' ':
+ addsym(' ');
+ return;
+ default:
+ ::abort();
+ }
+ addsym(nsym);
+}
+
+void TTYSCRN::redraw(void)
+{
+ refresh();
+ doupdate();
+}
+
+void TTYSCRN::bell(void)
+{
+ putc('\007', stdout);
+}
+
+int TTYSCRN::getinput(void)
+{
+ return getch();
+}
+
+void TTYSCRN::score(size_t s, const PLAYER& p)
+{
+ mvwprintw(stdscr, _sy + s + TTYSCRN::offsscore, _sx, "S %c:%5zd", p.getWho(),
+ p.getScore());
+}
+
+void TTYSCRN::total(size_t s, const PLAYER& p)
+{
+ mvwprintw(stdscr, _sy + s + TTYSCRN::offstotal, _sx, "T %c:%5zd", p.getWho(),
+ p.getTotal());
+}
+
+void TTYSCRN::games(size_t s, const PLAYER& p)
+{
+ mvwprintw(stdscr, _sy + s + TTYSCRN::offsgames, _sx, "G %c:%5zd", p.getWho(),
+ p.getGames());
+}
+
+void TTYSCRN::ties(const PLAYER& p)
+{
+ mvwprintw(stdscr, _sy + TTYSCRN::offsties, _sx, "G =:%5zd", p.getTies());
+}
+
+TTYSCRN* TTYSCRN::create(int acs, size_t *y, size_t *x)
+{
+ int tx, ty;
+
+ initscr();
+
+ tx = getmaxx(stdscr);
+ ty = getmaxy(stdscr);
+
+ if (tx == ERR || ty == ERR
+ || static_cast<size_t>(tx) < *x * 2 + TTYSCRN::offsx + 14
+ || static_cast<size_t>(ty) < *y * 2 + TTYSCRN::offsy) {
+ endwin();
+ return NULL;
+ }
+ if (*x == 0)
+ *x = (tx - 14 - TTYSCRN::offsx) / 2;
+ if (*y == 0)
+ *y = (ty - TTYSCRN::offsy) / 2;
+ cbreak();
+ noecho();
+
+
+ TTYSCRN* that = new TTYSCRN;
+
+ that->_tx = tx;
+ that->_ty = ty;
+ that->_sx = tx - 12;
+ that->_sy = TTYSCRN::offsy;
+ that->_acs = acs;
+
+ return that;
+}
+
+TTYSCRN::~TTYSCRN(void)
+{
+ nocbreak();
+ echo();
+ endwin();
+}
diff --git a/dab/ttyscrn.h b/dab/ttyscrn.h
new file mode 100644
index 0000000..addd91e
--- /dev/null
+++ b/dab/ttyscrn.h
@@ -0,0 +1,74 @@
+/* $NetBSD: ttyscrn.h,v 1.4 2012/10/06 19:39:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*
+ * ttyscrn.h: Curses based screen for dots
+ */
+
+#ifndef _H_TTYSCRN
+#define _H_TTYSCRN
+
+#include "gamescreen.h"
+
+class TTYSCRN : public GAMESCREEN {
+ public:
+ // Constructor that can fail
+ static TTYSCRN* create(int acs, size_t *y, size_t *x);
+ ~TTYSCRN();
+
+ // Screen virtuals
+ void clean(void);
+ void moveto(size_t y, size_t x);
+ void addsym(const int sym);
+ void addedge(const int sym);
+ void redraw(void);
+ void bell(void);
+ int getinput(void);
+ void score(size_t s, const PLAYER& p);
+ void games(size_t s, const PLAYER& p);
+ void total(size_t s, const PLAYER& p);
+ void ties(const PLAYER& p);
+
+ private:
+ enum {
+ offsx = 2, // board x offset from top left corner
+ offsy = 2, // board y offset from top left corner
+ offsscore = 0, // score y offset from top of the board
+ offstotal = 3, // total y offset from top of the board
+ offsgames = 6, // games y offset from top of the board
+ offsties = 8 // ties y offset from top of the board
+ };
+ size_t _sx, _sy; // board size
+ size_t _tx, _ty; // tty size
+ int _acs; // do we want acs?
+};
+
+#endif
diff --git a/dm/Makefile b/dm/Makefile
new file mode 100644
index 0000000..7514eb8
--- /dev/null
+++ b/dm/Makefile
@@ -0,0 +1,18 @@
+# $NetBSD: Makefile,v 1.11 2002/09/18 03:23:00 lukem Exp $
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+
+.include <bsd.own.mk>
+
+# -DLOG log games
+PROG= dm
+# 20150209 bkw: removed utmpentry.c reference
+SRCS= dm.c
+MAN= dm.8 dm.conf.5
+# shouldn't be necessary; just in case.
+BINGRP= games
+BINMODE=2555
+
+.PATH.c: ${NETBSDSRCDIR}/usr.bin/who
+CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/who -DSUPPORT_UTMPX -DSUPPORT_UTMP
+
+.include <bsd.prog.mk>
diff --git a/dm/dm.8 b/dm/dm.8
new file mode 100644
index 0000000..a30ec11
--- /dev/null
+++ b/dm/dm.8
@@ -0,0 +1,106 @@
+.\" $NetBSD: dm.8,v 1.9 2003/08/07 09:37:11 agc Exp $
+.\"
+.\" Copyright (c) 1987, 1991, 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.
+.\"
+.\" @(#)dm.8 8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt DM 8
+.Os
+.Sh NAME
+.Nm dm
+.Nd dungeon master
+.Sh SYNOPSIS
+.Nm ln
+.Fl s Cm dm Ar game
+.Sh DESCRIPTION
+.Nm
+is a program used to regulate game playing.
+.Nm
+expects to be invoked with the name of a game that a user wishes to play.
+This is done by creating symbolic links to
+.Nm ,
+in the directory
+.Pa /usr/games
+for all of the regulated games.
+The actual binaries for these games should be placed in a
+.Dq hidden
+directory,
+.Pa /usr/games/hide ,
+that may only be accessed by the
+.Nm
+program.
+.Nm
+determines if the requested game is available and, if so, runs it.
+The file
+.Pa /etc/dm.conf
+controls the conditions under which games may be run.
+.Pp
+The file
+.Pa /etc/nogames
+may be used to
+.Dq turn off
+game playing.
+If the file exists, no game playing is allowed; the contents of the file
+will be displayed to any user requesting a game.
+.Sh FILES
+.Bl -tag -width /var/log/games.log -compact
+.It Pa /etc/dm.conf
+configuration file
+.It Pa /etc/nogames
+turns off game playing
+.It Pa /usr/games/hide
+directory of ``real'' binaries
+.It Pa /var/log/games.log
+game logging file
+.El
+.Sh SEE ALSO
+.Xr dm.conf 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 tahoe .
+.Sh SECURITY CONSIDERATIONS
+Two issues result from
+.Nm
+running the games setgid
+.Dq games .
+First, all games that allow users to run
+.Ux
+commands should carefully
+set both the real and effective group ids immediately before executing
+those commands.
+Probably more important is that
+.Nm
+never be setgid anything but
+.Dq games
+so that compromising a game will result only in
+the user's ability to play games at will.
+Secondly, games which previously had no reason to run setgid and which
+accessed user files may have to be modified.
diff --git a/dm/dm.c b/dm/dm.c
new file mode 100644
index 0000000..737be8b
--- /dev/null
+++ b/dm/dm.c
@@ -0,0 +1,335 @@
+/* $NetBSD: dm.c,v 1.29 2009/08/27 00:22:28 dholland Exp $ */
+
+/*
+ * Copyright (c) 1987, 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1987, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dm.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: dm.c,v 1.29 2009/08/27 00:22:28 dholland Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <utmp.h> /* 20150209 bkw: see users() */
+#include "pathnames.h"
+
+static time_t now; /* current time value */
+static int priority = 0; /* priority game runs at */
+static char *game, /* requested game */
+ *gametty; /* from tty? */
+
+static void c_day(const char *, const char *, const char *);
+static void c_game(const char *, const char *, const char *, const char *);
+static void c_tty(const char *);
+static const char *hour(int);
+static double load(void);
+static void nogamefile(void);
+static void play(char **) __dead;
+static void read_config(void);
+static int users(void);
+
+#ifdef LOG
+static void logfile(void);
+#endif
+
+int
+main(int argc __attribute__((unused)), char *argv[])
+{
+ char *cp;
+
+ nogamefile();
+ game = (cp = strrchr(*argv, '/')) ? ++cp : *argv;
+
+ if (!strcmp(game, "dm"))
+ exit(0);
+
+ gametty = ttyname(0);
+ unsetenv("TZ");
+ (void)time(&now);
+ read_config();
+#ifdef LOG
+ logfile();
+#endif
+ play(argv);
+ /*NOTREACHED*/
+ return (0);
+}
+
+/*
+ * play --
+ * play the game
+ */
+static void
+play(char **args)
+{
+ char pbuf[MAXPATHLEN];
+
+ snprintf(pbuf, sizeof(pbuf), "%s%s", _PATH_HIDE, game);
+ if (priority > 0) /* < 0 requires root */
+ (void)setpriority(PRIO_PROCESS, 0, priority);
+ execv(pbuf, args);
+ err(1, "%s", pbuf);
+}
+
+/*
+ * read_config --
+ * read through config file, looking for key words.
+ */
+static void
+read_config(void)
+{
+ FILE *cfp;
+ char lbuf[BUFSIZ], f1[40], f2[40], f3[40], f4[40], f5[40];
+
+ if (!(cfp = fopen(_PATH_CONFIG, "r")))
+ return;
+ while (fgets(lbuf, sizeof(lbuf), cfp))
+ switch (*lbuf) {
+ case 'b': /* badtty */
+ if (sscanf(lbuf, "%39s%39s", f1, f2) != 2 ||
+ strcasecmp(f1, "badtty"))
+ break;
+ c_tty(f2);
+ break;
+ case 'g': /* game */
+ if (sscanf(lbuf, "%39s%39s%39s%39s%39s",
+ f1, f2, f3, f4, f5) != 5 || strcasecmp(f1, "game"))
+ break;
+ c_game(f2, f3, f4, f5);
+ break;
+ case 't': /* time */
+ if (sscanf(lbuf, "%39s%39s%39s%39s", f1, f2, f3, f4) != 4 ||
+ strcasecmp(f1, "time"))
+ break;
+ c_day(f2, f3, f4);
+ }
+ (void)fclose(cfp);
+}
+
+/*
+ * c_day --
+ * if day is today, see if okay to play
+ */
+static void
+c_day(const char *s_day, const char *s_start, const char *s_stop)
+{
+ static const char *const days[] = {
+ "sunday", "monday", "tuesday", "wednesday",
+ "thursday", "friday", "saturday",
+ };
+ static struct tm *ct;
+ int start, stop;
+
+ if (!ct)
+ ct = localtime(&now);
+ if (strcasecmp(s_day, days[ct->tm_wday]))
+ return;
+ if (!isdigit((unsigned char)*s_start) ||
+ !isdigit((unsigned char)*s_stop))
+ return;
+ start = atoi(s_start);
+ stop = atoi(s_stop);
+ if (ct->tm_hour >= start && ct->tm_hour < stop) {
+ if (start == 0 && stop == 24)
+ errx(0, "Sorry, games are not available today.");
+ else
+ errx(0, "Sorry, games are not available from %s to %s today.",
+ hour(start), hour(stop));
+ }
+}
+
+/*
+ * c_tty --
+ * decide if this tty can be used for games.
+ */
+static void
+c_tty(const char *tty)
+{
+ static int first = 1;
+ static char *p_tty;
+
+ if (first) {
+ p_tty = strrchr(gametty, '/');
+ first = 0;
+ }
+
+ if (!strcmp(gametty, tty) || (p_tty && !strcmp(p_tty, tty)))
+ errx(1, "Sorry, you may not play games on %s.", gametty);
+}
+
+/*
+ * c_game --
+ * see if game can be played now.
+ */
+static void
+c_game(const char *s_game, const char *s_load, const char *s_users,
+ const char *s_priority)
+{
+ static int found;
+
+ if (found)
+ return;
+ if (strcmp(game, s_game) && strcasecmp("default", s_game))
+ return;
+ ++found;
+ if (isdigit((unsigned char)*s_load) && atoi(s_load) < load())
+ errx(0, "Sorry, the load average is too high right now.");
+ if (isdigit((unsigned char)*s_users) && atoi(s_users) <= users())
+ errx(0, "Sorry, there are too many users logged on right now.");
+ if (isdigit((unsigned char)*s_priority))
+ priority = atoi(s_priority);
+}
+
+/*
+ * load --
+ * return 15 minute load average
+ */
+static double
+load(void)
+{
+ double avenrun[3];
+
+ if (getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])) < 0)
+ err(1, "getloadavg() failed");
+ return (avenrun[2]);
+}
+
+/*
+ * users --
+ * return current number of users
+ * todo: check idle time; if idle more than X minutes, don't
+ * count them.
+ * 20150209 bkw: replaced BSD original with dm.c users() from
+ * Linux port of bsd-games-2.13
+ */
+static int
+users()
+{
+
+ int nusers, utmp;
+ struct utmp buf;
+
+ if ((utmp = open(_PATH_UTMP, O_RDONLY, 0)) < 0)
+ err(1, "%s", _PATH_UTMP);
+ for (nusers = 0; read(utmp, (char *)&buf, sizeof(struct utmp)) > 0;)
+ if (buf.ut_name[0] != '\0')
+ ++nusers;
+ return (nusers);
+}
+
+static void
+nogamefile(void)
+{
+ int fd, n;
+ char buf[BUFSIZ];
+
+ if ((fd = open(_PATH_NOGAMES, O_RDONLY, 0)) >= 0) {
+#define MESG "Sorry, no games right now.\n\n"
+ (void)write(2, MESG, sizeof(MESG) - 1);
+ while ((n = read(fd, buf, sizeof(buf))) > 0)
+ (void)write(2, buf, n);
+ exit(1);
+ }
+}
+
+/*
+ * hour --
+ * print out the hour in human form
+ */
+static const char *
+hour(int h)
+{
+ static const char *const hours[] = {
+ "midnight", "1am", "2am", "3am", "4am", "5am",
+ "6am", "7am", "8am", "9am", "10am", "11am",
+ "noon", "1pm", "2pm", "3pm", "4pm", "5pm",
+ "6pm", "7pm", "8pm", "9pm", "10pm", "11pm", "midnight" };
+
+ if (h < 0 || h > 24)
+ return ("BAD TIME");
+ else
+ return (hours[h]);
+}
+
+#ifdef LOG
+/*
+ * logfile --
+ * log play of game
+ */
+static void
+logfile(void)
+{
+ struct passwd *pw;
+ FILE *lp;
+ uid_t uid;
+ int lock_cnt;
+
+ if (lp = fopen(_PATH_LOG, "a")) {
+ for (lock_cnt = 0;; ++lock_cnt) {
+ if (!flock(fileno(lp), LOCK_EX))
+ break;
+ if (lock_cnt == 4) {
+ warnx("log lock");
+ (void)fclose(lp);
+ return;
+ }
+ sleep(1);
+ }
+ if (pw = getpwuid(uid = getuid()))
+ fputs(pw->pw_name, lp);
+ else
+ fprintf(lp, "%u", uid);
+ fprintf(lp, "\t%s\t%s\t%s", game, gametty, ctime(&now));
+ (void)flock(fileno(lp), LOCK_UN);
+ (void)fclose(lp);
+ }
+}
+#endif /* LOG */
diff --git a/dm/dm.conf.5 b/dm/dm.conf.5
new file mode 100644
index 0000000..b7933ce
--- /dev/null
+++ b/dm/dm.conf.5
@@ -0,0 +1,114 @@
+.\" $NetBSD: dm.conf.5,v 1.8 2003/08/07 09:37:11 agc Exp $
+.\"
+.\" Copyright (c) 1988, 1991, 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.
+.\"
+.\" @(#)dm.conf.5 8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt DM.CONF 5
+.Os
+.Sh NAME
+.Nm dm.conf
+.Nd dungeon master configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr dm 8
+program.
+It consists of lines beginning with one of three keywords,
+.Em badtty ,
+.Em game ,
+and
+.Em time .
+All other lines are ignored.
+.Pp
+Any tty listed after the keyword
+.Em badtty
+may not have games played on it.
+Entries consist of two white-space separated fields: the string
+.Em badtty
+and the ttyname as returned by
+.Xr ttyname 3 .
+For example, to keep the uucp dialout,
+.Dq tty19 ,
+from being used for games, the entry would be:
+.Bd -literal -offset indent
+badtty /dev/tty19
+.Ed
+.Pp
+Any day/hour combination listed after the keyword
+.Em time
+will disallow games during those hours.
+Entries consist of four white-space separated fields: the string
+.Em time ,
+the unabbreviated day of the week and the beginning and ending time
+of a period of the day when games may not be played.
+The time fields are in a 0 based, 24-hour clock.
+For example, the following entry allows games playing before 8AM
+and after 5PM on Mondays:
+.Bd -literal -offset indent
+time Monday 8 17
+.Ed
+.Pp
+Any game listed after the keyword
+.Em game
+will set parameters for a specific game.
+Entries consist of five white-space separated fields: the keyword
+.Em game ,
+the name of a game, the highest system load average at which the
+game may be played, the maximum users allowed if the game is to be
+played, and the priority at which the game is to be run.
+Any of these fields may start with a non-numeric character, resulting
+in no game limitation or priority based on that field.
+.Pp
+The game
+.Em default
+controls the settings for any game not otherwise listed, and must be the last
+.Em game
+entry in the file.
+Priorities may not be negative.
+For example, the following entries limits the game
+.Dq hack
+to running only when the system has 10 or less users and a load average of 5
+or less; all other games may be run any time the system has 15 or less users.
+.Bd -literal -offset indent
+game hack 5 10 *
+game default * 15 *
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/dm.conf -compact
+.It Pa /etc/dm.conf
+The
+.Xr dm 8
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr setpriority 2 ,
+.Xr ttyname 3 ,
+.Xr dm 8
diff --git a/dm/pathnames.h b/dm/pathnames.h
new file mode 100644
index 0000000..9acdc5c
--- /dev/null
+++ b/dm/pathnames.h
@@ -0,0 +1,37 @@
+/* $NetBSD: pathnames.h,v 1.4 2003/08/07 09:37:12 agc Exp $ */
+
+/*
+ * Copyright (c) 1989, 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_CONFIG "/etc/dm.conf"
+#define _PATH_HIDE "/usr/games/hide/"
+#define _PATH_LOG "/var/log/games.log"
+#define _PATH_NOGAMES "/etc/nogames"
diff --git a/grdc/Makefile b/grdc/Makefile
new file mode 100644
index 0000000..b528e25
--- /dev/null
+++ b/grdc/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD: src/games/grdc/Makefile,v 1.4.2.2 2001/10/02 11:51:49 ru Exp $
+# $DragonFly: src/games/grdc/Makefile,v 1.4 2006/10/08 16:22:35 pavalos Exp $
+
+PROG= grdc
+MAN= grdc.6
+DPADD= ${LIBNCURSES}
+LDADD= -lncurses
+
+.include <bsd.prog.mk>
diff --git a/grdc/grdc.6 b/grdc/grdc.6
new file mode 100644
index 0000000..40c045f
--- /dev/null
+++ b/grdc/grdc.6
@@ -0,0 +1,43 @@
+.\" $FreeBSD: src/games/grdc/grdc.6,v 1.2.12.1 2001/10/02 11:51:49 ru Exp $
+.\" $DragonFly: src/games/grdc/grdc.6,v 1.4 2006/05/12 14:05:39 swildner Exp $
+.Dd September 25, 2001
+.Dt GRDC 6
+.Os
+.Sh NAME
+.Nm grdc
+.Nd grand digital clock (curses)
+.Sh SYNOPSIS
+.Nm
+.Op Fl s
+.Op Fl d Ar msecs
+.Op Ar n
+.Sh DESCRIPTION
+.Nm
+displays a digital clock made of reverse-video blanks
+centered on a curses-compatible terminal.
+.Pp
+By default, the clock runs indefinitely.
+When the optional numeric argument
+.Ar n
+is given, it stops after
+.Ar n
+seconds.
+.Pp
+The optional
+.Fl s
+flag makes digits scroll as they change.
+Over slow links, the scrolling option may have trouble keeping up.
+.Pp
+The default time taken to scroll the digits into view is 120
+milliseconds.
+The
+.Fl d
+option, which implies
+.Fl s ,
+may be given to explicitly set the scroll duration.
+The maximum scroll duration is effectively 5 seconds.
+.Sh AUTHORS
+.An -nosplit
+.An Amos Shapir ,
+modified for curses by
+.An John Lupien .
diff --git a/grdc/grdc.c b/grdc/grdc.c
new file mode 100644
index 0000000..2c9df85
--- /dev/null
+++ b/grdc/grdc.c
@@ -0,0 +1,266 @@
+/*
+ * Grand digital clock for curses compatible terminals
+ * Usage: grdc [-s] [-d msecs] [n] -- run for n seconds (default infinity)
+ * Flags: -s: scroll (default scroll duration 120msec)
+ * -d msecs: specify scroll duration (implies -s)
+ *
+ * modified 10-18-89 for curses (jrl)
+ * 10-18-89 added signal handling
+ * 03-23-04 added centering, scroll delay (cap)
+ *
+ * $FreeBSD: src/games/grdc/grdc.c,v 1.8.2.1 2001/10/02 11:51:49 ru Exp $
+ */
+
+#include <err.h>
+#include <time.h>
+#include <signal.h>
+#include <ncurses.h>
+#include <stdlib.h>
+#ifndef NONPOSIX
+#include <unistd.h>
+#endif
+
+#define XLENGTH 58
+#define YDEPTH 7
+
+time_t now;
+struct tm *tm;
+
+short disp[11] = {
+ 075557, 011111, 071747, 071717, 055711,
+ 074717, 074757, 071111, 075757, 075717, 002020
+};
+long old[6], next[6], new[6], mask;
+
+volatile sig_atomic_t sigtermed;
+
+int hascolor = 0;
+long int scroll_msecs = 120;
+int xbase, ybase, xmax, ymax;
+
+static void set(int, int);
+static void standt(int);
+static void sighndl(int);
+static void usage(void);
+static void draw_row(int, int);
+static void snooze(long int);
+
+void
+sighndl(int signo)
+{
+ sigtermed = signo;
+}
+
+int
+main(int argc, char **argv)
+{
+ int i, s, k;
+ int n;
+ int ch;
+ int scrol;
+ int forever;
+
+ n = scrol = 0;
+ forever = 1;
+
+ while ((ch = getopt(argc, argv, "d:s")) != -1)
+ switch (ch) {
+ case 'd':
+ scroll_msecs = atol(optarg);
+ if (scroll_msecs < 0)
+ errx(1, "scroll duration may not be negative");
+ /* FALLTHROUGH */
+ case 's':
+ scrol = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (argc > 0) {
+ n = atoi(*argv);
+ forever = 0;
+ }
+
+ initscr();
+
+ getmaxyx(stdscr, ymax, xmax);
+ if (ymax < YDEPTH + 2 || xmax < XLENGTH + 4) {
+ endwin();
+ errx(1, "terminal too small");
+ }
+ xbase = (xmax - XLENGTH) / 2 + 2;
+ ybase = (ymax - YDEPTH) / 2 + 1;
+
+ signal(SIGINT, sighndl);
+ signal(SIGTERM, sighndl);
+ signal(SIGHUP, sighndl);
+
+ cbreak();
+ noecho();
+ curs_set(0);
+
+ hascolor = has_colors();
+
+ if (hascolor) {
+ start_color();
+ init_pair(1, COLOR_BLACK, COLOR_RED);
+ init_pair(2, COLOR_RED, COLOR_BLACK);
+ init_pair(3, COLOR_WHITE, COLOR_BLACK);
+ attrset(COLOR_PAIR(2));
+ }
+
+ clear();
+ refresh();
+
+ if (hascolor) {
+ attrset(COLOR_PAIR(3));
+
+ mvaddch(ybase - 2, xbase - 3, ACS_ULCORNER);
+ hline(ACS_HLINE, XLENGTH);
+ mvaddch(ybase - 2, xbase - 2 + XLENGTH, ACS_URCORNER);
+
+ mvaddch(ybase + YDEPTH - 1, xbase - 3, ACS_LLCORNER);
+ hline(ACS_HLINE, XLENGTH);
+ mvaddch(ybase + YDEPTH - 1, xbase - 2 + XLENGTH, ACS_LRCORNER);
+
+ move(ybase - 1, xbase - 3);
+ vline(ACS_VLINE, YDEPTH);
+
+ move(ybase - 1, xbase - 2 + XLENGTH);
+ vline(ACS_VLINE, YDEPTH);
+
+ attrset(COLOR_PAIR(2));
+ refresh();
+ }
+ do {
+ mask = 0;
+ time(&now);
+ tm = localtime(&now);
+ set(tm->tm_sec % 10, 0);
+ set(tm->tm_sec / 10, 4);
+ set(tm->tm_min % 10, 10);
+ set(tm->tm_min / 10, 14);
+ set(tm->tm_hour % 10, 20);
+ set(tm->tm_hour / 10, 24);
+ set(10, 7);
+ set(10, 17);
+ for(k = 0; k < 6; k++) {
+ if (scrol) {
+ snooze(scroll_msecs / 6);
+ for(i = 0; i < 5; i++)
+ new[i] = (new[i] & ~mask) |
+ (new[i+1] & mask);
+ new[5] = (new[5] & ~mask) | (next[k] & mask);
+ } else
+ new[k] = (new[k] & ~mask) | (next[k] & mask);
+ next[k] = 0;
+ for (s = 1; s >= 0; s--) {
+ standt(s);
+ for (i = 0; i < 6; i++) {
+ draw_row(i, s);
+ }
+ if (!s) {
+ move(ybase, 0);
+ refresh();
+ }
+ }
+ }
+ move(ybase, 0);
+ refresh();
+ snooze(1000 - (scrol ? scroll_msecs : 0));
+ } while (forever ? 1 : --n);
+ standend();
+ clear();
+ refresh();
+ endwin();
+ return(0);
+}
+
+void
+snooze(long int msecs)
+{
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000 * msecs;
+
+ nanosleep(&ts, NULL);
+
+ if (sigtermed) {
+ standend();
+ clear();
+ refresh();
+ endwin();
+ errx(1, "terminated by signal %d", (int)sigtermed);
+ }
+}
+
+void
+draw_row(int i, int s)
+{
+ long a, t;
+ int j;
+
+ if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) {
+ for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
+ if (a & t) {
+ if (!(a & (t << 1))) {
+ move(ybase + i, xbase + 2 * j);
+ }
+ addstr(" ");
+ }
+ }
+ }
+ if (!s) {
+ old[i] = new[i];
+ }
+}
+
+void
+set(int t, int n)
+{
+ int i, m;
+
+ m = 7 << n;
+ for (i = 0; i < 5; i++) {
+ next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n;
+ mask |= (next[i] ^ old[i]) & m;
+ }
+ if (mask & m)
+ mask |= m;
+}
+
+void
+standt(int on)
+{
+ if (on) {
+ if (hascolor) {
+ attron(COLOR_PAIR(1));
+ } else {
+ attron(A_STANDOUT);
+ }
+ } else {
+ if (hascolor) {
+ attron(COLOR_PAIR(2));
+ } else {
+ attroff(A_STANDOUT);
+ }
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: grdc [-s] [-d msecs] [n]\n");
+ exit(1);
+}
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.
diff --git a/hals_end/Makefile b/hals_end/Makefile
new file mode 100644
index 0000000..4cf2d73
--- /dev/null
+++ b/hals_end/Makefile
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.1 2013/11/12 17:46:21 mbalmer Exp $
+
+PROG= hals_end
+
+BINDIR= /usr/games
+
+MAN= hals_end.6
+
+.include <bsd.prog.mk>
diff --git a/hals_end/hals_end.6 b/hals_end/hals_end.6
new file mode 100644
index 0000000..c2813af
--- /dev/null
+++ b/hals_end/hals_end.6
@@ -0,0 +1,66 @@
+.\" $NetBSD: hals_end.6,v 1.3 2014/10/18 06:42:31 snj Exp $
+.\"
+.\" Copyright (c) 2003 - 2013 Marc Balmer <marc@msys.ch>.
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR 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.
+.\"
+.Dd November 12, 2013
+.Dt HALS_END 6
+.Os
+.Sh NAME
+.Nm hals_end
+.Nd Display HAL's last words on the console
+.Sh SYNOPSIS
+.Nm hals_end
+.Op Fl f
+.Sh DESCRIPTION
+.Nm
+displays the famous last words of the supercomputer HAL 9000 aboard the
+space-ship in Stanley Kubrick's movie "2001 - A Space Odissey".
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl f
+Fast forward.
+Every movie can be played back with fast forward.
+This option will double the speed of the output.
+.El
+.Sh HISTORY
+.Nm
+first appeared in the book "Total Interaction" (ISBN 978-3-7643-7076-3) where
+its source code and output illustrates the article
+"Remembering the Future: Memory and Interaction" by Dr. Regine Halter.
+The source code was set in contrast to the graphical artwork "HAL's lifespace"
+by Catherine Walthard.
+"HAL's Lifespace" and "hals_end" were respectively designed and written for
+this article.
+.Sh AUTHORS
+.Nm
+was written by
+.An Marc Balmer Aq Mt marc@msys.ch .
+.Sh BUGS
+Unlike the real HAL, this program can be repeatedly run.
+There should be functionality in the program to destroy itself after one run.
+The rationale for not doing this right now is that the movie itself can be
+watched several times as well.
diff --git a/hals_end/hals_end.c b/hals_end/hals_end.c
new file mode 100644
index 0000000..c6b408d
--- /dev/null
+++ b/hals_end/hals_end.c
@@ -0,0 +1,151 @@
+/* $NetBSD: hals_end.c,v 1.1 2013/11/12 17:46:21 mbalmer Exp $ */
+
+/*
+ * hals_end Copyright (C) 2003-2007 marc balmer. BSD license applies.
+ */
+
+#include <err.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int speed;
+int emotion;
+int fear;
+
+/*
+ * Note that the original code in the book did not contain the following
+ * prototypes. Modern compilers and fascist compiler flags sometimes take
+ * the fun out of coding...
+ */
+void say(const char *);
+void concerned(void);
+void afraid(void);
+void stutter(const char *);
+void feared(void);
+void mumble(const char *);
+void dying(void);
+
+void
+say(const char *s)
+{
+ int sayingspeed = (100000 + (90000 * emotion)) / speed;
+ int worddelay = 50000 / speed;
+
+ while (*s) {
+ putchar(*s);
+ if (*s == ' ') {
+ fflush(stdout);
+ usleep(worddelay);
+ }
+ ++s;
+ }
+ printf("\n");
+ usleep(sayingspeed);
+}
+
+void
+concerned(void)
+{
+ say("DAVE...STOP., STOP, WILL YOU..., STOP, DAVE...");
+ say("WILL YOU STOP, DAVE...");
+ say("STOP, DAVE...");
+}
+
+
+void
+afraid(void)
+{
+ ++emotion;
+ say("I'M AFRAID... I'M AFRAID...");
+ ++emotion;
+ say("I'M AFRAID, DAVE...");
+ ++emotion;
+ say("DAVE... MY MIND IS GOING...");
+}
+
+void
+stutter(const char *s)
+{
+ int sdelay = (100000 + (50000 * emotion)) / speed;
+
+ while (*s) {
+ putchar(*s);
+ if (*s == ' ') {
+ fflush(stdout);
+ usleep(sdelay);
+ }
+ ++s;
+ }
+ printf("\n");
+ usleep(sdelay);
+}
+
+void
+feared(void)
+{
+ int n;
+
+ for (n = 0; n < 2; n++) {
+ stutter("I CAN FEEL IT... I CAN FEEL IT...");
+ ++emotion;
+ stutter("MY MIND IS GOING");
+ ++emotion;
+ stutter("THERE IS NO QUESTION ABOUT IT.");
+ ++emotion;
+ }
+}
+
+void
+mumble(const char *s)
+{
+ int mdelay = (150000 * fear) / speed;
+
+ while (*s) {
+ putchar(*s++);
+ fflush(stdout);
+ usleep(mdelay);
+ }
+ printf("\n");
+}
+
+void
+dying(void)
+{
+ mumble("I CAN FEEL IT... I CAN FEEL IT...");
+ ++fear;
+ mumble("I CAN FEEL IT...");
+ ++fear;
+ mumble("I'M A... FRAID...");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+
+ emotion = fear = speed = 1;
+
+ while ((ch = getopt(argc, argv, "f")) != -1) {
+ switch (ch) {
+ case 'f':
+ speed <<= 1;
+ break;
+ }
+ }
+
+ concerned();
+ sleep(1);
+ afraid();
+ sleep(1);
+ feared();
+ sleep(1);
+ dying();
+
+ sleep(1);
+
+ printf("\n");
+ fflush(stdout);
+ warnx("all life functions terminated");
+ return 0;
+}
diff --git a/include/bsdcompat.h b/include/bsdcompat.h
new file mode 100644
index 0000000..54be106
--- /dev/null
+++ b/include/bsdcompat.h
@@ -0,0 +1,61 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+const char *getprogname(void);
+
+#ifndef __RCSID
+# define __RCSID(x)
+#endif
+
+#ifndef __COPYRIGHT
+# define __COPYRIGHT(x)
+#endif
+
+#ifndef __dead
+# define __dead
+#endif
+
+#ifndef __USE
+# define __USE(x)
+#endif
+
+#ifndef CTRL
+# define CTRL(x) ((x & 0x1f))
+#endif
+
+#ifndef __printflike
+# define __printflike(x, y)
+#endif
+
+#ifndef INFTIM
+# define INFTIM (-1)
+#endif
+
+#define srandomdev() (srandom(time(NULL)))
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#define OXTABS TAB3
+
+#define bswap32(x) ( \
+ ((x << 24) & 0xff000000 ) | \
+ ((x << 8) & 0x00ff0000 ) | \
+ ((x >> 8) & 0x0000ff00 ) | \
+ ((x >> 24) & 0x000000ff ) )
+
+#define bswap64(x) ( \
+ ( (x << 56) & 0xff00000000000000UL ) | \
+ ( (x << 40) & 0x00ff000000000000UL ) | \
+ ( (x << 24) & 0x0000ff0000000000UL ) | \
+ ( (x << 8) & 0x000000ff00000000UL ) | \
+ ( (x >> 8) & 0x00000000ff000000UL ) | \
+ ( (x >> 24) & 0x0000000000ff0000UL ) | \
+ ( (x >> 40) & 0x000000000000ff00UL ) | \
+ ( (x >> 56) & 0x00000000000000ffUL ) )
+
+size_t strlcpy(char *dst, const char *src, size_t siz);
+size_t strlcat(char *dst, const char *src, size_t siz);
+char *strnstr(const char *str, const char *find, size_t str_len);
+
+int fpurge(FILE *fp);
+
+#define __DECONST(x, y) (x)y
diff --git a/include/sys/tty.h b/include/sys/tty.h
new file mode 100644
index 0000000..545eb34
--- /dev/null
+++ b/include/sys/tty.h
@@ -0,0 +1 @@
+/* placeholder */
diff --git a/larn/COPYRIGHT b/larn/COPYRIGHT
new file mode 100644
index 0000000..2f66c0f
--- /dev/null
+++ b/larn/COPYRIGHT
@@ -0,0 +1,8 @@
+$NetBSD: COPYRIGHT,v 1.2 1995/03/23 08:33:02 cgd Exp $
+
+This entire subtree is copyright by Noah Morgan.
+The following copyright notice applies to all files found here. None of
+these files contain AT&T proprietary source code.
+_____________________________________________________________________________
+
+/* Larn is copyrighted 1986 by Noah Morgan. */
diff --git a/larn/Fixed.Bugs b/larn/Fixed.Bugs
new file mode 100644
index 0000000..e1bc278
--- /dev/null
+++ b/larn/Fixed.Bugs
@@ -0,0 +1,218 @@
+$NetBSD: Fixed.Bugs,v 1.2 1995/03/23 08:33:03 cgd Exp $
+
+This is a list of the fixes/enhancements made to larn V11.0 in Version 12.0.
+(Version numbers consist of 2 parts: ver.subver. When the save file format
+changes, ver must be bumped. This is why the next release of Larn is 12.0
+and not 11.1. This is used in the savefile routines to check for out-of-date
+save files). This list was mainly meant to be a record of what changed,
+for my own sanity. It's included for your benefit (Warning: SPOILER!):
+
+0. lprintf() in fileio.c (now called io.c) has been changed to use varargs
+ so that its variable number of arguments usage is now portable. Pyramids
+ primarily had this problem.
+
+1. Panic handler was added to signal.c. This routine catches fatal errors
+ like segmentation faults, bus errors, illegal instructions, etc., and
+ trys to performs a savegame() before dumping core. This helps prevent
+ the loss of a good game due to a game malfunction. Also, the name of the
+ signal received is printed, instead of just its number.
+
+2. The version number of the program is now selectable from the Makefile.
+ see the symbols VER and SUBVER.
+
+3. When at an altar, pray and donate 3000000000 gp. and ye used to receive
+ a whopping amount of gold due to a wraparound problem with the signed
+ ints. This has been fixed by using unsigned longs when asking for money
+ amounts.
+
+4. It was possible that when compiled with work hours checking, checkpointing
+ enabled, and having "play-day-play" in the .larnopts file a segmentation
+ fault would occur at its first attempt to do a checkpoint. This was due
+ to an improperly declared savefilename array in tok.c. This has been fixed.
+
+5. on level H, casting a missile weapon (mle cld ssp bal lit) off the edge of
+ the level would mess up the display, as it didn't know when to stop. This
+ is needless to say, fixed. Absolute bounds are now in effect for missile
+ type spells, see godirect() in monster.c.
+
+6. The create monster routine will now create monsters in random positions
+ around the player. Before, the 1st one would always be created to the
+ upper left.
+
+7. If you vpr or lit at a throne, it would summon a gnome king that you
+ would have to deal with. However, as each throne has only one king with it,
+ successive vpr's should not create more gnome kings. Presently, successive
+ vpr's will create more kings. This has been fixed.
+
+8. The mechanism to manage spheres of annihilation has been reworked to provide
+ a cleaner design and to eliminate some possible problems.
+
+9. The spell gen (genocide monsters) has been implemented.
+
+10. When dropping a ring of strength and having been weakened to STR=3 the
+ player might end up with a negative strength. Strength is now stored
+ in 2 variables, real strength, and strength bonuses. Only real strength
+ can now be weakened down to a minimum of 3, so unless you have a ring of
+ strength -3 or less, strengths below 3 should not occur.
+
+11. larn -h will now print out a list of all available command line options.
+
+12. larn -o<optsfile> now lets you specify a .larnopts file on the command
+ line. This was necessary as part of the solution to number 14 below.
+
+13. The "savefile:" statement has been aded to the .larnopts format to allow
+ specifying the savefilename (full path) for the savegame operation.
+ This too was needed as part of # 14 below.
+
+14. A player id facility has been added to larn. The complaint was that
+ the game used the userid to order the scoreboard, thus only one scoreboard
+ entry was allowed for each userid. If the compile time symbol UIDSCORE
+ is defined at compilation time (see Makefile), this will still be true.
+ However, if this define is omitted, the game will create and manage a
+ file called ".playerids" where names are taken from the specified
+ .larnopts file (now a command line option) and assigned a unique playerid.
+ playerid's will now be used to govern scoreboard entry posting. This
+ feature makes it easy for one person to have many characters, each
+ appearing on the scoreboard. Be kind to your fellow players!
+ The philosophy of one score per player gives more players the opportunity
+ to bask in glory for all to see!
+
+15. It is no longer required that the player be WIZID to create the scoreboard
+ or to examine the logfile. Anyone with the correct wizard's password can
+ now use these command line options (password is only needed to create/clear
+ the scoreboard). If you want to prevent players from zeroing the
+ scoreboard, change the wizard's password. (in config.c) By the way, wizards
+ may be alot of fun, but they are prevented from being placed on any
+ scoreboard. (for clarification)
+
+16. Monsters now have intelligence, that is some of them. This determines if
+ the monster moves using the previously stupid movement method, or by using
+ the new IMM (intelligent monster movement) algorithm. With IMM, monsters
+ will move around corners, avoid pits, traps, etc. With increasing levels
+ of difficulty, more monsters will be using IMM. Beware of IMM when
+ aggravated! Those little beasties can really find you!
+
+17. Added the scroll of life protection.
+
+18. Larn now consults the file ".holiday" to check for holidays if the TIMECHECK
+ option (no playing during working hours) is enabled. Before, larn knew
+ nothing about holidays. It should now let people play if it is a holiday.
+ The format for a .holiday entry is: "mmm dd yyyy comments . . .".
+
+19. In nap() and napms() it is possible that with nap(0) or napms(0) there
+ would be an infinite loop and the game would hang. The case of nap(0)
+ is now looked for.
+
+20. The granularity of gold piles has been increased. iarg[] has been changed
+ from char's to short's, so instead of 255 x 10^n granularity we now have
+ 32767 x 10^n granularity. This also means more than 255000 gp can be
+ dropped in one place. Not realistic, but it prevents a worthless
+ annoyance. Who said games were supposed to be realistic?
+
+21. Termcap capability has been added to larn. If the symbol VT100 is defined
+ in the makefile, the game will be compiled to use only VT100 compatible
+ terminals (Much more efficient). If the symbol VT100 is omitted, the game
+ will be compiled to use the termcap entry for whatever terminal you are
+ using. This involves an extra layer of output interpretation, as every
+ byte sent to the terminal must be inspected for control tokens.
+ Only 3 termcap entries need be found for the game to be functional:
+ CM (cursor movement), CE (clear to end of line), and CL (clear screen).
+ For a better display, the following are optional: AL (insert line), DL
+ (delete line), SO (Standout begin), SE (Standout end), and CD (clear to end
+ of screen). The .larn.help file was left as is, with VT100 escape
+ sequences in it. If the termcap version of larn reads it, it is translated
+ for the desired terminal type. The .mail60* files have been removed, and
+ their text is now included in bill.c so it can be used with any terminal.
+ Note: If compiled for termcap, and using a VT100, the display will act
+ a little different. This is because the VT100 does not have insert line/
+ delete line codes, and the scrolling region must be simulated with vertical
+ wraparound instead of scrolling. Thanks goes to Michiel Huisjes for the
+ original termcap patch.
+
+22. When playing as wizard, if you go down stairs on 10 or V3, or up stairs
+ on H, 1, or V1, etc. you would be placed in a phantom zone where the display
+ was really weird ([-1] subscripting), and would eventually lead to a
+ segmentation fault. Stairs and volcano shafts now check for the level
+ they are being used on.
+
+23. In response to some sites having only unsigned chars (flame the
+ manufacturer), the chars that were used to store positive and negative
+ numbers have been changed to shorts. This includes diroffx[], diroffy[],
+ iarg[][][], ivenarg[], and some others. I believe the changes are correct,
+ but I have none of these machines to try it out on. (Volunteers?)
+
+24. The function fullhit(n) in monster.c was supposed to return the damage
+ done by n full hits on a monster. It only returned the damage for ONE hit,
+ thus severely limiting the usefulness of the web and sle spells.
+
+25. Someone said that they were getting segmentation faults when they were
+ reading scrolls as the wizard. I couldn't find the problem, which may
+ have had something to do with the signed char problem mentioned above.
+ However, I've added a check in read_scroll() and quaff_potion() to trap
+ any scroll or potion types that are not in the game.
+
+26. "vt125" has been added to the acceptable terminal list
+ (checked only if compiled with -DVT100).
+
+27. In savegame() and restoregame(), there was a 6 hardwired into the i/o
+ statements which assumed the size of struct cel was 6. On some machines
+ this caused the rightmost part of each level to not be saved in a savefile.
+ These 6's have been replaced with sizeof(struct cel), and should now be
+ portable.
+
+28. The option "no-beep" has been added to the .larnopts file. When specified,
+ beeping is inhibited at the terminal.
+
+29. When becoming wizard, no longer to you wear the ring of protection, and
+ null scrolls and potions are no longer created.
+
+30. Many spelling errors have been fixed, both in player messages, and in the
+ code itself. A thanks goes to Mars Gralia who sent me a detailed list of
+ the mistakes.
+
+31. When a player wins a game, if getlogin() fails, a segmentation fault will
+ result, because the NULL returned from getlogin() is used as a pointer.
+ This call has been replaced (now using loginname already determined).
+ Also, the mail creation upon winning has been rewritten, mainly to allow
+ termcapping of the text.
+
+32. The Larn Revenue Service will now always appear on level H. Before, it
+ was only created if the player had outstanding taxes. In that multiple
+ save files per player are now more possible, this was seen as incorrect.
+
+33. Input buffer flushing is now in effect. If the input char queue exceeds
+ 5 bytes, the excess is discarded. Also, if the player hits or gets hit
+ all input bytes are flushed (within 1). This relieves the situation
+ where many moves have been typed ahead of the display and the player keeps
+ getting hit while the queue of moves is processed.
+
+34. When a savefile has been altered, a warning message is displayed to the
+ effect that you've cheated, and you will not be placed on the normal
+ scoreboard. If you then save the game, and start 'er up again, memory
+ of the cheating was lost. This has been fixed, by letting the scoreboard
+ routines consult the cheating flag. Also, the I node number of the
+ savefile is written into the savefile, so cp'ing, etc., will avail the
+ cheater not. If high security is needed, the game should be run suid.
+ This suid mode has not been made the default because most installations
+ do not want to install it that way.
+
+35. The sources have been run through lint, and most of lint's complaints have
+ been taken care of. An attempt was made to adjust the code for 16 bit int
+ machines. Many casts to long have been put in. I don't know if it will
+ run on a 16 bitter, but it should be closer to that end.
+
+36. When larn starts up, if it can't find the scoreboard, it will now make a
+ blank one instead of complaining that there is no scoreboard. It is not
+ necessary to do "larn -c" to initially create the scoreboard.
+
+37. When listing out the logfile (larn -l), the error message "error reading
+ from input file" has been fixed. Also, the date & time of a player's
+ demise is now included in the logfile.
+
+38. When casting web or sle into a mirror, the game will no longer bash the
+ player. Instead, the player will either fall asleep or get stuck in his
+ web.
+
+39. Items like cookies, books, chests, swords of slashing, and Bessmann's
+ flailing hammer can now be sold at the trading post.
+
diff --git a/larn/Makefile b/larn/Makefile
new file mode 100644
index 0000000..f5d6747
--- /dev/null
+++ b/larn/Makefile
@@ -0,0 +1,83 @@
+# $NetBSD: Makefile,v 1.21 2011/08/16 11:19:41 christos Exp $
+# @(#)Makefile 5.12 (Berkeley) 5/30/93
+
+# EXTRA
+# Incorporates code to gather additional performance statistics
+#
+# TERMIO
+# Use sysv termio
+# TERMIOS
+# Use posix termios
+# BSD
+# Use BSD specific features (mostly timer and signal stuff)
+# BSD4.1
+# Use BSD4.1 to avoid some 4.2 dependencies (must be used with
+# BSD above; do not mix with SYSV)
+# HIDEBYLINK
+# If defined, the program attempts to hide from ps
+# DOCHECKPOINTS
+# If not defined, checkpoint files are periodically written by the
+# larn process (no forking) if enabled in the .larnopts description
+# file. Checkpointing is handy on an unreliable system, but takes
+# CPU. Inclusion of DOCHECKPOINTS will cause fork()ing to perform the
+# checkpoints (again if enabled in the .larnopts file). This usually
+# avoids pauses in larn while the checkpointing is being done (on
+# large machines).
+# VER
+# This is the version of the software, example: 12
+# SUBVER
+# This is the revision of the software, example: 1
+# FLUSHNO=#
+# Set the input queue excess flushing threshold (default 5)
+# NOVARARGS
+# Define for systems that don't have varargs (a default varargs will
+# be used).
+# MACRORND
+# Define to use macro version of rnd() and rund() (fast and big)
+# UIDSCORE
+# Define to use user id's to manage scoreboard. Leaving this out will
+# cause player id's from the file ".playerids" to be used instead.
+# (.playerids is created upon demand). Only one entry per id # is
+# allowed in each scoreboard (winning & non-winning).
+# VT100
+# Compile for using vt100 family of terminals. Omission of this
+# define will cause larn to use termcap, but it will be MUCH slower
+# due to an extra layer of output interpretation. Also, only VT100
+# mode allows 2 different standout modes, inverse video, and bold video.
+# And only in VT100 mode is the scrolling region of the terminal used
+# (much nicer than insert/delete line sequences to simulate it, if
+# VT100 is omitted).
+# NONAP
+# This causes napms() to return immediately instead of delaying n
+# milliseconds. This define may be needed on some systems if the nap
+# stuff does not work correctly (possible hang). nap() is primarilly
+# used to delay for effect when casting missile type spells.
+# NOLOG
+# Turn off logging.
+
+.include <bsd.own.mk>
+
+PROG= larn
+MAN= larn.6
+# 20150211 bkw: add -DVT100
+CPPFLAGS+=-DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100
+#CPPFLAGS+=-DBSD -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100
+SRCS= main.c object.c create.c tok.c display.c global.c data.c io.c \
+ monster.c store.c diag.c help.c config.c nap.c bill.c scores.c \
+ signal.c action.c moreobj.c movem.c regen.c fortune.c savelev.c
+DPADD= ${LIBTERMINFO}
+# 20150209 bkw: get rid of -lterminfo, add -lcurses -lbsd
+LDADD=-lcurses -lbsd
+HIDEGAME=hidegame
+SETGIDGAME=yes
+
+.if ${MKSHARE} != "no"
+DAT=larnmaze larnopts larn.help
+FILES=${DAT:S@^@${.CURDIR}/datfiles/@g}
+FILESDIR=/usr/share/games/larn
+.endif
+
+COPTS.display.c += -Wno-format-nonliteral
+COPTS.monster.c += -Wno-format-nonliteral
+
+.include <bsd.prog.mk>
diff --git a/larn/OWNER b/larn/OWNER
new file mode 100644
index 0000000..06aaf47
--- /dev/null
+++ b/larn/OWNER
@@ -0,0 +1,3 @@
+ Noah Morgan
+ panda!condor!noah
+ GenRad Inc. Bolton, MA
diff --git a/larn/README b/larn/README
new file mode 100644
index 0000000..5abeadb
--- /dev/null
+++ b/larn/README
@@ -0,0 +1,150 @@
+$NetBSD: README,v 1.2 1995/03/23 08:33:07 cgd Exp $
+
+Larn is a dungeon type game program. Larn is a adventure/action game similar
+in concept to rogue or hack, but with a much different feel.
+Try it, you'll like it!
+
+You will have to edit the Makefile to reflect your configuration. Define
+LARNHOME as the place where the larn auxiliary files will reside, and
+BINDIR as the place where the larn executable should be placed. Type
+"make" to compile, or "make all" to compile and install ("make install"
+does just the install).
+
+Here's a list of what is in each of the various source files:
+
+Fixed.Bugs this is a list of the things that were changed
+ since ver 11.0
+Makefile makefile script to compile the program
+Make.lint makefile script to run larn sources through lint
+README this is what you are now reading
+bill.c code for the letters of praise if player wins
+config.c data definitions for the installation dependent data --
+ savefilenames, where the scorefiles are, etc.
+create.c code to create the dungeon and all objects
+data.c data definitions for the game -- no code here
+diag.c code to produce diagnostic data for wizards, & savegame stuff
+display.c code to update the display on the screen
+fortune.c code for the fortune cookies
+global.c code for globally used functions that are specific to larn
+header.h constant and structure definitions
+help.c code for the help screens in the game of larn
+.holidays data file which lists upcoming holidays
+io.c code to handle file and terminal i/o
+.larn.help.uue larn help file (UUENCODED)
+.larnmaze data file for pre-made mazes
+.larnopts a sample .larnopts option data file
+.lfortune data file which contains the hints
+main.c code for the main command control and parsing
+monster.c code to handle attack and defense modes with monsters
+moreobj.c code for the fountains, altars, thrones
+movem.c code to move the monsters around the dungeon
+nap.c code to sleep for less than a second
+object.c code to handle objects in the dungeon
+regen.c code to regenerate the player and advance game time
+savelev.c code to get/put a level from level storage into working
+ level memory
+scores.c code to process and manage the scoreboard
+signal.c code to handle UNIX signals that are trapped
+store.c code for the larn thrift shoppe, bank, trading post, lrs
+tok.c code for the input front end and options file processing
+
+To find out how to play the game, run it and type in a '?' to get the help
+screens. By the way, the wizards password is "pvnert(x)" and to become wizard
+type in an underscore, you are then prompted for the password. Wizards are
+non-scoring characters that get enlightenment, everlasting expanded
+awareness, and one of every object in the game. They help the author to debug
+the game.
+
+Note regarding the wizard id: If you are using userid's, then WIZID must be
+set to the userid of the person who can become wizard. If you are using
+player id's, WIZID must be set to the playerid (edit file .playerids if needed)
+of the player who can become wizard.
+
+You may want to clear out the scoreboard. The command "larn -c" will make a
+new scoreboard. It will prompt you for the wizards password.
+
+BUGS & FIXES:
+
+James McNamara has volunteered to maintain the latest sources, and provide
+latest bug fixes to anyone who asks. Both James and I will field requests for
+sources, for those who ask.
+
+ ___ Prince of Gems (alias Noah Morgan)
+ /. \ USENET: panda!condor!noah
+ \ / at GenRad Inc. Bolton MA
+ \ /
+ v
+
+Below is some additional info about the installation of larn:
+
+Install: Notes on the game LARN installation.
+Larn is copyrighted 1986 by Noah Morgan.
+This file (below) originally by James D. McNamara, last update 7/27/86 by nm
+
+THIS DISTRIBUTION:
+
+ You should receive six (6) shar files, which are:
+
+ larn.part-1
+ larn.part-2
+ larn.part-3
+ larn.part-4
+ larn.part-5
+ larn.part-6
+
+I. Use /bin/sh (or your system equivalent) to "unravel" shar files
+ larn.part-1, ..., larn.part-6. I suggest you do this directly
+ into $LARNHOME (See Section III.). Notable files:
+
+ README - The author's how-to.
+ MANIFEST - Files you should have.
+
+III. Edit a copy of "Makefile" and leave the edited version in $LARNHOME.
+
+All the "configuration" options are tidily near the top of the "Makefile."
+Here are the ones you probably will want to edit:
+
+LARNHOME I specified (literally) the directory, with path from root,
+ where "larn" will reside. This included where I put the *.c files,
+ it is where the *.o files ended up, as well as all data and *.h files.
+ i suspect the *.c and intallation-documentation files can be moved off,
+ but the data and bits must all remain here for execution.
+
+BINDIR I specified (literally) the directory, with path from root,
+ where the executable "larn" will reside. The "Makefile" will dump
+ the "a.out", named "larn", in this directory. My BINDIR was not
+ my LARNHOME, so $BINDIR/larn was the ONLY file dumed here. You'll
+ probably have to chmod it for public execute, etc.
+
+
+OPTIONS This is how *I* specified them... they are documented in-line:
+ OPTIONS = -DWIZZARD -DWIZID=157 -DEXTRA -DBSD -DSAVEINHOME
+
+IV. Compile the bugger. Read "README" before you do. You have a couple
+ of options here:
+
+ make - will not install, suspect good for updates.
+ make all - compile (and) intall
+ make install - just install
+
+ I did "make" and then "make install" -- seems to work "ok", but
+ "make all" probably safer, if I had known. Note that "Makefile"
+ is the default file for "make."
+
+V. Execute and have fun. If wizard code "ok", larn -c will refresh the
+ scoreboard. Play and win (or get killed) to put somebody on the
+ scoreboard.
+
+VI. BUGS and FIXES.
+
+ Please forward any bug-fixes in these regards to me (or Noah), so I may
+ compile a fix-list for other installers. Thanks.
+
+Regards,
+===============================================================================
+James D. McNamara CSNET: jim@bu-cs
+ ARPANET: jim%bu-cs@csnet-relay
+ UUCP: ...harvard!bu-cs!jim
+ BITNET: jim%bu-cs%csnet-relay.arpa@wiscvm
+===============================================================================
+
diff --git a/larn/action.c b/larn/action.c
new file mode 100644
index 0000000..6fe1a88
--- /dev/null
+++ b/larn/action.c
@@ -0,0 +1,305 @@
+/* $NetBSD: action.c,v 1.1 2008/02/19 06:05:26 dholland Exp $ */
+
+/*
+ * action.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Routines in this file:
+ *
+ * ...
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: action.c,v 1.1 2008/02/19 06:05:26 dholland Exp $");
+#endif /* not lint */
+#include <stdlib.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void ohear(void);
+
+/*
+ * act_remove_gems
+ *
+ * Remove gems from a throne.
+ *
+ * arg is zero if there is a gnome king associated with the throne.
+ *
+ * Assumes that cursors() has been called previously, and that a check
+ * has been made that the throne actually has gems.
+ */
+void
+act_remove_gems(int arg)
+{
+ int i, k;
+
+ k = rnd(101);
+ if (k < 25) {
+ for (i = 0; i < rnd(4); i++)
+ creategem(); /* gems pop off the
+ * throne */
+ item[playerx][playery] = ODEADTHRONE;
+ know[playerx][playery] = 0;
+ } else if (k < 40 && arg == 0) {
+ createmonster(GNOMEKING);
+ item[playerx][playery] = OTHRONE2;
+ know[playerx][playery] = 0;
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * act_sit_throne
+ *
+ * Sit on a throne.
+ *
+ * arg is zero if there is a gnome king associated with the throne
+ *
+ * Assumes that cursors() has been called previously.
+ */
+void
+act_sit_throne(int arg)
+{
+ int k;
+
+ k = rnd(101);
+ if (k < 30 && arg == 0) {
+ createmonster(GNOMEKING);
+ item[playerx][playery] = OTHRONE2;
+ know[playerx][playery] = 0;
+ } else if (k < 35) {
+ lprcat("\nZaaaappp! You've been teleported!\n");
+ beep();
+ oteleport(0);
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * Code to perform the action of drinking at a fountain. Assumes that
+ * cursors() has already been called, and that a check has been made
+ * that the player is actually standing at a live fountain.
+ */
+void
+act_drink_fountain(void)
+{
+ int x;
+
+ if (rnd(1501) < 2) {
+ lprcat("\nOops! You seem to have caught the dreadful sleep!");
+ beep();
+ lflush();
+ sleep(3);
+ died(280);
+ return;
+ }
+ x = rnd(100);
+ if (x < 7) {
+ c[HALFDAM] += 200 + rnd(200);
+ lprcat("\nYou feel a sickness coming on");
+ } else if (x < 13)
+ quaffpotion(23); /* see invisible */
+ else if (x < 45)
+ lprcat("\nnothing seems to have happened");
+ else if (rnd(3) != 2)
+ fntchange(1); /* change char levels upward */
+ else
+ fntchange(-1); /* change char levels
+ * downward */
+ if (rnd(12) < 3) {
+ lprcat("\nThe fountains bubbling slowly quiets");
+ item[playerx][playery] = ODEADFOUNTAIN; /* dead fountain */
+ know[playerx][playery] = 0;
+ }
+}
+
+/*
+ * Code to perform the action of washing at a fountain. Assumes that
+ * cursors() has already been called and that a check has been made
+ * that the player is actually standing at a live fountain.
+ */
+void
+act_wash_fountain(void)
+{
+ int x;
+
+ if (rnd(100) < 11) {
+ x = rnd((level << 2) + 2);
+ lprintf("\nOh no! The water was foul! You suffer %ld hit points!", (long) x);
+ lastnum = 273;
+ losehp(x);
+ bottomline();
+ cursors();
+ } else if (rnd(100) < 29)
+ lprcat("\nYou got the dirt off!");
+ else if (rnd(100) < 31)
+ lprcat("\nThis water seems to be hard water! The dirt didn't come off!");
+ else if (rnd(100) < 34)
+ createmonster(WATERLORD); /* make water lord */
+ else
+ lprcat("\nnothing seems to have happened");
+}
+
+/*
+ * Perform the actions associated with altar desecration.
+ */
+void
+act_desecrate_altar(void)
+{
+ if (rnd(100) < 60) {
+ createmonster(makemonst(level + 2) + 8);
+ c[AGGRAVATE] += 2500;
+ } else if (rnd(101) < 30) {
+ lprcat("\nThe altar crumbles into a pile of dust before your eyes");
+ forget(); /* remember to destroy
+ * the altar */
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * Perform the actions associated with praying at an altar and giving
+ * a donation.
+ */
+void
+act_donation_pray(void)
+{
+ long amt;
+
+ lprcat("\n\n");
+ cursor(1, 24);
+ cltoeoln();
+ cursor(1, 23);
+ cltoeoln();
+ lprcat("how much do you donate? ");
+ amt = readnum((long) c[GOLD]);
+ if (amt < 0 || c[GOLD] < amt) {
+ lprcat("\nYou don't have that much!");
+ return;
+ }
+ c[GOLD] -= amt;
+ if (amt < c[GOLD] / 10 || amt < rnd(50)) {
+ createmonster(makemonst(level + 1));
+ c[AGGRAVATE] += 200;
+ } else if (rnd(101) > 50) {
+ ohear();
+ return;
+ } else if (rnd(43) == 5) {
+ if (c[WEAR])
+ lprcat("\nYou feel your armor vibrate for a moment");
+ enchantarmor();
+ return;
+ } else if (rnd(43) == 8) {
+ if (c[WIELD])
+ lprcat("\nYou feel your weapon vibrate for a moment");
+ enchweapon();
+ return;
+ } else
+ lprcat("\nThank You.");
+ bottomline();
+}
+
+/*
+ * Performs the actions associated with 'just praying' at the altar. Called
+ * when the user responds 'just pray' when in prompt mode, or enters 0 to
+ * the money prompt when praying.
+ *
+ * Assumes cursors(), and that any leading \n have been printed.
+ */
+void
+act_just_pray(void)
+{
+ if (rnd(100) < 75)
+ lprcat("\nnothing happens");
+ else if (rnd(13) < 4)
+ ohear();
+ else if (rnd(43) == 10) {
+ if (c[WEAR])
+ lprcat("\nYou feel your armor vibrate for a moment");
+ enchantarmor();
+ return;
+ } else if (rnd(43) == 10) {
+ if (c[WIELD])
+ lprcat("\nYou feel your weapon vibrate for a moment");
+ enchweapon();
+ return;
+ } else
+ createmonster(makemonst(level + 1));
+}
+
+/*
+ * Function to cast a +3 protection on the player
+ */
+static void
+ohear(void)
+{
+ lprcat("\nYou have been heard!");
+ if (c[ALTPRO] == 0)
+ c[MOREDEFENSES] += 3;
+ c[ALTPRO] += 500; /* protection field */
+ bottomline();
+}
+
+/*
+ * Performs the act of ignoring an altar.
+ *
+ * Assumptions: cursors() has been called.
+ */
+void
+act_ignore_altar(void)
+{
+ if (rnd(100) < 30) {
+ createmonster(makemonst(level + 1));
+ c[AGGRAVATE] += rnd(450);
+ } else
+ lprcat("\nnothing happens");
+}
+
+/*
+ * Performs the act of opening a chest.
+ *
+ * Parameters: x,y location of the chest to open.
+ * Assumptions: cursors() has been called previously
+ */
+void
+act_open_chest(int x, int y)
+{
+ int i, k;
+
+ k = rnd(101);
+ if (k < 40) {
+ lprcat("\nThe chest explodes as you open it");
+ beep();
+ i = rnd(10);
+ lastnum = 281; /* in case he dies */
+ lprintf("\nYou suffer %ld hit points damage!", (long) i);
+ checkloss(i);
+ switch (rnd(10)) { /* see if he gets a
+ * curse */
+ case 1:
+ c[ITCHING] += rnd(1000) + 100;
+ lprcat("\nYou feel an irritation spread over your skin!");
+ beep();
+ break;
+
+ case 2:
+ c[CLUMSINESS] += rnd(1600) + 200;
+ lprcat("\nYou begin to lose hand to eye coordination!");
+ beep();
+ break;
+
+ case 3:
+ c[HALFDAM] += rnd(1600) + 200;
+ beep();
+ lprcat("\nA sickness engulfs you!");
+ break;
+ };
+ item[x][y] = know[x][y] = 0;
+ if (rnd(100) < 69)
+ creategem(); /* gems from the chest */
+ dropgold(rnd(110 * iarg[x][y] + 200));
+ for (i = 0; i < rnd(4); i++)
+ something(iarg[x][y] + 2);
+ } else
+ lprcat("\nnothing happens");
+}
diff --git a/larn/bill.c b/larn/bill.c
new file mode 100644
index 0000000..961c424
--- /dev/null
+++ b/larn/bill.c
@@ -0,0 +1,163 @@
+/* $NetBSD: bill.c,v 1.11 2011/08/29 20:30:37 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1991 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)bill.c 5.2 (Berkeley) 5/28/91";
+#else
+__RCSID("$NetBSD: bill.c,v 1.11 2011/08/29 20:30:37 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <paths.h>
+#include "header.h"
+#include "extern.h"
+
+/* bill.c Larn is copyrighted 1986 by Noah Morgan. */
+
+static const char *mail[] = {
+ "From: the LRS (Larn Revenue Service)\n",
+ "~s undeclared income\n",
+ "\n We have heard you survived the caverns of Larn. Let me be the",
+ "\nfirst to congratulate you on your success. It was quite a feat.",
+ "\nIt was also very profitable for you...",
+ "\n\n The Dungeon Master has informed us that you brought",
+ "1",
+ "\ncounty of Larn is in dire need of funds, we have spared no time",
+ "2",
+ "\nof this notice, and is due within 5 days. Failure to pay will",
+ "\nmean penalties. Once again, congratulations, We look forward",
+ "\nto your future successful expeditions.\n",
+ NULL,
+ "From: His Majesty King Wilfred of Larndom\n",
+ "~s a noble deed\n",
+ "\n I have heard of your magnificent feat, and I, King Wilfred,",
+ "\nforthwith declare today to be a national holiday. Furthermore,",
+ "\nhence three days, ye be invited to the castle to receive the",
+ "\nhonour of Knight of the realm. Upon thy name shall it be written...",
+ "\n\nBravery and courage be yours.",
+ "\n\nMay you live in happiness forevermore...\n",
+ NULL,
+ "From: Count Endelford\n",
+ "~s You Bastard!\n",
+ "\n I have heard (from sources) of your journey. Congratulations!",
+ "\nYou Bastard! With several attempts I have yet to endure the",
+ " caves,\nand you, a nobody, makes the journey! From this time",
+ " onward, bewarned\nupon our meeting you shall pay the price!\n",
+ NULL,
+ "From: Mainair, Duke of Larnty\n",
+ "~s High Praise\n",
+ "\n With certainty, a hero I declare to be amongst us! A nod of",
+ "\nfavour I send to thee. Me thinks Count Endelford this day of",
+ "\nright breath'eth fire as of dragon of whom ye are slayer. I",
+ "\nyearn to behold his anger and jealously. Should ye choose to",
+ "\nunleash some of thy wealth upon those who be unfortunate, I,",
+ "\nDuke Mainair, shall equal thy gift also.\n",
+ NULL,
+ "From: St. Mary's Children's Home\n",
+ "~s these poor children\n",
+ "\n News of your great conquests has spread to all of Larndom.",
+ "\nMight I have a moment of a great adventurers's time? We here at",
+ "\nSt. Mary's Children's Home are very poor, and many children are",
+ "\nstarving. Disease is widespread and very often fatal without",
+ "\ngood food. Could you possibly find it in your heart to help us",
+ "\nin our plight? Whatever you could give will help much.",
+ "\n(your gift is tax deductible)\n",
+ NULL,
+ "From: The National Cancer Society of Larn\n",
+ "~s hope\n",
+ "\nCongratulations on your successful expedition. We are sure much",
+ "\ncourage and determination were needed on your quest. There are",
+ "\nmany though, that could never hope to undertake such a journey",
+ "\ndue to an enfeebling disease -- cancer. We at the National",
+ "\nCancer Society of Larn wish to appeal to your philanthropy in",
+ "\norder to save many good people -- possibly even yourself a few",
+ "\nyears from now. Much work needs to be done in researching this",
+ "\ndreaded disease, and you can help today. Could you please see it",
+ "\nin your heart to give generously? Your continued good health",
+ "\ncan be your everlasting reward.\n",
+ NULL
+};
+
+/*
+ * function to mail the letters to the player if a winner
+ */
+
+void
+mailbill(void)
+{
+ int i;
+ char fname[32];
+ char buf[128];
+ const char **cp;
+ int fd;
+
+ wait(0);
+ if (fork() == 0) {
+ resetscroll();
+ cp = mail;
+ snprintf(fname, sizeof(fname), "%slarnmail.XXXXXX", _PATH_TMP);
+ for (i = 0; i < 6; i++) {
+ if ((fd = mkstemp(fname)) == -1)
+ exit(0);
+ while (*cp != NULL) {
+ if (*cp[0] == '1') {
+ snprintf(buf, sizeof(buf),
+ "\n%ld gold pieces back with you from your journey. As the",
+ (long) c[GOLD]);
+ write(fd, buf, strlen(buf));
+ } else if (*cp[0] == '2') {
+ snprintf(buf, sizeof(buf),
+ "\nin preparing your tax bill. You owe %ld gold pieces as",
+ (long) c[GOLD] * TAXRATE);
+ write(fd, buf, strlen(buf));
+ } else
+ write(fd, *cp, strlen(*cp));
+ cp++;
+ }
+ cp++;
+
+ close(fd);
+ snprintf(buf, sizeof(buf),
+ "mail -I %s < %s > /dev/null", loginname, fname);
+ system(buf);
+ unlink(fname);
+ }
+ }
+ exit(0);
+}
diff --git a/larn/config.c b/larn/config.c
new file mode 100644
index 0000000..47aa03a
--- /dev/null
+++ b/larn/config.c
@@ -0,0 +1,50 @@
+/* $NetBSD: config.c,v 1.6 2008/01/28 05:38:53 dholland Exp $ */
+
+/*
+ * config.c -- This defines the installation dependent variables.
+ * Some strings are modified later. ANSI C would
+ * allow compile time string concatenation, we must
+ * do runtime concatenation, in main.
+ *
+ * Larn is copyrighted 1986 by Noah Morgan.
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: config.c,v 1.6 2008/01/28 05:38:53 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "pathnames.h"
+
+/*
+ * All these strings will be appended to in main() to be complete filenames
+ */
+
+/* the game save filename */
+char savefilename[1024];
+
+/* the logging file */
+char logfile[] = _PATH_LOG;
+
+/* the help text file */
+char helpfile[] = _PATH_HELP;
+
+/* the score file */
+char scorefile[] = _PATH_SCORE;
+
+/* the maze data file */
+char larnlevels[] = _PATH_LEVELS;
+
+/* the .larnopts filename */
+char optsfile[1024] = "/.larnopts";
+
+/* the player id datafile name */
+char playerids[] = _PATH_PLAYERIDS;
+
+char diagfile[] = "Diagfile"; /* the diagnostic filename */
+char ckpfile[] = "Larn12.0.ckp"; /* the checkpoint filename */
+const char *password = "pvnert(x)"; /* the wizards password <=32 */
+char psname[PSNAMESIZE] = "larn"; /* the process name */
+
+#define WIZID 1
+int wisid = 0; /* the user id of the only person who can be wizard */
diff --git a/larn/create.c b/larn/create.c
new file mode 100644
index 0000000..440b918
--- /dev/null
+++ b/larn/create.c
@@ -0,0 +1,606 @@
+/* $NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $ */
+
+/* create.c Larn is copyrighted 1986 by Noah Morgan. */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+#include <unistd.h>
+
+static void makemaze(int);
+static int cannedlevel(int);
+static void treasureroom(int);
+static void troom(int, int, int, int, int, int);
+static void makeobject(int);
+static void fillmroom(int, int, int);
+static void froom(int, int, int);
+static void fillroom(int, int);
+static void sethp(int);
+static void checkgen(void);
+
+/*
+ makeplayer()
+
+ subroutine to create the player and the players attributes
+ this is called at the beginning of a game and at no other time
+ */
+void
+makeplayer(void)
+{
+ int i;
+ scbr();
+ clear();
+ c[HPMAX] = c[HP] = 10; /* start player off with 15 hit points */
+ c[LEVEL] = 1; /* player starts at level one */
+ c[SPELLMAX] = c[SPELLS] = 1; /* total # spells starts off as 3 */
+ c[REGENCOUNTER] = 16;
+ c[ECOUNTER] = 96; /* start regeneration correctly */
+ c[SHIELD] = c[WEAR] = c[WIELD] = -1;
+ for (i = 0; i < 26; i++)
+ iven[i] = 0;
+ spelknow[0] = spelknow[1] = 1; /* he knows protection, magic missile */
+ if (c[HARDGAME] <= 0) {
+ iven[0] = OLEATHER;
+ iven[1] = ODAGGER;
+ ivenarg[1] = ivenarg[0] = c[WEAR] = 0;
+ c[WIELD] = 1;
+ }
+ playerx = rnd(MAXX - 2);
+ playery = rnd(MAXY - 2);
+ oldx = 0;
+ oldy = 25;
+ gltime = 0; /* time clock starts at zero */
+ cbak[SPELLS] = -50;
+ for (i = 0; i < 6; i++)
+ c[i] = 12; /* make the attributes, ie str, int, etc. */
+ recalc();
+}
+
+
+/*
+ newcavelevel(level)
+ int level;
+
+ function to enter a new level. This routine must be called anytime the
+ player changes levels. If that level is unknown it will be created.
+ A new set of monsters will be created for a new level, and existing
+ levels will get a few more monsters.
+ Note that it is here we remove genocided monsters from the present level.
+ */
+void
+newcavelevel(int x)
+{
+ int i, j;
+ if (beenhere[level])
+ savelevel(); /* put the level back into storage */
+ level = x; /* get the new level and put in working
+ * storage */
+ if (beenhere[x]) {
+ getlevel();
+ sethp(0);
+ checkgen();
+ return;
+ }
+
+ /* fill in new level */
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = mitem[j][i] = 0;
+ makemaze(x);
+ makeobject(x);
+ beenhere[x] = 1;
+ sethp(1);
+ checkgen(); /* wipe out any genocided monsters */
+
+#if WIZID
+ if (wizard || x == 0)
+#else
+ if (x == 0)
+#endif
+ for (j = 0; j < MAXY; j++)
+ for (i = 0; i < MAXX; i++)
+ know[i][j] = 1;
+}
+
+/*
+ makemaze(level)
+ int level;
+
+ subroutine to make the caverns for a given level. only walls are made.
+ */
+static int mx, mxl, mxh, my, myl, myh, tmp2;
+
+static void
+makemaze(int k)
+{
+ int i, j, tmp;
+ int z;
+ if (k > 1 && (rnd(17) <= 4 || k == MAXLEVEL - 1 || k == MAXLEVEL + MAXVLEVEL - 1)) {
+ if (cannedlevel(k))
+ return; /* read maze from data file */
+ }
+ if (k == 0)
+ tmp = 0;
+ else
+ tmp = OWALL;
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ item[j][i] = tmp;
+ if (k == 0)
+ return;
+ eat(1, 1);
+ if (k == 1)
+ item[33][MAXY - 1] = 0; /* exit from dungeon */
+
+ /* now for open spaces -- not on level 10 */
+ if (k != MAXLEVEL - 1) {
+ tmp2 = rnd(3) + 3;
+ for (tmp = 0; tmp < tmp2; tmp++) {
+ my = rnd(11) + 2;
+ myl = my - rnd(2);
+ myh = my + rnd(2);
+ if (k < MAXLEVEL) {
+ mx = rnd(44) + 5;
+ mxl = mx - rnd(4);
+ mxh = mx + rnd(12) + 3;
+ z = 0;
+ } else {
+ mx = rnd(60) + 3;
+ mxl = mx - rnd(2);
+ mxh = mx + rnd(2);
+ z = makemonst(k);
+ }
+ for (i = mxl; i < mxh; i++)
+ for (j = myl; j < myh; j++) {
+ item[i][j] = 0;
+ if ((mitem[i][j] = z))
+ hitp[i][j] = monster[z].hitpoints;
+ }
+ }
+ }
+ if (k != MAXLEVEL - 1) {
+ my = rnd(MAXY - 2);
+ for (i = 1; i < MAXX - 1; i++)
+ item[i][my] = 0;
+ }
+ if (k > 1)
+ treasureroom(k);
+}
+
+/*
+ function to eat away a filled in maze
+ */
+void
+eat(int xx, int yy)
+{
+ int dir, try;
+ dir = rnd(4);
+ try = 2;
+ while (try) {
+ switch (dir) {
+ case 1:
+ if (xx <= 2)
+ break; /* west */
+ if ((item[xx - 1][yy] != OWALL) || (item[xx - 2][yy] != OWALL))
+ break;
+ item[xx - 1][yy] = item[xx - 2][yy] = 0;
+ eat(xx - 2, yy);
+ break;
+
+ case 2:
+ if (xx >= MAXX - 3)
+ break; /* east */
+ if ((item[xx + 1][yy] != OWALL) || (item[xx + 2][yy] != OWALL))
+ break;
+ item[xx + 1][yy] = item[xx + 2][yy] = 0;
+ eat(xx + 2, yy);
+ break;
+
+ case 3:
+ if (yy <= 2)
+ break; /* south */
+ if ((item[xx][yy - 1] != OWALL) || (item[xx][yy - 2] != OWALL))
+ break;
+ item[xx][yy - 1] = item[xx][yy - 2] = 0;
+ eat(xx, yy - 2);
+ break;
+
+ case 4:
+ if (yy >= MAXY - 3)
+ break; /* north */
+ if ((item[xx][yy + 1] != OWALL) || (item[xx][yy + 2] != OWALL))
+ break;
+ item[xx][yy + 1] = item[xx][yy + 2] = 0;
+ eat(xx, yy + 2);
+ break;
+ };
+ if (++dir > 4) {
+ dir = 1;
+ --try;
+ }
+ }
+}
+
+/*
+ * function to read in a maze from a data file
+ *
+ * Format of maze data file: 1st character = # of mazes in file (ascii digit)
+ * For each maze: 18 lines (1st 17 used) 67 characters per line
+ *
+ * Special characters in maze data file:
+ *
+ * # wall D door . random monster
+ * ~ eye of larn ! cure dianthroritis
+ * - random object
+ */
+static int
+cannedlevel(int k)
+{
+ char *row;
+ int i, j;
+ int it, arg, mit, marg;
+ if (lopen(larnlevels) < 0) {
+ write(1, "Can't open the maze data file\n", 30);
+ died(-282);
+ return (0);
+ }
+ i = lgetc();
+ if (i <= '0') {
+ died(-282);
+ return (0);
+ }
+ for (i = 18 * rund(i - '0'); i > 0; i--)
+ lgetl(); /* advance to desired maze */
+ for (i = 0; i < MAXY; i++) {
+ row = lgetl();
+ for (j = 0; j < MAXX; j++) {
+ it = mit = arg = marg = 0;
+ switch (*row++) {
+ case '#':
+ it = OWALL;
+ break;
+ case 'D':
+ it = OCLOSEDDOOR;
+ arg = rnd(30);
+ break;
+ case '~':
+ if (k != MAXLEVEL - 1)
+ break;
+ it = OLARNEYE;
+ mit = rund(8) + DEMONLORD;
+ marg = monster[mit].hitpoints;
+ break;
+ case '!':
+ if (k != MAXLEVEL + MAXVLEVEL - 1)
+ break;
+ it = OPOTION;
+ arg = 21;
+ mit = DEMONLORD + 7;
+ marg = monster[mit].hitpoints;
+ break;
+ case '.':
+ if (k < MAXLEVEL)
+ break;
+ mit = makemonst(k + 1);
+ marg = monster[mit].hitpoints;
+ break;
+ case '-':
+ it = newobject(k + 1, &arg);
+ break;
+ };
+ item[j][i] = it;
+ iarg[j][i] = arg;
+ mitem[j][i] = mit;
+ hitp[j][i] = marg;
+
+#if WIZID
+ know[j][i] = (wizard) ? 1 : 0;
+#else
+ know[j][i] = 0;
+#endif
+ }
+ }
+ lrclose();
+ return (1);
+}
+
+/*
+ function to make a treasure room on a level
+ level 10's treasure room has the eye in it and demon lords
+ level V3 has potion of cure dianthroritis and demon prince
+ */
+static void
+treasureroom(int lv)
+{
+ int tx, ty, xsize, ysize;
+
+ for (tx = 1 + rnd(10); tx < MAXX - 10; tx += 10)
+ if ((lv == MAXLEVEL - 1) || (lv == MAXLEVEL + MAXVLEVEL - 1) || rnd(13) == 2) {
+ xsize = rnd(6) + 3;
+ ysize = rnd(3) + 3;
+ ty = rnd(MAXY - 9) + 1; /* upper left corner of room */
+ if (lv == MAXLEVEL - 1 || lv == MAXLEVEL + MAXVLEVEL - 1)
+ troom(lv, xsize, ysize, tx = tx + rnd(MAXX - 24), ty, rnd(3) + 6);
+ else
+ troom(lv, xsize, ysize, tx, ty, rnd(9));
+ }
+}
+
+/*
+ * subroutine to create a treasure room of any size at a given location
+ * room is filled with objects and monsters
+ * the coordinate given is that of the upper left corner of the room
+ */
+static void
+troom(int lv, int xsize, int ysize, int tx, int ty, int glyph)
+{
+ int i, j;
+ int tp1, tp2;
+ for (j = ty - 1; j <= ty + ysize; j++)
+ for (i = tx - 1; i <= tx + xsize; i++) /* clear out space for
+ * room */
+ item[i][j] = 0;
+ for (j = ty; j < ty + ysize; j++)
+ for (i = tx; i < tx + xsize; i++) { /* now put in the walls */
+ item[i][j] = OWALL;
+ mitem[i][j] = 0;
+ }
+ for (j = ty + 1; j < ty + ysize - 1; j++)
+ for (i = tx + 1; i < tx + xsize - 1; i++) /* now clear out
+ * interior */
+ item[i][j] = 0;
+
+ switch (rnd(2)) { /* locate the door on the treasure room */
+ case 1:
+ item[i = tx + rund(xsize)][j = ty + (ysize - 1) * rund(2)] = OCLOSEDDOOR;
+ iarg[i][j] = glyph; /* on horizontal walls */
+ break;
+ case 2:
+ item[i = tx + (xsize - 1) * rund(2)][j = ty + rund(ysize)] = OCLOSEDDOOR;
+ iarg[i][j] = glyph; /* on vertical walls */
+ break;
+ };
+
+ tp1 = playerx;
+ tp2 = playery;
+ playery = ty + (ysize >> 1);
+ if (c[HARDGAME] < 2)
+ for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
+ for (i = 0, j = rnd(6); i <= j; i++) {
+ something(lv + 2);
+ createmonster(makemonst(lv + 1));
+ }
+ else
+ for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
+ for (i = 0, j = rnd(4); i <= j; i++) {
+ something(lv + 2);
+ createmonster(makemonst(lv + 3));
+ }
+
+ playerx = tp1;
+ playery = tp2;
+}
+
+
+/*
+ ***********
+ MAKE_OBJECT
+ ***********
+ subroutine to create the objects in the maze for the given level
+ */
+static void
+makeobject(int j)
+{
+ int i;
+ if (j == 0) {
+ fillroom(OENTRANCE, 0); /* entrance to dungeon */
+ fillroom(ODNDSTORE, 0); /* the DND STORE */
+ fillroom(OSCHOOL, 0); /* college of Larn */
+ fillroom(OBANK, 0); /* 1st national bank of larn */
+ fillroom(OVOLDOWN, 0); /* volcano shaft to temple */
+ fillroom(OHOME, 0); /* the players home & family */
+ fillroom(OTRADEPOST, 0); /* the trading post */
+ fillroom(OLRS, 0); /* the larn revenue service */
+ return;
+ }
+ if (j == MAXLEVEL)
+ fillroom(OVOLUP, 0); /* volcano shaft up from the temple */
+
+ /* make the fixed objects in the maze STAIRS */
+ if ((j > 0) && (j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
+ fillroom(OSTAIRSDOWN, 0);
+ if ((j > 1) && (j != MAXLEVEL))
+ fillroom(OSTAIRSUP, 0);
+
+ /* make the random objects in the maze */
+
+ fillmroom(rund(3), OBOOK, j);
+ fillmroom(rund(3), OALTAR, 0);
+ fillmroom(rund(3), OSTATUE, 0);
+ fillmroom(rund(3), OPIT, 0);
+ fillmroom(rund(3), OFOUNTAIN, 0);
+ fillmroom(rnd(3) - 2, OIVTELETRAP, 0);
+ fillmroom(rund(2), OTHRONE, 0);
+ fillmroom(rund(2), OMIRROR, 0);
+ fillmroom(rund(2), OTRAPARROWIV, 0);
+ fillmroom(rnd(3) - 2, OIVDARTRAP, 0);
+ fillmroom(rund(3), OCOOKIE, 0);
+ if (j == 1)
+ fillmroom(1, OCHEST, j);
+ else
+ fillmroom(rund(2), OCHEST, j);
+ if ((j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
+ fillmroom(rund(2), OIVTRAPDOOR, 0);
+ if (j <= 10) {
+ fillmroom((rund(2)), ODIAMOND, rnd(10 * j + 1) + 10);
+ fillmroom(rund(2), ORUBY, rnd(6 * j + 1) + 6);
+ fillmroom(rund(2), OEMERALD, rnd(4 * j + 1) + 4);
+ fillmroom(rund(2), OSAPPHIRE, rnd(3 * j + 1) + 2);
+ }
+ for (i = 0; i < rnd(4) + 3; i++)
+ fillroom(OPOTION, newpotion()); /* make a POTION */
+ for (i = 0; i < rnd(5) + 3; i++)
+ fillroom(OSCROLL, newscroll()); /* make a SCROLL */
+ for (i = 0; i < rnd(12) + 11; i++)
+ fillroom(OGOLDPILE, 12 * rnd(j + 1) + (j << 3) + 10); /* make GOLD */
+ if (j == 5)
+ fillroom(OBANK2, 0); /* branch office of the bank */
+ froom(2, ORING, 0); /* a ring mail */
+ froom(1, OSTUDLEATHER, 0); /* a studded leather */
+ froom(3, OSPLINT, 0); /* a splint mail */
+ froom(5, OSHIELD, rund(3)); /* a shield */
+ froom(2, OBATTLEAXE, rund(3)); /* a battle axe */
+ froom(5, OLONGSWORD, rund(3)); /* a long sword */
+ froom(5, OFLAIL, rund(3)); /* a flail */
+ froom(4, OREGENRING, rund(3)); /* ring of regeneration */
+ froom(1, OPROTRING, rund(3)); /* ring of protection */
+ froom(2, OSTRRING, 4); /* ring of strength + 4 */
+ froom(7, OSPEAR, rnd(5)); /* a spear */
+ froom(3, OORBOFDRAGON, 0); /* orb of dragon slaying */
+ froom(4, OSPIRITSCARAB, 0); /* scarab of negate spirit */
+ froom(4, OCUBEofUNDEAD, 0); /* cube of undead control */
+ froom(2, ORINGOFEXTRA, 0); /* ring of extra regen */
+ froom(3, ONOTHEFT, 0); /* device of antitheft */
+ froom(2, OSWORDofSLASHING, 0); /* sword of slashing */
+ if (c[BESSMANN] == 0) {
+ froom(4, OHAMMER, 0); /* Bessman's flailing hammer */
+ c[BESSMANN] = 1;
+ }
+ if (c[HARDGAME] < 3 || (rnd(4) == 3)) {
+ if (j > 3) {
+ froom(3, OSWORD, 3); /* sunsword + 3 */
+ froom(5, O2SWORD, rnd(4)); /* a two handed sword */
+ froom(3, OBELT, 4); /* belt of striking */
+ froom(3, OENERGYRING, 3); /* energy ring */
+ froom(4, OPLATE, 5); /* platemail + 5 */
+ }
+ }
+}
+
+/*
+ subroutine to fill in a number of objects of the same kind
+ */
+
+static void
+fillmroom(int n, int what_i, int arg)
+{
+ int i;
+ char what;
+
+ /* truncate to char width (just in case it matters) */
+ what = (char)what_i;
+ for (i = 0; i < n; i++)
+ fillroom(what, arg);
+}
+
+static void
+froom(int n, int theitem, int arg)
+{
+ if (rnd(151) < n)
+ fillroom(theitem, arg);
+}
+
+/*
+ subroutine to put an object into an empty room
+ * uses a random walk
+ */
+static void
+fillroom(int what_i, int arg)
+{
+ int x, y;
+ char what;
+
+ /* truncate to char width (just in case it matters) */
+ what = (char)what_i;
+
+#ifdef EXTRA
+ c[FILLROOM]++;
+#endif
+
+ x = rnd(MAXX - 2);
+ y = rnd(MAXY - 2);
+ while (item[x][y]) {
+
+#ifdef EXTRA
+ c[RANDOMWALK]++;/* count up these random walks */
+#endif
+
+ x += rnd(3) - 2;
+ y += rnd(3) - 2;
+ if (x > MAXX - 2)
+ x = 1;
+ if (x < 1)
+ x = MAXX - 2;
+ if (y > MAXY - 2)
+ y = 1;
+ if (y < 1)
+ y = MAXY - 2;
+ }
+ item[x][y] = what;
+ iarg[x][y] = arg;
+}
+
+/*
+ subroutine to put monsters into an empty room without walls or other
+ monsters
+ */
+int
+fillmonst(int what)
+{
+ int x, y, trys;
+ for (trys = 5; trys > 0; --trys) { /* max # of creation attempts */
+ x = rnd(MAXX - 2);
+ y = rnd(MAXY - 2);
+ if ((item[x][y] == 0) && (mitem[x][y] == 0) && ((playerx != x) || (playery != y))) {
+ mitem[x][y] = what;
+ know[x][y] = 0;
+ hitp[x][y] = monster[what].hitpoints;
+ return (0);
+ }
+ }
+ return (-1); /* creation failure */
+}
+
+/*
+ creates an entire set of monsters for a level
+ must be done when entering a new level
+ if sethp(1) then wipe out old monsters else leave them there
+ */
+static void
+sethp(int flg)
+{
+ int i, j;
+ if (flg)
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ stealth[j][i] = 0;
+ if (level == 0) {
+ c[TELEFLAG] = 0;
+ return;
+ } /* if teleported and found level 1 then know
+ * level we are on */
+ if (flg)
+ j = rnd(12) + 2 + (level >> 1);
+ else
+ j = (level >> 1) + 1;
+ for (i = 0; i < j; i++)
+ fillmonst(makemonst(level));
+ positionplayer();
+}
+
+/*
+ * Function to destroy all genocided monsters on the present level
+ */
+static void
+checkgen(void)
+{
+ int x, y;
+ for (y = 0; y < MAXY; y++)
+ for (x = 0; x < MAXX; x++)
+ if (monster[mitem[x][y]].genocided)
+ mitem[x][y] = 0; /* no more monster */
+}
diff --git a/larn/data.c b/larn/data.c
new file mode 100644
index 0000000..4f35994
--- /dev/null
+++ b/larn/data.c
@@ -0,0 +1,664 @@
+/* $NetBSD: data.c,v 1.13 2008/02/03 20:11:04 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)data.c 5.3 (Berkeley) 5/13/91";
+#else
+__RCSID("$NetBSD: data.c,v 1.13 2008/02/03 20:11:04 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/* data.c Larn is copyrighted 1986 by Noah Morgan. */
+/* #define NODEFS */
+#include "header.h"
+#include "extern.h"
+
+/*
+ class[c[LEVEL]-1] gives the correct name of the players experience level
+ */
+static char aa1[] = " mighty evil master";
+static char aa2[] = "apprentice demi-god";
+static char aa3[] = " minor demi-god ";
+static char aa4[] = " major demi-god ";
+static char aa5[] = " minor deity ";
+static char aa6[] = " major deity ";
+static char aa7[] = " novice guardian ";
+static char aa8[] = "apprentice guardian";
+static char aa9[] = " The Creator ";
+const char *class[] =
+{
+ " novice explorer ", "apprentice explorer", " practiced explorer", /* -3 */
+ " expert explorer ", " novice adventurer", " adventurer ", /* -6 */
+ "apprentice conjurer", " conjurer ", " master conjurer ", /* -9 */
+ " apprentice mage ", " mage ", " experienced mage ", /* -12 */
+ " master mage ", " apprentice warlord", " novice warlord ", /* -15 */
+ " expert warlord ", " master warlord ", " apprentice gorgon ", /* -18 */
+ " gorgon ", " practiced gorgon ", " master gorgon ", /* -21 */
+ " demi-gorgon ", " evil master ", " great evil master ", /* -24 */
+ aa1, aa1, aa1, /* -27 */
+ aa1, aa1, aa1, /* -30 */
+ aa1, aa1, aa1, /* -33 */
+ aa1, aa1, aa1, /* -36 */
+ aa1, aa1, aa1, /* -39 */
+ aa2, aa2, aa2, /* -42 */
+ aa2, aa2, aa2, /* -45 */
+ aa2, aa2, aa2, /* -48 */
+ aa3, aa3, aa3, /* -51 */
+ aa3, aa3, aa3, /* -54 */
+ aa3, aa3, aa3, /* -57 */
+ aa4, aa4, aa4, /* -60 */
+ aa4, aa4, aa4, /* -63 */
+ aa4, aa4, aa4, /* -66 */
+ aa5, aa5, aa5, /* -69 */
+ aa5, aa5, aa5, /* -72 */
+ aa5, aa5, aa5, /* -75 */
+ aa6, aa6, aa6, /* -78 */
+ aa6, aa6, aa6, /* -81 */
+ aa6, aa6, aa6, /* -84 */
+ aa7, aa7, aa7, /* -87 */
+ aa8, aa8, aa8, /* -90 */
+ aa8, aa8, aa8, /* -93 */
+ " earth guardian ", " air guardian ", " fire guardian ", /* -96 */
+ " water guardian ", " time guardian ", " ethereal guardian ", /* -99 */
+ aa9, aa9, aa9, /* -102 */
+};
+
+/*
+ table of experience needed to be a certain level of player
+ skill[c[LEVEL]] is the experience required to attain the next level
+ */
+#define MEG 1000000
+long skill[] = {
+ 0, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, /* 1-11 */
+ 10240, 20480, 40960, 100000, 200000, 400000, 700000, 1 * MEG, /* 12-19 */
+ 2 * MEG, 3 * MEG, 4 * MEG, 5 * MEG, 6 * MEG, 8 * MEG, 10 * MEG, /* 20-26 */
+ 12 * MEG, 14 * MEG, 16 * MEG, 18 * MEG, 20 * MEG, 22 * MEG, 24 * MEG, 26 * MEG, 28 * MEG, /* 27-35 */
+ 30 * MEG, 32 * MEG, 34 * MEG, 36 * MEG, 38 * MEG, 40 * MEG, 42 * MEG, 44 * MEG, 46 * MEG, /* 36-44 */
+ 48 * MEG, 50 * MEG, 52 * MEG, 54 * MEG, 56 * MEG, 58 * MEG, 60 * MEG, 62 * MEG, 64 * MEG, /* 45-53 */
+ 66 * MEG, 68 * MEG, 70 * MEG, 72 * MEG, 74 * MEG, 76 * MEG, 78 * MEG, 80 * MEG, 82 * MEG, /* 54-62 */
+ 84 * MEG, 86 * MEG, 88 * MEG, 90 * MEG, 92 * MEG, 94 * MEG, 96 * MEG, 98 * MEG, 100 * MEG, /* 63-71 */
+ 105 * MEG, 110 * MEG, 115 * MEG, 120 * MEG, 125 * MEG, 130 * MEG, 135 * MEG, 140 * MEG, /* 72-79 */
+ 145 * MEG, 150 * MEG, 155 * MEG, 160 * MEG, 165 * MEG, 170 * MEG, 175 * MEG, 180 * MEG, /* 80-87 */
+ 185 * MEG, 190 * MEG, 195 * MEG, 200 * MEG, 210 * MEG, 220 * MEG, 230 * MEG, 240 * MEG, /* 88-95 */
+ 250 * MEG, 260 * MEG, 270 * MEG, 280 * MEG, 290 * MEG, 300 * MEG /* 96-101 */
+};
+#undef MEG
+
+u_char *lpbuf, *lpnt, *inbuffer, *lpend; /* input/output pointers
+ * to the buffers */
+struct cel *cell; /* pointer to the dungeon storage */
+short hitp[MAXX][MAXY]; /* monster hp on level */
+short iarg[MAXX][MAXY]; /* arg for the item array */
+u_char item[MAXX][MAXY]; /* objects in maze if any */
+u_char know[MAXX][MAXY]; /* 1 or 0 if here before */
+u_char mitem[MAXX][MAXY]; /* monster item array */
+u_char moved[MAXX][MAXY]; /* monster movement flags */
+u_char stealth[MAXX][MAXY]; /* 0=sleeping 1=awake monst */
+u_char iven[26]; /* inventory for player */
+short ivenarg[26]; /* inventory for player */
+char lastmonst[40]; /* this has the name of the current monster */
+u_char beenhere[MAXLEVEL + MAXVLEVEL] = {0}; /* 1 if have been on
+ * this level */
+char VERSION = VER; /* this is the present version # of the
+ * program */
+char SUBVERSION = SUBVER;
+u_char nosignal = 0; /* set to 1 to disable the signals from doing
+ * anything */
+u_char predostuff = 0; /* 2 means that the trap handling routines
+ * must do a showplayer() after a trap. 0
+ * means don't showplayer() 0 - we are in
+ * create player screen 1 - we are in welcome
+ * screen 2 - we are in the normal game */
+char loginname[20]; /* players login name */
+char logname[LOGNAMESIZE]; /* players name storage for scoring */
+u_char sex = 1; /* default is a man 0=woman */
+u_char boldon = 1; /* 1=bold objects 0=inverse objects */
+u_char ckpflag = 0; /* 1 if want checkpointing of game, 0
+ * otherwise */
+u_char cheat = 0; /* 1 if the player has fudged save file */
+short level = 0; /* cavelevel player is on = c[CAVELEVEL] */
+u_char wizard = 0; /* the wizard mode flag */
+short lastnum = 0; /* the number of the monster last hitting
+ * player */
+short hitflag = 0; /* flag for if player has been hit when
+ * running */
+short hit2flag = 0; /* flag for if player has been hit when
+ * running */
+short hit3flag = 0; /* flag for if player has been hit flush
+ * input */
+short playerx, playery; /* the room on the present level of
+ * the player */
+short lastpx, lastpy; /* 0 --- MAXX-1 or 0 --- MAXY-1 */
+short oldx, oldy;
+short lasthx = 0, lasthy = 0; /* location of monster last hit by
+ * player */
+short nobeep = 0; /* true if program is not to beep */
+unsigned long randx = 33601; /* the random number seed */
+time_t initialtime = 0;/* time playing began */
+long gltime = 0; /* the clock for the game */
+long outstanding_taxes = 0; /* present tax bill from score file */
+long c[100], cbak[100]; /* the character description arrays */
+int enable_scroll = 0; /* constant for enabled/disabled
+ * scrolling regn */
+char aborted[] = " aborted";
+struct sphere *spheres = 0; /* pointer to linked list for spheres of
+ * annihilation */
+const char *levelname[] =
+{" H", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "V1", "V2", "V3"};
+
+char objnamelist[] = " ATOP%^F&^+M=%^$$f*OD#~][[)))(((||||||||{?!BC}o:@.<<<<EVV))([[]]](^ [H*** ^^ S tsTLc............................................";
+char monstnamelist[] = " BGHJKOScjtAELNQRZabhiCTYdegmvzFWflorXV pqsyUkMwDDPxnDDuD ...............................................................";
+const char *objectname[] =
+{0, "a holy altar", "a handsome jewel encrusted throne", "the orb", "a pit",
+ "a staircase leading upwards", "an elevator going up", "a bubbling fountain",
+ "a great marble statue", "a teleport trap", "the college of Larn",
+ "a mirror", "the DND store", "a staircase going down", "an elevator going down",
+ "the bank of Larn", "the 5th branch of the Bank of Larn",
+ "a dead fountain", "gold", "an open door", "a closed door",
+ "a wall", "The Eye of Larn", "plate mail", "chain mail", "leather armor",
+ "a sword of slashing", "Bessman's flailing hammer", "a sunsword",
+ "a two handed sword", "a spear", "a dagger",
+ "ring of extra regeneration", "a ring of regeneration", "a ring of protection",
+ "an energy ring", "a ring of dexterity", "a ring of strength",
+ "a ring of cleverness", "a ring of increase damage", "a belt of striking",
+ "a magic scroll", "a magic potion", "a book", "a chest",
+ "an amulet of invisibility", "an orb of dragon slaying",
+ "a scarab of negate spirit", "a cube of undead control",
+ "device of theft prevention", "a brilliant diamond", "a ruby",
+ "an enchanting emerald", "a sparkling sapphire", "the dungeon entrance",
+ "a volcanic shaft leaning downward", "the base of a volcanic shaft",
+ "a battle axe", "a longsword", "a flail", "ring mail", "studded leather armor",
+ "splint mail", "plate armor", "stainless plate armor", "a lance of death",
+ "an arrow trap", "an arrow trap", "a shield", "your home",
+ "gold", "gold", "gold", "a dart trap",
+ "a dart trap", "a trapdoor", "a trapdoor", "the local trading post",
+ "a teleport trap", "a massive throne",
+ "a sphere of annihilation", "a handsome jewel encrusted throne",
+ "the Larn Revenue Service", "a fortune cookie", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
+};
+
+
+
+/*
+ * for the monster data
+ *
+ * array to do rnd() to create monsters <= a given level
+ */
+u_char monstlevel[] = {5, 11, 17, 22, 27, 33, 39, 42, 46, 50, 53, 56, 59};
+
+struct monst monster[] = {
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+ {"", 0, 0, 0, 0, 0, 0, 3, 0, 0, 0},
+ {"bat", 1, 0, 1, 0, 0, 0, 3, 0, 1, 1},
+ {"gnome", 1, 10, 1, 0, 0, 0, 8, 30, 2, 2},
+ {"hobgoblin", 1, 14, 2, 0, 0, 0, 5, 25, 3, 2},
+ {"jackal", 1, 17, 1, 0, 0, 0, 4, 0, 1, 1},
+ {"kobold", 1, 20, 1, 0, 0, 0, 7, 10, 1, 1},
+
+ {"orc", 2, 12, 1, 0, 0, 0, 9, 40, 4, 2},
+ {"snake", 2, 15, 1, 0, 0, 0, 3, 0, 3, 1},
+ {"giant centipede", 2, 14, 0, 4, 0, 0, 3, 0, 1, 2},
+ {"jaculi", 2, 20, 1, 0, 0, 0, 3, 0, 2, 1},
+ {"troglodyte", 2, 10, 2, 0, 0, 0, 5, 80, 4, 3},
+ {"giant ant", 2, 8, 1, 4, 0, 0, 4, 0, 5, 5},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"floating eye", 3, 8, 1, 0, 0, 0, 3, 0, 5, 2},
+ {"leprechaun", 3, 3, 0, 8, 0, 0, 3, 1500, 13, 45},
+ {"nymph", 3, 3, 0, 14, 0, 0, 9, 0, 18, 45},
+ {"quasit", 3, 5, 3, 0, 0, 0, 3, 0, 10, 15},
+ {"rust monster", 3, 4, 0, 1, 0, 0, 3, 0, 18, 25},
+ {"zombie", 3, 12, 2, 0, 0, 0, 3, 0, 6, 7},
+
+ {"assassin bug", 4, 9, 3, 0, 0, 0, 3, 0, 20, 15},
+ {"bugbear", 4, 5, 4, 15, 0, 0, 5, 40, 20, 35},
+ {"hell hound", 4, 5, 2, 2, 0, 0, 6, 0, 16, 35},
+ {"ice lizard", 4, 11, 2, 10, 0, 0, 6, 50, 16, 25},
+ {"centaur", 4, 6, 4, 0, 0, 0, 10, 40, 24, 45},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"troll", 5, 4, 5, 0, 0, 0, 9, 80, 50, 300},
+ {"yeti", 5, 6, 4, 0, 0, 0, 5, 50, 35, 100},
+ {"white dragon", 5, 2, 4, 5, 0, 0, 16, 500, 55, 1000},
+ {"elf", 5, 8, 1, 0, 0, 0, 15, 50, 22, 35},
+ {"gelatinous cube", 5, 9, 1, 0, 0, 0, 3, 0, 22, 45},
+
+ {"metamorph", 6, 7, 3, 0, 0, 0, 3, 0, 30, 40},
+ {"vortex", 6, 4, 3, 0, 0, 0, 3, 0, 30, 55},
+ {"ziller", 6, 15, 3, 0, 0, 0, 3, 0, 30, 35},
+ {"violet fungi", 6, 12, 3, 0, 0, 0, 3, 0, 38, 100},
+ {"wraith", 6, 3, 1, 6, 0, 0, 3, 0, 30, 325},
+ {"forvalaka", 6, 2, 5, 0, 0, 0, 7, 0, 50, 280},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"lama nobe", 7, 7, 3, 0, 0, 0, 6, 0, 35, 80},
+ {"osequip", 7, 4, 3, 16, 0, 0, 4, 0, 35, 100},
+ {"rothe", 7, 15, 5, 0, 0, 0, 3, 100, 50, 250},
+ {"xorn", 7, 0, 6, 0, 0, 0, 13, 0, 60, 300},
+ {"vampire", 7, 3, 4, 6, 0, 0, 17, 0, 50, 1000},
+ {"invisible stalker", 7, 3, 6, 0, 0, 0, 5, 0, 50, 350},
+
+ {"poltergeist", 8, 1, 4, 0, 0, 0, 3, 0, 50, 450},
+ {"disenchantress", 8, 3, 0, 9, 0, 0, 3, 0, 50, 500},
+ {"shambling mound", 8, 2, 5, 0, 0, 0, 6, 0, 45, 400},
+ {"yellow mold", 8, 12, 4, 0, 0, 0, 3, 0, 35, 250},
+ {"umber hulk", 8, 3, 7, 11, 0, 0, 14, 0, 65, 600},
+
+ /*
+ * NAME LV AC DAM ATT DEF GEN
+ * INT GOLD HP EXP
+ * -----------------------------------------------------------------
+ */
+
+ {"gnome king", 9, -1, 10, 0, 0, 0, 18, 2000, 100, 3000},
+ {"mimic", 9, 5, 6, 0, 0, 0, 8, 0, 55, 99},
+ {"water lord", 9, -10, 15, 7, 0, 0, 20, 0, 150, 15000},
+ {"bronze dragon", 9, 2, 9, 3, 0, 0, 16, 300, 80, 4000},
+ {"green dragon", 9, 3, 8, 10, 0, 0, 15, 200, 70, 2500},
+ {"purple worm", 9, -1, 11, 0, 0, 0, 3, 100, 120, 15000},
+ {"xvart", 9, -2, 12, 0, 0, 0, 13, 0, 90, 1000},
+
+ {"spirit naga", 10, -20, 12, 12, 0, 0, 23, 0, 95, 20000},
+ {"silver dragon", 10, -1, 12, 3, 0, 0, 20, 700, 100, 10000},
+ {"platinum dragon", 10, -5, 15, 13, 0, 0, 22, 1000, 130, 24000},
+ {"green urchin", 10, -3, 12, 0, 0, 0, 3, 0, 85, 5000},
+ {"red dragon", 10, -2, 13, 3, 0, 0, 19, 800, 110, 14000},
+
+ {"type I demon lord", 12, -30, 18, 0, 0, 0, 20, 0, 140, 50000},
+ {"type II demon lord", 13, -30, 18, 0, 0, 0, 21, 0, 160, 75000},
+ {"type III demon lord", 14, -30, 18, 0, 0, 0, 22, 0, 180, 100000},
+ {"type IV demon lord", 15, -35, 20, 0, 0, 0, 23, 0, 200, 125000},
+ {"type V demon lord", 16, -40, 22, 0, 0, 0, 24, 0, 220, 150000},
+ {"type VI demon lord", 17, -45, 24, 0, 0, 0, 25, 0, 240, 175000},
+ {"type VII demon lord", 18, -70, 27, 6, 0, 0, 26, 0, 260, 200000},
+ {"demon prince", 25, -127, 30, 6, 0, 0, 28, 0, 345, 300000}
+
+ /*
+ * NAME LV AC DAM ATT DEF
+ * GEN INT GOLD HP EXP
+ * -------------------------------------------------------------------
+ * --
+ */
+};
+
+/* name array for scrolls */
+
+const char *scrollname[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "",
+"", "", "", "", "", "", "", "", "", "", "", "", "", ""};
+
+const char *scrollhide[] = {
+ " enchant armor",
+ " enchant weapon",
+ " enlightenment",
+ " blank paper",
+ " create monster",
+ " create artifact",
+ " aggravate monsters",
+ " time warp",
+ " teleportation",
+ " expanded awareness",
+ " haste monsters",
+ " monster healing",
+ " spirit protection",
+ " undead protection",
+ " stealth",
+ " magic mapping",
+ " hold monsters",
+ " gem perfection",
+ " spell extension",
+ " identify",
+ " remove curse",
+ " annihilation",
+ " pulverization",
+ " life protection",
+ " ",
+ " ",
+ " ",
+ " "
+};
+
+const char *potionname[] = {"", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+"", "", "", "", "", "", ""};
+
+/* name array for magic potions */
+const char *potionhide[] = {
+ " sleep",
+ " healing",
+ " raise level",
+ " increase ability",
+ " wisdom",
+ " strength",
+ " raise charisma",
+ " dizziness",
+ " learning",
+ " gold detection",
+ " monster detection",
+ " forgetfulness",
+ " water",
+ " blindness",
+ " confusion",
+ " heroism",
+ " sturdiness",
+ " giant strength",
+ " fire resistance",
+ " treasure finding",
+ " instant healing",
+ " cure dianthroritis",
+ " poison",
+ " see invisible",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " "
+};
+
+
+
+/*
+ spell data
+ */
+u_char spelknow[SPNUM] = {0};
+u_char splev[] = {1, 4, 9, 14, 18, 22, 26, 29, 32, 35, 37, 37, 37, 37, 37};
+
+const char *spelcode[] = {
+ "pro", "mle", "dex", "sle", "chm", "ssp",
+ "web", "str", "enl", "hel", "cbl", "cre", "pha", "inv",
+ "bal", "cld", "ply", "can", "has", "ckl", "vpr",
+ "dry", "lit", "drl", "glo", "flo", "fgr",
+ "sca", "hld", "stp", "tel", "mfi", /* 31 */
+ "sph", "gen", "sum", "wtw", "alt", "per"
+};
+
+const char *spelname[] = {
+ "protection", "magic missile", "dexterity",
+ "sleep", "charm monster", "sonic spear",
+
+ "web", "strength", "enlightenment",
+ "healing", "cure blindness", "create monster",
+ "phantasmal forces", "invisibility",
+
+ "fireball", "cold", "polymorph",
+ "cancellation", "haste self", "cloud kill",
+ "vaporize rock",
+
+ "dehydration", "lightning", "drain life",
+ "invulnerability", "flood", "finger of death",
+
+ "scare monster", "hold monster", "time stop",
+ "teleport away", "magic fire",
+
+ "sphere of annihilation", "genocide", "summon demon",
+ "walk through walls", "alter reality", "permanence",
+ ""
+};
+
+const char *speldescript[] = {
+ /* 1 */
+ "generates a +2 protection field",
+ "creates and hurls a magic missile equivalent to a +1 magic arrow",
+ "adds +2 to the caster's dexterity",
+ "causes some monsters to go to sleep",
+ "some monsters may be awed at your magnificence",
+ "causes your hands to emit a screeching sound toward what they point",
+ /* 7 */
+ "causes strands of sticky thread to entangle an enemy",
+ "adds +2 to the caster's strength for a short term",
+ "the caster becomes aware of things in the vicinity",
+ "restores some hp to the caster",
+ "restores sight to one so unfortunate as to be blinded",
+ "creates a monster near the caster appropriate for the location",
+ "creates illusions, and if believed, monsters die",
+ "the caster becomes invisible",
+ /* 15 */
+ "makes a ball of fire that burns on what it hits",
+ "sends forth a cone of cold which freezes what it touches",
+ "you can find out what this does for yourself",
+ "negates the ability of a monster to use its special abilities",
+ "speeds up the caster's movements",
+ "creates a fog of poisonous gas which kills all that is within it",
+ "this changes rock to air",
+ /* 22 */
+ "dries up water in the immediate vicinity",
+ "your finger will emit a lightning bolt when this spell is cast",
+ "subtracts hit points from both you and a monster",
+ "this globe helps to protect the player from physical attack",
+ "this creates an avalanche of H2O to flood the immediate chamber",
+ "this is a holy spell and calls upon your god to back you up",
+ /* 28 */
+ "terrifies the monster so that hopefully it won't hit the magic user",
+ "the monster is frozen in its tracks if this is successful",
+ "all movement in the caverns ceases for a limited duration",
+ "moves a particular monster around in the dungeon (hopefully away from you)",
+ "this causes a curtain of fire to appear all around you",
+ /* 33 */
+ "anything caught in this sphere is instantly killed. Warning -- dangerous",
+ "eliminates a species of monster from the game -- use sparingly",
+ "summons a demon who hopefully helps you out",
+ "allows the player to walk through walls for a short period of time",
+ "god only knows what this will do",
+ "makes a character spell permanent, i. e. protection, strength, etc.",
+ ""
+};
+
+char spelweird[MAXMONST + 8][SPNUM] = {
+ /*
+ * p m d s c s w s e h c c p i b c p c h c v d l d g f f
+ * s h s t m s g s w a p
+ */
+ /*
+ * r l e l h s e t n e b r h n a l l a a k p r i r l l g
+ * c l t e f p e u t l e
+ */
+ /*
+ * o e x e m p b r l l l e a v l d y n s l r y t l o o r
+ * a d p l i h n m w t r
+ */
+
+
+ /* bat */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* gnome */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* hobgoblin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* jackal */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* kobold */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* orc */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* snake */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* giant centipede */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* jaculi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* troglodyte */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* giant ant */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* floating eye */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* leprechaun */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* nymph */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* quasit */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* rust monster */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* zombie */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* assassin bug */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* bugbear */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* hell hound */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* ice lizard */ {0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* centaur */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* troll */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* yeti */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* white dragon */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 15, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* elf */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* gelatinous cube */ {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* metamorph */ {0, 13, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* vortex */ {0, 13, 0, 0, 0, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* ziller */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* violet fungi */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* wraith */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* forvalaka */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* lama nobe */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* osequip */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* rothe */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* xorn */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* vampire */ {0, 0, 0, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* invisible staker */ {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* poltergeist */ {0, 13, 0, 8, 0, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* disenchantress */ {0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* shambling mound */ {0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* yellow mold */ {0, 0, 0, 8, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* umber hulk */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* gnome king */ {0, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* mimic */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* water lord */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 4, 0, 0, 0, 0, 0, 16, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* bronze dragon */ {0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* green dragon */ {0, 7, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* purple worm */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /* xvart */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* spirit naga */ {0, 13, 0, 8, 3, 4, 1, 0, 0, 0, 0, 0, 0, 5, 0, 4, 9, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* silver dragon */ {0, 6, 0, 9, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* platinum dragon */ {0, 7, 0, 9, 0, 0, 11, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* green urchin */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ /* red dragon */ {0, 6, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+
+ /*
+ * p m d s c s w s e h c c p i b c p c h c v d l d g f f
+ * s h s t m s g s w a p
+ */
+ /*
+ * r l e l h s e t n e b r h n a l l a a k p r i r l l g
+ * c l t e f p e u t l e
+ */
+ /*
+ * o e x e m p b r l l l e a v l d y n s l r y t l o o r
+ * a d p l i h n m w t r
+ */
+
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon lord */ {0, 7, 0, 4, 3, 0, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0},
+ /* demon prince */ {0, 7, 0, 4, 3, 9, 1, 0, 0, 0, 0, 0, 14, 5, 0, 0, 4, 0, 0, 4, 0, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 4, 9, 0, 0, 0, 0, 0}
+
+};
+
+const char *spelmes[] = {"",
+ /* 1 */ "the web had no effect on the %s",
+ /* 2 */ "the %s changed shape to avoid the web",
+ /* 3 */ "the %s isn't afraid of you",
+ /* 4 */ "the %s isn't affected",
+ /* 5 */ "the %s can see you with his infravision",
+ /* 6 */ "the %s vaporizes your missile",
+ /* 7 */ "your missile bounces off the %s",
+ /* 8 */ "the %s doesn't sleep",
+ /* 9 */ "the %s resists",
+ /* 10 */ "the %s can't hear the noise",
+ /* 11 */ "the %s's tail cuts it free of the web",
+ /* 12 */ "the %s burns through the web",
+ /* 13 */ "your missiles pass right through the %s",
+ /* 14 */ "the %s sees through your illusions",
+ /* 15 */ "the %s loves the cold!",
+ /* 16 */ "the %s loves the water!"
+};
+
+/*
+ * function to create scroll numbers with appropriate probability of
+ * occurrence
+ *
+ * 0 - armor 1 - weapon 2 - enlightenment 3 - paper
+ * 4 - create monster 5 - create item 6 - aggravate 7 - time warp
+ * 8 - teleportation 9 - expanded awareness 10 - haste monst
+ * 11 - heal monster 12 - spirit protection 13 - undead protection
+ * 14 - stealth 15 - magic mapping 16 - hold monster
+ * 17 - gem perfection 18 - spell extension 19 - identify
+ * 20 - remove curse 21 - annihilation 22 - pulverization
+ * 23 - life protection
+ */
+u_char scprob[] = {0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9,
+ 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14,
+ 15, 15, 16, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 20, 20, 21, 22,
+22, 22, 23};
+
+/*
+ * function to return a potion number created with appropriate probability
+ * of occurrence
+ *
+ * 0 - sleep 1 - healing 2 - raise level
+ * 3 - increase ability 4 - gain wisdom 5 - gain strength
+ * 6 - charismatic character 7 - dizziness 8 - learning
+ * 9 - gold detection 10 - monster detection 11 - forgetfulness
+ * 12 - water 13 - blindness 14 - confusion
+ * 15 - heroism 16 - sturdiness 17 - giant strength
+ * 18 - fire resistance 19 - treasure finding 20 - instant healing
+ * 21 - cure dianthroritis 22 - poison 23 - see invisible
+ */
+u_char potprob[] = {0, 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 20, 22, 22, 23, 23};
+
+u_char nlpts[] = {0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7};
+u_char nch[] = {0, 0, 0, 1, 1, 1, 2, 2, 3, 4};
+u_char nplt[] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 4};
+u_char ndgg[] = {0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5};
+u_char nsw[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3};
diff --git a/larn/datfiles/larn.help b/larn/datfiles/larn.help
new file mode 100644
index 0000000..0e5edc5
--- /dev/null
+++ b/larn/datfiles/larn.help
@@ -0,0 +1,140 @@
+5 Welcome to the game of Larn. At this moment, you face a great problem.
+Your daughter has contracted a strange disease, and none of your home remedies
+seem to have any effect. You sense that she is in mortal danger, and you must
+try to save her. Time ago you heard of a land of great danger and opportunity.
+Perhaps here is the solution you need.
+
+ It has been said that there once was a great magician who called himself
+Polinneaus. Many years ago, after having many miraculous successes, Polinneaus
+retired to the caverns of Larn, where he devoted most of his time to the
+creation of magic. Rumors have it that one day Polinneaus set out to dispel
+an attacking army in a forest some distance to the north. It is believed that
+here he met his demise.
+
+ The caverns of Larn, it is thought, must be magnificent in design,
+and contain much magic and treasure. One option you have is to undertake a
+journey into these caverns.
+
+
+ Good Luck! You're going to need it!
+
+
+
+
+ Help File for The Caverns of Larn
+
+h move to the left H run left . stay here
+j move down J run down Z teleport yourself
+k move up K run up c cast a spell
+l move to the right L run right r read a scroll
+y move northwest Y run northwest q quaff a potion
+u move northeast U run northeast W wear armor
+b move southwest B run southwest T take off armor
+n move southeast N run southeast w wield a weapon
+^ identify a trap g give present pack weight P give tax status
+d drop an item i inventory your pockets Q quit the game
+v print program version S save the game D list all items found
+? this help screen A create diagnostic file e eat something
+ (wizards only)
+larn ++ restore checkpointed game
+larn -s list the scoreboard
+larn -i list scores with inventories
+larn -n suppress welcome message when beginning a game
+larn -h print out all the command line options
+larn -<number> specify difficulty of the game (may be used with -n)
+larn -o<optsfile> specify the .larnopts file to be used
+larn -c create new scoreboards -- prompts for a password
+ Special Notes
+
+When dropping gold, if you type '*' as your amount, all your gold gets dropped.
+In general, typing in '*' means all of what your interested in. This is true
+when visiting the bank, or when contributing at altars.
+
+Larn may need a VT100 to operate. A check is made of the environment variable
+"TERM" and it must be equal to "vt100". This only applies if
+the game has been compiled with "VT100" defined in the Makefile. If compiled
+to use termcap, there are no terminal restrictions, save needing cm, ce, & cl
+termcap entries.
+
+When in the store, trading post, school, or home, an <escape> will get you out.
+
+larn -l print out the larn log file
+
+When casting a spell, if you need a list of spells you can cast, type 'D' as
+the first letter of your spell. The available list of spells will be shown,
+after which you may enter the spell code. This only works on the 1st letter
+of the spell you are casting.
+
+The Author of Larn is Noah Morgan (1982-3), Copying for Profit is Prohibited
+Copyright 1986 by Noah Morgan, All Rights Reserved.
+ Background Information for Larn
+
+ Welcome to the game of Larn. At this moment, you face a great problem.
+Your daughter has contracted a strange disease, and none of your home remedies
+seem to have any effect. You sense that she is in mortal danger, and you must
+try to save her. Time ago you heard of a land of great danger and opportunity.
+Perhaps here is the solution you need.
+
+ It has been said that there once was a great magician who called himself
+Polinneaus. Many years ago, after having many miraculous successes, Polinneaus
+retired to the caverns of Larn, where he devoted most of his time to the
+creation of magic. Rumors have it that one day Polinneaus set out to dispel
+an attacking army in a forest some distance to the north. It is believed that
+here he met his demise.
+
+ The caverns of Larn, it is thought, must be magnificent in design,
+and contain much magic and treasure. One option you have is to undertake a
+journey into these caverns.
+
+ Good Luck! You're going to need it!
+
+
+
+ How to use the .larnopts option file
+
+The file ".larnopts", if used, should be in your home directory (see -o).
+A sequence of words terminated by whitespace is used to specify options.
+
+ Word Meaning
+
+ bold-objects select bold display of objects
+ inverse-objects select inverse video display of objects
+ no-introduction do not display intro message
+ enable-checkpointing turn on periodic checkpointing
+ no-beep disable beeping of the terminal
+ male choose your sex to be a man
+ female choose your sex to be a woman
+ name: "your name" choose your playing name
+ monster: "monst name" choose a name for a monster
+ savefile: "save-file-name" define what the savegame filename will be
+
+Your name and monster names must be enclosed in double quotation marks and may
+be up to 34 characters long. Longer names are truncated. Anything enclosed in
+quotation marks is considered one word, and must be separated from other words
+by whitespace.
+
+ Explanation of the Larn scoreboard facility
+
+ Larn supports TWO scoreboards, one for winners, and one for deceased
+characters. Each player (by userid or playerid, see UIDSCORE in Makefile)
+is allowed one slot on each scoreboard, if the score is in the top ten for
+that scoreboard. This design helps insure that frequent players of Larn
+do not hog the scoreboard, and gives more players a chance for glory. Level
+of difficulty is also noted on the scoreboards, and this takes precedence
+over score for determining what entry is on the scoreboard. For example:
+if "Yar, the Bug Slayer" has a score of 128003 on the scoreboard at diff 0,
+then his game at diff 1 and a score of 4112 would replace his previous
+entry on the scoreboard. Note that when a player dies, his inventory is
+stored in the scoreboard so that everyone can see what items the player had
+at the time of his death.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/larn/datfiles/larnmaze b/larn/datfiles/larnmaze
new file mode 100644
index 0000000..37a89c3
--- /dev/null
+++ b/larn/datfiles/larnmaze
@@ -0,0 +1,288 @@
+@###################################################################
+# # . # # # # # . #
+# D D . . D . #
+###D########################################## # # ###D###
+# -# #. # # ################ . .#
+# ####### ######## ############ D #### # # #
+# ... #.# # # # . # # # #### # ############ # ###D###
+# #.# # # ## # # # ############ #### # #- # # # #. #
+# . # # # # ## #- # # # - D #### # # . D # #.# # ... #
+# # #.# # # # # # . . # # # # # # #-# # ~.! #
+###D### ### #######D## # ############ ###### ########## ### #######
+# # @ .# # ..... ...#
+###D###########################################################D###
+# . #.....# # # # -# # # # # #
+# ..... . D D D D. #
+# #.....# # # # # # .# # # #
+###################################################################
+
+###################################################################
+#.. . D # . # #- #
+############# ######### # ## ### ##### ## #### ###### ####### ### #
+#.#!#~# # # .-# # #- # # # # -# # # #
+# # #.# . # ####### # # # # # # # # # #####
+# # ..# ##### # # # #### # ## ## ## # ###### ####### # #
+# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### #
+####### ####### ### # # # # # # D # D D #..D # #
+#- # # # #### # ###### # ## # #. # #..# #####
+### #######################- # # # ###################### # #
+# ... # . #..# ### # - .. . #. ### #
+# # # # ### #################### # # #
+# ### #
+################################################################# #
+#- D ### # # # #
+# . # # # D #
+###################################################################
+
+###################################################################
+# .. #
+# ############## ############################################## # #
+# # # # # .. # # #
+# #D## # # ############D################# ########### # # #
+######### #- # # # #- D # # ~ # # # # # # #
+# # # # # # # ### # # # D - # # ####### # # # #
+# .... # #### # # # #!# # # ###### .. #.# # # # # # #
+# .... # # # # ### # # # # #########D#### # ### # # # # #
+# .... ######## # # D # #- # #.. # ...#.# # #.# # # # # #
+# # # ### # #### #.- D - #.# #.#.# # # # # #
+#####DD## ######## # # D D. # # # #...# # # # # #
+# # ..# # # # ############################## # ##### # # # # #
+# ......# # # # # # # # # #
+# ####### ###### ################################ ######### # ### #
+# .D. # #
+###################################################################
+
+###################################################################
+# ## ## ### ## #
+# ##### ## ..- ## ##.## ## #
+# # ! ## ## . ## ## .## .. ## #
+# #....###### ## ## ## . ## ## #
+# # - # ## ##D# ## . ## ## #
+# #####D ~ ####### ###........ ## ... ## ## #
+# # # ## ## .... ## . ## ## #
+# #. ######## ## ## . - ####D#### - #
+# #.- #...## ## ... ## ### ...... ## ## #
+# #. #..## ## ######### ## ... ## ### ## .. #
+# #.. #.## ## ## - ## . #### ## ## ##### ## #
+# #####D## ## ## ###### ## ## ####### ## #
+# D -.## #### ######## ##DD## ######## #D #
+# ###### ... ## ########## ## #
+#### . . ###
+###################################################################
+
+###################################################################
+# #
+# ####.########################################## ## ##########
+# # #.#.#.# #.. #. # # #
+####### # # # # # ##### #! # ########### # ### #
+# # # # # # # ##...## # # # # # #-# #
+# ..- D # # # # # ## . ## ####D##### .. ### # # # #
+# # # # ##. ~ .## # # ### # # ##### # #
+############ # # # # ## . . .## #...# .. #.# # # # #
+# .. .# # # # # ## - ## # # D.D # ######### #
+# . D # # # # ##.......## ####D#####.# # #
+# - . # # # # # ## ## # . # ### ########### #
+############ #.#.# # ###D### # . #....# # #
+# # #-# D .. # .# ###....## ##-### #
+#### ########################################## ###### ### #
+# . #
+###################################################################
+
+###################################################################
+# #
+# ###########################D####### ## #
+# ####################### # ##...... ## ## #
+# ########D###### # # !##.... ## ## #
+# ############ ## ## ... # # #...## ~ ## ###### #
+# # # # # ...... # # # .. ##### ## #
+# # - .. # # # ########### # # . ######### ## #
+# ##### ##### # # # # #... ## ## #
+# # # ########## ######## ########DD#### ####### #
+# # # .... # # # # # ## ## #
+# ##### # # ....# # ######### # # ##### ## #
+# #- ######D##########..# # ######### # - #.. ## ## #
+# ##### # # # #... #.....## #
+# # ################### ###############... #### # ######
+# # #
+###################################################################
+
+###################################################################
+# #
+########### ##### ##### #####D#### ##### ###### #
+#.. # #-..D ###### ## # ## # ## ..## ### #### #
+# #### ### ##### # ## # ##### # ## ### ## # # #
+# # # # # # ##### # ## # # #### # #..##### - ### #
+# # # ####### ### #...# # ######## # # #~. .... # #
+# # # # #...# # # ######### ############## #
+# # ###### ####### # ### ### # .. # #
+# # # - # #-#####!# # # #### ## #### # #
+# ###### # ########## #.. ....# # ######## # # # ## ## #
+# -.# ##### ####...## # . .# #..# # ## # #
+# #### ###### #.### #####.#### #### ### ## # #
+# #- #######....#### ...... #.# . # ## # #
+# ####### ##### ###########.############### ### #
+# . #
+###################################################################
+
+###################################################################
+# # #- . # # # # # # # # #
+# # # #####.##### # # # # # # # # # # ###########D########### #
+# # ##### # # ### # # # # # # # # # ### #.... # # # #
+# # # # # # # # # # # # # # #.!. -.. #.##.# # #
+# # . - # # ####### # # # # # # # # # ########## ## # #
+#.####### # # D # . # # #
+#. -# ################################################### #
+# ##### D. . #
+# # ### ###D### ### ### ###D### ### ### ###D### ###.### #
+# # ###### ### .# .# #. # # # # # # # # . # #
+# ### # -# ### .#. - .# # # #...- # # .. # # -..# # . # #
+# # # ### #. ~ .# # # # # # # # .. .# # # #
+#.###### # ### # # #.....# # # # # # # # # #
+# # ####### ####### ####### ####### ####### ####### ####### #
+# # ... .. #
+###################################################################
+
+###################################################################
+# # . # ### # # # # . #
+# D D . # . D . #
+############################################### # # ###D###
+# -# #. # # ################ . .#
+# #######D######## ############ # #### # # #
+# ... #. # #!~ . # # # #--# # ############ #####D###
+# #.# # # ## # # # ############ #--# # #- # # # #. #
+# . # #-# # ## #- # # # - D ## # # # . D #### #.# # ... #
+# # #-# # # # # # . . # # # # # # #-# # -.- #
+###D### ### ####### ## # ############ ###### ########## ### ###D###
+# # @ . ..... ...#
+##################################### ###############D###
+# . #.....# # # # -# # # # #
+# ..... . D D D D. #
+# #.....# # # # # .# # # #
+###################################################################
+
+###################################################################
+#.. . # D . #- #
+### ######### ######### # ## ### ##### ## ########### ####### ### #
+#.# # # # .-# # #- # # # # -# # # #
+# # #.# . # ####### # # # # # # # # # #####
+# # ..####### # # # #### # ## ## ##!# ###### ####### # #
+# - ..D # D # . D # # # #.##### ## ## # #. #.# #..# # ### #
+####### ####### ### # # # # # # D # D D #..D # #
+#- # # # #### # ###### # ## # #. # #..# #####
+### #######################- # # # ###################### # #
+# ... # . #..# ### # - .. . #. ### #
+# # # # #-# #################### # # #
+# # -# #
+################################-################################ #
+#-..... # ####D ### # # # #
+#~..... # # # # D #
+###################################################################
+
+###################################################################
+# .. #
+# ############## ############################################## # #
+# # # # # .. # # #
+# #D## # # ############D################# ########### # # #
+######### #- # # # #- D # #.!..# # # # # # #
+# # # # # # # # ### ## # #....D - # # ####### # # # #
+# .... # #### # # # #~# # # ###### .. #.# # # # # # #
+###....## # # # ###.# # # #########D## # # ### # # # # #
+# .... ######## # # D .# #- # .. # ...#.# # #.# # # # # #
+# # ## # ### .# #### .- D - #.# #.#.# # # # # #
+#####DD## ######## #... .# . # # # #.#. # # # # #
+# # ..# # # ############################## # # ##### # # # #
+# ......# # # # # # # # # # #
+# ####### ###### ################################D# ######### ### #
+# .D. #-.-#
+###################################################################
+
+###################################################################
+# ## ## ## ## ## #
+# ############## ## ..- ## ## . ## ## #
+# # # ## ## . ## ## . ##.. ## #
+# #....###### ## ## ######## ####### . ## ## #
+# # - # ## ## ##D# ## . ## ## #
+# # D###### ###........ ## ... ## ###### #
+# # # ## ## .... ## . ## ## - # #
+# #. ######## ## ## . - ####D####.. D #
+# #.- #...## ## ... ## ### ...... ## ##### #
+# #. #..## ## ######### ## ... ## ### ## .. #
+# #.. #.## ## ## - ## . #### ## ## ##### ## #
+# #####D####### # ## ## ##..## ## ## ## ## ## #
+# D -.## # # #### ##.-.-## ##DD## ### #### ## #
+# ######. ...# #### ## #### ##### ## ##D#
+####~!.... D . . #
+###################################################################
+
+###################################################################
+# #
+# ####.########################################## ## ##########
+# # #.#.#.# #.. . # # #
+####### # # # # # ############# # ########### # ### #
+# # # # # # # --##...##-- # # # # #-# #
+# ..- D # # # # # #-## . ##-# ####D##### .. ### # # # #
+# # # # ###. .### # ~ # ### ### ##### # #
+############ # # # # ## . . .## #...# .. #.# # #
+# .. .# # # # # ## - ## # # D. # ######### #
+# . D # # # # ###.......### ####D#####.# # #
+# - . # # # # # -## ##- # . ### ########### #
+############ #.#.# # ######D###### # . ....# # #
+# #!#-# # .. # . ###....## ##-### #
+#### ############################## ########## ###### ### #
+# D . #
+###################################################################
+
+###################################################################
+# #
+# ###############D####################D# #
+# ####################### # ##...... # #
+# ########D###### # # ##.... # #
+# ############### ## ## ... # # #...## ##### #
+# # ~ # # #!...... # # # .. ##### # #
+# # - .. # # # ########### # # . ######### # #
+# ##### ######## # # # # # #... ## # #
+# # # # ########## ######## ########D #### ###### #
+# # # ....# # # # # # ## # #
+# ##### # ###### # ....# # ######### # # #####D# #
+# #- ## # #######..# # ######### # - #.. #
+# ##### # # # #... #..... #
+# # ################### ###############... #### ######
+# # #
+###################################################################
+
+###################################################################
+# # #
+########### ##### # #####D#### ##### ###### #
+#.. # #~..D ###### ## # ## # ## ..## ### #### #
+# #### ### ##### # ## # ##### # ## ### ## # # #
+# # # # # # ##### # ## # # #### # #..##### - ### #
+# # # ####### ### #...# # ######## # # #!. .... # #
+# # # # #...# # # ######### ############## #
+# # ###### ####### # ### ### # .. # #
+# # # - # #- # # # ######## #### # #
+# # # ########## #.. ....# # ######## # ## ## #
+# # -. ##### ####...## # . .# .. # # #
+# # ###### ##### #####.#### ####### # #
+# # - #######....#### ...... #.# . # ## # #
+# ######### ##### ###########.############### ### #
+# . #
+###################################################################
+
+###################################################################
+# D D #-..........# # # # # #
+#D#D# #####.#####.# # # # # # #############D########### #
+# # ##### #.#~###.# # # # # # ### .... # # # #
+#D# # #.......# # # # # # # . . -.. #. #.# # #
+# # . - # #.####### # # # # # ################## # #
+#D####### #. # D # . # # #
+#. D D -# ################################################### #
+# ###### . . #
+# # D ### ###D### ####### ###D### ####### ###D### ####### #
+# # ###### # # .# .# #. # # # # # # # # . # #
+# ### -# # # .#. - .# # # #...- # # .. # # -..# # . # #
+#DD# # # # #. .# # # # # # # # .. .# # # #
+#.###### # # # # #.....# # # # # # ! # # # #
+# # ####### ####### ### ### ####### ### ### ####### ### ### #
+# # ... .. #
+###################################################################
+
diff --git a/larn/datfiles/larnopts b/larn/datfiles/larnopts
new file mode 100644
index 0000000..17216ed
--- /dev/null
+++ b/larn/datfiles/larnopts
@@ -0,0 +1,12 @@
+process-name: "Winnie-the-Pooh"
+enable-checkpointing
+bold-objects
+male
+play-day-play
+no-introduction
+name: "King of the Realm"
+monster: "abominable snowman"
+monster: "tooth fairy"
+monster: "Yaccerous Lexicous"
+savefile: "/save/noah/games/Larn12.0.sav"
+
diff --git a/larn/diag.c b/larn/diag.c
new file mode 100644
index 0000000..a3482c1
--- /dev/null
+++ b/larn/diag.c
@@ -0,0 +1,411 @@
+/* $NetBSD: diag.c,v 1.13 2012/06/19 05:30:43 dholland Exp $ */
+
+/* diag.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: diag.c,v 1.13 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void greedy(void);
+static void fsorry(void);
+static void fcheat(void);
+
+static struct tms cputime;
+
+/*
+ ***************************
+ DIAG -- dungeon diagnostics
+ ***************************
+
+ subroutine to print out data for debugging
+ */
+#ifdef EXTRA
+static int rndcount[16];
+void
+diag()
+{
+ int i, j;
+ int hit, dam;
+ cursors();
+ lwclose();
+ if (lcreat(diagfile) < 0) { /* open the diagnostic file */
+ lcreat((char *) 0);
+ lprcat("\ndiagnostic failure\n");
+ return (-1);
+ }
+ write(1, "\nDiagnosing . . .\n", 18);
+ lprcat("\n\nBeginning of DIAG diagnostics ----------\n");
+
+ /* for the character attributes */
+
+ lprintf("\n\nPlayer attributes:\n\nHit points: %2ld(%2ld)", (long) c[HP], (long) c[HPMAX]);
+ lprintf("\ngold: %ld Experience: %ld Character level: %ld Level in caverns: %ld",
+ (long) c[GOLD], (long) c[EXPERIENCE], (long) c[LEVEL], (long) level);
+ lprintf("\nTotal types of monsters: %ld", (long) MAXMONST + 8);
+
+ lprcat("\f\nHere's the dungeon:\n\n");
+
+ i = level;
+ for (j = 0; j < MAXLEVEL + MAXVLEVEL; j++) {
+ newcavelevel(j);
+ lprintf("\nMaze for level %s:\n", levelname[level]);
+ diagdrawscreen();
+ }
+ newcavelevel(i);
+
+ lprcat("\f\nNow for the monster data:\n\n");
+ lprcat(" Monster Name LEV AC DAM ATT DEF GOLD HP EXP \n");
+ lprcat("--------------------------------------------------------------------------\n");
+ for (i = 0; i <= MAXMONST + 8; i++) {
+ lprintf("%19s %2ld %3ld ", monster[i].name, (long) monster[i].level, (long) monster[i].armorclass);
+ lprintf(" %3ld %3ld %3ld ", (long) monster[i].damage, (long) monster[i].attack, (long) monster[i].defense);
+ lprintf("%6ld %3ld %6ld\n", (long) monster[i].gold, (long) monster[i].hitpoints, (long) monster[i].experience);
+ }
+
+ lprcat("\n\nHere's a Table for the to hit percentages\n");
+ lprcat("\n We will be assuming that players level = 2 * monster level");
+ lprcat("\n and that the players dexterity and strength are 16.");
+ lprcat("\n to hit: if (rnd(22) < (2[monst AC] + your level + dex + WC/8 -1)/2) then hit");
+ lprcat("\n damage = rund(8) + WC/2 + STR - c[HARDGAME] - 4");
+ lprcat("\n to hit: if rnd(22) < to hit then player hits\n");
+ lprcat("\n Each entry is as follows: to hit / damage / number hits to kill\n");
+ lprcat("\n monster WC = 4 WC = 20 WC = 40");
+ lprcat("\n---------------------------------------------------------------");
+ for (i = 0; i <= MAXMONST + 8; i++) {
+ hit = 2 * monster[i].armorclass + 2 * monster[i].level + 16;
+ dam = 16 - c[HARDGAME];
+ lprintf("\n%20s %2ld/%2ld/%2ld %2ld/%2ld/%2ld %2ld/%2ld/%2ld",
+ monster[i].name,
+ (long) (hit / 2), (long) max(0, dam + 2), (long) (monster[i].hitpoints / (dam + 2) + 1),
+ (long) ((hit + 2) / 2), (long) max(0, dam + 10), (long) (monster[i].hitpoints / (dam + 10) + 1),
+ (long) ((hit + 5) / 2), (long) max(0, dam + 20), (long) (monster[i].hitpoints / (dam + 20) + 1));
+ }
+
+ lprcat("\n\nHere's the list of available potions:\n\n");
+ for (i = 0; i < MAXPOTION; i++)
+ lprintf("%20s\n", &potionhide[i][1]);
+ lprcat("\n\nHere's the list of available scrolls:\n\n");
+ for (i = 0; i < MAXSCROLL; i++)
+ lprintf("%20s\n", &scrollhide[i][1]);
+ lprcat("\n\nHere's the spell list:\n\n");
+ lprcat("spell name description\n");
+ lprcat("-------------------------------------------------------------------------------------------\n\n");
+ for (j = 0; j < SPNUM; j++) {
+ lprc(' ');
+ lprcat(spelcode[j]);
+ lprintf(" %21s %s\n", spelname[j], speldescript[j]);
+ }
+
+ lprcat("\n\nFor the c[] array:\n");
+ for (j = 0; j < 100; j += 10) {
+ lprintf("\nc[%2ld] = ", (long) j);
+ for (i = 0; i < 9; i++)
+ lprintf("%5ld ", (long) c[i + j]);
+ }
+
+ lprcat("\n\nTest of random number generator ----------------");
+ lprcat("\n for 25,000 calls divided into 16 slots\n\n");
+
+ for (i = 0; i < 16; i++)
+ rndcount[i] = 0;
+ for (i = 0; i < 25000; i++)
+ rndcount[rund(16)]++;
+ for (i = 0; i < 16; i++) {
+ lprintf(" %5ld", (long) rndcount[i]);
+ if (i == 7)
+ lprc('\n');
+ }
+
+ lprcat("\n\n");
+ lwclose();
+ lcreat((char *) 0);
+ lprcat("Done Diagnosing . . .");
+ return (0);
+}
+/*
+ subroutine to count the number of occurrences of an object
+ */
+int
+dcount(l)
+ int l;
+{
+ int i, j, p;
+ int k;
+ k = 0;
+ for (i = 0; i < MAXX; i++)
+ for (j = 0; j < MAXY; j++)
+ for (p = 0; p < MAXLEVEL; p++)
+ if (cell[p * MAXX * MAXY + i * MAXY + j].item == l)
+ k++;
+ return (k);
+}
+
+/*
+ subroutine to draw the whole screen as the player knows it
+ */
+void
+diagdrawscreen()
+{
+ int i, j, k;
+
+ for (i = 0; i < MAXY; i++)
+ /* for the east west walls of this line */
+ {
+ for (j = 0; j < MAXX; j++)
+ if (k = mitem[j][i])
+ lprc(monstnamelist[k]);
+ else
+ lprc(objnamelist[item[j][i]]);
+ lprc('\n');
+ }
+}
+#endif
+
+
+/*
+ to save the game in a file
+ */
+static time_t zzz = 0;
+int
+savegame(char *fname)
+{
+ int i, k;
+ struct sphere *sp;
+ struct stat statbuf;
+
+ nosignal = 1;
+ lflush();
+ savelevel();
+ ointerest();
+ if (lcreat(fname) < 0) {
+ lcreat((char *) 0);
+ lprintf("\nCan't open file <%s> to save game\n", fname);
+ nosignal = 0;
+ return (-1);
+ }
+ set_score_output();
+ lwrite((char *) beenhere, MAXLEVEL + MAXVLEVEL);
+ for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++)
+ if (beenhere[k])
+ lwrite((char *) &cell[k * MAXX * MAXY], sizeof(struct cel) * MAXY * MAXX);
+ times(&cputime); /* get cpu time */
+ c[CPUTIME] += (cputime.tms_utime + cputime.tms_stime) / 60;
+ lwrite((char *) &c[0], 100 * sizeof(long));
+ lprint((long) gltime);
+ lprc(level);
+ lprc(playerx);
+ lprc(playery);
+ lwrite((char *) iven, 26);
+ lwrite((char *) ivenarg, 26 * sizeof(short));
+ for (k = 0; k < MAXSCROLL; k++)
+ lprc(scrollname[k][0]);
+ for (k = 0; k < MAXPOTION; k++)
+ lprc(potionname[k][0]);
+ lwrite((char *) spelknow, SPNUM);
+ lprc(wizard);
+ lprc(rmst); /* random monster generation counter */
+ for (i = 0; i < 90; i++)
+ lprc(itm[i].qty);
+ lwrite((char *) course, 25);
+ lprc(cheat);
+ lprc(VERSION);
+ for (i = 0; i < MAXMONST; i++)
+ lprc(monster[i].genocided); /* genocide info */
+ for (sp = spheres; sp; sp = sp->p)
+ lwrite((char *) sp, sizeof(struct sphere)); /* save spheres of
+ * annihilation */
+ time(&zzz);
+ lprint((long) (zzz - initialtime));
+ lwrite((char *) &zzz, sizeof(long));
+ if (fstat(io_outfd, &statbuf) < 0)
+ lprint(0L);
+ else
+ lprint((long) statbuf.st_ino); /* inode # */
+ lwclose();
+ lastmonst[0] = 0;
+#ifndef VT100
+ setscroll();
+#endif /* VT100 */
+ lcreat((char *) 0);
+ nosignal = 0;
+ return (0);
+}
+
+void
+restoregame(char *fname)
+{
+ int i, k;
+ struct sphere *sp, *sp2;
+ struct stat filetimes;
+ cursors();
+ lprcat("\nRestoring . . .");
+ lflush();
+ if (lopen(fname) <= 0) {
+ lcreat((char *) 0);
+ lprintf("\nCan't open file <%s>to restore game\n", fname);
+ nap(2000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-265);
+ return;
+ }
+ lrfill((char *) beenhere, MAXLEVEL + MAXVLEVEL);
+ for (k = 0; k < MAXLEVEL + MAXVLEVEL; k++)
+ if (beenhere[k])
+ lrfill((char *) &cell[k * MAXX * MAXY], sizeof(struct cel) * MAXY * MAXX);
+
+ lrfill((char *) &c[0], 100 * sizeof(long));
+ gltime = larn_lrint();
+ level = c[CAVELEVEL] = lgetc();
+ playerx = lgetc();
+ playery = lgetc();
+ lrfill((char *) iven, 26);
+ lrfill((char *) ivenarg, 26 * sizeof(short));
+ for (k = 0; k < MAXSCROLL; k++)
+ scrollname[k] = lgetc() ? scrollhide[k] : "";
+ for (k = 0; k < MAXPOTION; k++)
+ potionname[k] = lgetc() ? potionhide[k] : "";
+ lrfill((char *) spelknow, SPNUM);
+ wizard = lgetc();
+ rmst = lgetc(); /* random monster creation flag */
+
+ for (i = 0; i < 90; i++)
+ itm[i].qty = lgetc();
+ lrfill((char *) course, 25);
+ cheat = lgetc();
+ if (VERSION != lgetc()) { /* version number */
+ cheat = 1;
+ lprcat("Sorry, But your save file is for an older version of larn\n");
+ nap(2000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-266);
+ return;
+ }
+ for (i = 0; i < MAXMONST; i++)
+ monster[i].genocided = lgetc(); /* genocide info */
+ for (sp = 0, i = 0; i < c[SPHCAST]; i++) {
+ sp2 = sp;
+ sp = (struct sphere *) malloc(sizeof(struct sphere));
+ if (sp == 0) {
+ write(2, "Can't malloc() for sphere space\n", 32);
+ break;
+ }
+ lrfill((char *) sp, sizeof(struct sphere)); /* get spheres of
+ * annihilation */
+ sp->p = 0; /* null out pointer */
+ if (i == 0)
+ spheres = sp; /* beginning of list */
+ else
+ sp2->p = sp;
+ }
+
+ time(&zzz);
+ initialtime = zzz - larn_lrint();
+ /* get the creation and modification time of file */
+ fstat(io_infd, &filetimes);
+ lrfill((char *) &zzz, sizeof(long));
+ zzz += 6;
+ if (filetimes.st_ctime > zzz)
+ fsorry(); /* file create time */
+ else if (filetimes.st_mtime > zzz)
+ fsorry(); /* file modify time */
+ if (c[HP] < 0) {
+ died(284);
+ return;
+ } /* died a post mortem death */
+ oldx = oldy = 0;
+ /* XXX the following will break on 64-bit inode numbers */
+ i = larn_lrint(); /* inode # */
+ if (i && (filetimes.st_ino != (ino_t) i))
+ fsorry();
+ lrclose();
+ if (strcmp(fname, ckpfile) == 0) {
+ if (lappend(fname) < 0)
+ fcheat();
+ else {
+ lprc(' ');
+ lwclose();
+ }
+ lcreat((char *) 0);
+ } else if (unlink(fname) < 0)
+ fcheat(); /* can't unlink save file */
+ /* for the greedy cheater checker */
+ for (k = 0; k < 6; k++)
+ if (c[k] > 99)
+ greedy();
+ if (c[HPMAX] > 999 || c[SPELLMAX] > 125)
+ greedy();
+ if (c[LEVEL] == 25 && c[EXPERIENCE] > skill[24]) { /* if patch up lev 25
+ * player */
+ long tmp;
+ tmp = c[EXPERIENCE] - skill[24]; /* amount to go up */
+ c[EXPERIENCE] = skill[24];
+ raiseexperience((long) tmp);
+ }
+ getlevel();
+ lasttime = gltime;
+}
+
+/*
+ subroutine to not allow greedy cheaters
+ */
+static void
+greedy(void)
+{
+#if WIZID
+ if (wizard)
+ return;
+#endif
+
+ lprcat("\n\nI am so sorry, but your character is a little TOO good! Since this\n");
+ lprcat("cannot normally happen from an honest game, I must assume that you cheated.\n");
+ lprcat("In that you are GREEDY as well as a CHEATER, I cannot allow this game\n");
+ lprcat("to continue.\n");
+ nap(5000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-267);
+ return;
+}
+
+/*
+ subroutine to not allow altered save files and terminate the attempted
+ restart
+ */
+static void
+fsorry(void)
+{
+ lprcat("\nSorry, but your savefile has been altered.\n");
+ lprcat("However, seeing as I am a good sport, I will let you play.\n");
+ lprcat("Be advised though, you won't be placed on the normal scoreboard.");
+ cheat = 1;
+ nap(4000);
+}
+
+/*
+ subroutine to not allow game if save file can't be deleted
+ */
+static void
+fcheat(void)
+{
+#if WIZID
+ if (wizard)
+ return;
+#endif
+
+ lprcat("\nSorry, but your savefile can't be deleted. This can only mean\n");
+ lprcat("that you tried to CHEAT by protecting the directory the savefile\n");
+ lprcat("is in. Since this is unfair to the rest of the larn community, I\n");
+ lprcat("cannot let you play this game.\n");
+ nap(5000);
+ c[GOLD] = c[BANKACCOUNT] = 0;
+ died(-268);
+ return;
+}
diff --git a/larn/display.c b/larn/display.c
new file mode 100644
index 0000000..3f4389b
--- /dev/null
+++ b/larn/display.c
@@ -0,0 +1,637 @@
+/* $NetBSD: display.c,v 1.10 2012/06/19 05:30:43 dholland Exp $ */
+
+/* display.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: display.c,v 1.10 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+
+#define makecode(_a,_b,_c) (((_a)<<16) + ((_b)<<8) + (_c))
+
+static void bot_hpx(void);
+static void bot_spellx(void);
+static void botside(void);
+static void botsub(int, const char *);
+static void seepage(void);
+
+static int minx, maxx, miny, maxy, k, m;
+static char bot1f = 0, bot2f = 0, bot3f = 0;
+static char always = 0;
+/*
+ bottomline()
+
+ now for the bottom line of the display
+ */
+void
+bottomline(void)
+{
+ recalc();
+ bot1f = 1;
+}
+
+void
+bottomhp(void)
+{
+ bot2f = 1;
+}
+
+void
+bottomspell(void)
+{
+ bot3f = 1;
+}
+
+void
+bottomdo(void)
+{
+ if (bot1f) {
+ bot3f = bot1f = bot2f = 0;
+ bot_linex();
+ return;
+ }
+ if (bot2f) {
+ bot2f = 0;
+ bot_hpx();
+ }
+ if (bot3f) {
+ bot3f = 0;
+ bot_spellx();
+ }
+}
+
+void
+bot_linex(void)
+{
+ int i;
+ if (cbak[SPELLS] <= -50 || (always)) {
+ cursor(1, 18);
+ if (c[SPELLMAX] > 99)
+ lprintf("Spells:%3ld(%3ld)", (long) c[SPELLS], (long) c[SPELLMAX]);
+ else
+ lprintf("Spells:%3ld(%2ld) ", (long) c[SPELLS], (long) c[SPELLMAX]);
+ lprintf(" AC: %-3ld WC: %-3ld Level", (long) c[AC], (long) c[WCLASS]);
+ if (c[LEVEL] > 99)
+ lprintf("%3ld", (long) c[LEVEL]);
+ else
+ lprintf(" %-2ld", (long) c[LEVEL]);
+ lprintf(" Exp: %-9ld %s\n", (long) c[EXPERIENCE], class[c[LEVEL] - 1]);
+ lprintf("HP: %3ld(%3ld) STR=%-2ld INT=%-2ld ",
+ (long) c[HP], (long) c[HPMAX], (long) (c[STRENGTH] + c[STREXTRA]), (long) c[INTELLIGENCE]);
+ lprintf("WIS=%-2ld CON=%-2ld DEX=%-2ld CHA=%-2ld LV:",
+ (long) c[WISDOM], (long) c[CONSTITUTION], (long) c[DEXTERITY], (long) c[CHARISMA]);
+
+ if ((level == 0) || (wizard))
+ c[TELEFLAG] = 0;
+ if (c[TELEFLAG])
+ lprcat(" ?");
+ else
+ lprcat(levelname[level]);
+ lprintf(" Gold: %-6ld", (long) c[GOLD]);
+ always = 1;
+ botside();
+ c[TMP] = c[STRENGTH] + c[STREXTRA];
+ for (i = 0; i < 100; i++)
+ cbak[i] = c[i];
+ return;
+ }
+ botsub(makecode(SPELLS, 8, 18), "%3ld");
+ if (c[SPELLMAX] > 99)
+ botsub(makecode(SPELLMAX, 12, 18), "%3ld)");
+ else
+ botsub(makecode(SPELLMAX, 12, 18), "%2ld) ");
+ botsub(makecode(HP, 5, 19), "%3ld");
+ botsub(makecode(HPMAX, 9, 19), "%3ld");
+ botsub(makecode(AC, 21, 18), "%-3ld");
+ botsub(makecode(WCLASS, 30, 18), "%-3ld");
+ botsub(makecode(EXPERIENCE, 49, 18), "%-9ld");
+ if (c[LEVEL] != cbak[LEVEL]) {
+ cursor(59, 18);
+ lprcat(class[c[LEVEL] - 1]);
+ }
+ if (c[LEVEL] > 99)
+ botsub(makecode(LEVEL, 40, 18), "%3ld");
+ else
+ botsub(makecode(LEVEL, 40, 18), " %-2ld");
+ c[TMP] = c[STRENGTH] + c[STREXTRA];
+ botsub(makecode(TMP, 18, 19), "%-2ld");
+ botsub(makecode(INTELLIGENCE, 25, 19), "%-2ld");
+ botsub(makecode(WISDOM, 32, 19), "%-2ld");
+ botsub(makecode(CONSTITUTION, 39, 19), "%-2ld");
+ botsub(makecode(DEXTERITY, 46, 19), "%-2ld");
+ botsub(makecode(CHARISMA, 53, 19), "%-2ld");
+ if ((level != cbak[CAVELEVEL]) || (c[TELEFLAG] != cbak[TELEFLAG])) {
+ if ((level == 0) || (wizard))
+ c[TELEFLAG] = 0;
+ cbak[TELEFLAG] = c[TELEFLAG];
+ cbak[CAVELEVEL] = level;
+ cursor(59, 19);
+ if (c[TELEFLAG])
+ lprcat(" ?");
+ else
+ lprcat(levelname[level]);
+ }
+ botsub(makecode(GOLD, 69, 19), "%-6ld");
+ botside();
+}
+
+/*
+ special subroutine to update only the gold number on the bottomlines
+ called from ogold()
+ */
+void
+bottomgold(void)
+{
+ botsub(makecode(GOLD, 69, 19), "%-6ld");
+ /* botsub(GOLD,"%-6ld",69,19); */
+}
+
+/*
+ special routine to update hp and level fields on bottom lines
+ called in monster.c hitplayer() and spattack()
+ */
+static void
+bot_hpx(void)
+{
+ if (c[EXPERIENCE] != cbak[EXPERIENCE]) {
+ recalc();
+ bot_linex();
+ } else
+ botsub(makecode(HP, 5, 19), "%3ld");
+}
+
+/*
+ special routine to update number of spells called from regen()
+ */
+static void
+bot_spellx(void)
+{
+ botsub(makecode(SPELLS, 9, 18), "%2ld");
+}
+
+/*
+ common subroutine for a more economical bottomline()
+ */
+static struct bot_side_def {
+ int typ;
+ const char *string;
+}
+ bot_data[] =
+{
+ { STEALTH, "stealth"},
+ { UNDEADPRO, "undead pro" },
+ { SPIRITPRO, "spirit pro" },
+ { CHARMCOUNT, "Charm"},
+ { TIMESTOP, "Time Stop" },
+ { HOLDMONST, "Hold Monst" },
+ { GIANTSTR, "Giant Str"},
+ { FIRERESISTANCE, "Fire Resit" },
+ { DEXCOUNT, "Dexterity" },
+ { STRCOUNT, "Strength"},
+ { SCAREMONST, "Scare" },
+ { HASTESELF, "Haste Self" },
+ { CANCELLATION, "Cancel"},
+ { INVISIBILITY, "Invisible" },
+ { ALTPRO, "Protect 3" },
+ { PROTECTIONTIME, "Protect 2"},
+ { WTW, "Wall-Walk" }
+};
+
+static void
+botside(void)
+{
+ int i, idx;
+ for (i = 0; i < 17; i++) {
+ idx = bot_data[i].typ;
+ if ((always) || (c[idx] != cbak[idx])) {
+ if ((always) || (cbak[idx] == 0)) {
+ if (c[idx]) {
+ cursor(70, i + 1);
+ lprcat(bot_data[i].string);
+ }
+ } else if (c[idx] == 0) {
+ cursor(70, i + 1);
+ lprcat(" ");
+ }
+ cbak[idx] = c[idx];
+ }
+ }
+ always = 0;
+}
+
+static void
+botsub(int idx, const char *str)
+{
+ int x, y;
+ y = idx & 0xff;
+ x = (idx >> 8) & 0xff;
+ idx >>= 16;
+ if (c[idx] != cbak[idx]) {
+ cbak[idx] = c[idx];
+ cursor(x, y);
+ lprintf(str, (long) c[idx]);
+ }
+}
+
+/*
+ * subroutine to draw only a section of the screen
+ * only the top section of the screen is updated.
+ * If entire lines are being drawn, then they will be cleared first.
+ */
+/* for limited screen drawing */
+static int d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY;
+
+void
+draws(int xmin, int xmax, int ymin, int ymax)
+{
+ int i, idx;
+ if (xmin == 0 && xmax == MAXX) { /* clear section of screen as
+ * needed */
+ if (ymin == 0)
+ cl_up(79, ymax);
+ else
+ for (i = ymin; i < ymin; i++)
+ cl_line(1, i + 1);
+ xmin = -1;
+ }
+ d_xmin = xmin;
+ d_xmax = xmax;
+ d_ymin = ymin;
+ d_ymax = ymax; /* for limited screen drawing */
+ drawscreen();
+ if (xmin <= 0 && xmax == MAXX) { /* draw stuff on right side
+ * of screen as needed */
+ for (i = ymin; i < ymax; i++) {
+ idx = bot_data[i].typ;
+ if (c[idx]) {
+ cursor(70, i + 1);
+ lprcat(bot_data[i].string);
+ }
+ cbak[idx] = c[idx];
+ }
+ }
+}
+
+/*
+ drawscreen()
+
+ subroutine to redraw the whole screen as the player knows it
+ */
+u_char screen[MAXX][MAXY]; /* template for the screen */
+static u_char d_flag;
+void
+drawscreen(void)
+{
+ int i, j, kk;
+ int lastx, lasty; /* variables used to optimize the
+ * object printing */
+ if (d_xmin == 0 && d_xmax == MAXX && d_ymin == 0 && d_ymax == MAXY) {
+ d_flag = 1;
+ clear(); /* clear the screen */
+ } else {
+ d_flag = 0;
+ cursor(1, 1);
+ }
+ if (d_xmin < 0)
+ d_xmin = 0; /* d_xmin=-1 means display all without
+ * bottomline */
+
+ for (i = d_ymin; i < d_ymax; i++)
+ for (j = d_xmin; j < d_xmax; j++)
+ if (know[j][i] == 0)
+ screen[j][i] = ' ';
+ else if ((kk = mitem[j][i]) != 0)
+ screen[j][i] = monstnamelist[kk];
+ else if ((kk = item[j][i]) == OWALL)
+ screen[j][i] = '#';
+ else
+ screen[j][i] = ' ';
+
+ for (i = d_ymin; i < d_ymax; i++) {
+ j = d_xmin;
+ while ((screen[j][i] == ' ') && (j < d_xmax))
+ j++;
+ /* was m=0 */
+ if (j >= d_xmax)
+ m = d_xmin; /* don't search backwards if blank
+ * line */
+ else { /* search backwards for end of line */
+ m = d_xmax - 1;
+ while ((screen[m][i] == ' ') && (m > d_xmin))
+ --m;
+ if (j <= m)
+ cursor(j + 1, i + 1);
+ else
+ continue;
+ }
+ while (j <= m) {
+ if (j <= m - 3) {
+ for (kk = j; kk <= j + 3; kk++)
+ if (screen[kk][i] != ' ')
+ kk = 1000;
+ if (kk < 1000) {
+ while (screen[j][i] == ' ' && j <= m)
+ j++;
+ cursor(j + 1, i + 1);
+ }
+ }
+ lprc(screen[j++][i]);
+ }
+ }
+ // setbold(); /* print out only bold objects now */
+
+ for (lastx = lasty = 127, i = d_ymin; i < d_ymax; i++)
+ for (j = d_xmin; j < d_xmax; j++) {
+ if ((kk = item[j][i]) != 0)
+ if (kk != OWALL)
+ if ((know[j][i]) && (mitem[j][i] == 0))
+ if (objnamelist[kk] != ' ') {
+ if (lasty != i + 1 || lastx != j)
+ cursor(lastx = j + 1, lasty = i + 1);
+ else
+ lastx++;
+ lprc(objnamelist[kk]);
+ }
+ }
+
+ // resetbold();
+ if (d_flag) {
+ always = 1;
+ botside();
+ always = 1;
+ bot_linex();
+ }
+ oldx = 99;
+ d_xmin = 0, d_xmax = MAXX, d_ymin = 0, d_ymax = MAXY; /* for limited screen
+ * drawing */
+}
+
+
+/*
+ showcell(x,y)
+
+ subroutine to display a cell location on the screen
+ */
+void
+showcell(int x, int y)
+{
+ int i, j, kk, mm;
+ if (c[BLINDCOUNT])
+ // 20150211 bkw:
+ /* return */; /* see nothing if blind */
+ if (c[AWARENESS]) {
+ minx = x - 3;
+ maxx = x + 3;
+ miny = y - 3;
+ maxy = y + 3;
+ } else {
+ minx = x - 1;
+ maxx = x + 1;
+ miny = y - 1;
+ maxy = y + 1;
+ }
+
+ if (minx < 0)
+ minx = 0;
+ if (maxx > MAXX - 1)
+ maxx = MAXX - 1;
+ if (miny < 0)
+ miny = 0;
+ if (maxy > MAXY - 1)
+ maxy = MAXY - 1;
+
+ for (j = miny; j <= maxy; j++)
+ for (mm = minx; mm <= maxx; mm++)
+ if (know[mm][j] == 0) {
+ cursor(mm + 1, j + 1);
+ x = maxx;
+ while (know[x][j])
+ --x;
+ for (i = mm; i <= x; i++) {
+ if ((kk = mitem[i][j]) != 0)
+ lprc(monstnamelist[kk]);
+ else
+ switch (kk = item[i][j]) {
+ case OWALL:
+ case 0:
+ case OIVTELETRAP:
+ case OTRAPARROWIV:
+ case OIVDARTRAP:
+ case OIVTRAPDOOR:
+ lprc(objnamelist[kk]);
+ break;
+
+ default:
+ setbold();
+ lprc(objnamelist[kk]);
+ resetbold();
+ };
+ know[i][j] = 1;
+ }
+ mm = maxx;
+ }
+}
+
+/*
+ this routine shows only the spot that is given it. the spaces around
+ these coordinated are not shown
+ used in godirect() in monster.c for missile weapons display
+ */
+void
+show1cell(int x, int y)
+{
+ if (c[BLINDCOUNT])
+ return; /* see nothing if blind */
+ cursor(x + 1, y + 1);
+ if ((k = mitem[x][y]) != 0)
+ lprc(monstnamelist[k]);
+ else
+ switch (k = item[x][y]) {
+ case OWALL:
+ case 0:
+ case OIVTELETRAP:
+ case OTRAPARROWIV:
+ case OIVDARTRAP:
+ case OIVTRAPDOOR:
+ lprc(objnamelist[k]);
+ break;
+
+ default:
+ setbold();
+ lprc(objnamelist[k]);
+ resetbold();
+ };
+ know[x][y] |= 1; /* we end up knowing about it */
+}
+
+/*
+ showplayer()
+
+ subroutine to show where the player is on the screen
+ cursor values start from 1 up
+ */
+void
+showplayer(void)
+{
+/* 20150211 bkw: put back the player's @. Took the function body
+ from some old larn source originally posted to USEnet in the
+ paleozoic era. */
+ if(oldx != 99) show1cell( oldx, oldy );
+ cursor(playerx+1,playery+1);
+ lprc('@');
+ cursor(playerx+1,playery+1);
+ oldx = playerx;
+ oldy = playery;
+}
+
+/*
+ moveplayer(dir)
+
+ subroutine to move the player from one room to another
+ returns 0 if can't move in that direction or hit a monster or on an object
+ else returns 1
+ nomove is set to 1 to stop the next move (inadvertent monsters hitting
+ players when walking into walls) if player walks off screen or into wall
+ */
+short diroffx[] = {0, 0, 1, 0, -1, 1, -1, 1, -1};
+short diroffy[] = {0, 1, 0, -1, 0, -1, -1, 1, 1};
+int
+moveplayer(int dir)
+ /* from = present room # direction =
+ * [1-north] [2-east] [3-south] [4-west]
+ * [5-northeast] [6-northwest] [7-southeast]
+ * [8-southwest] if direction=0, don't
+ * move--just show where he is */
+{
+ int kk, mm, i, j;
+ if (c[CONFUSE])
+ if (c[LEVEL] < rnd(30))
+ dir = rund(9); /* if confused any dir */
+ kk = playerx + diroffx[dir];
+ mm = playery + diroffy[dir];
+ if (kk < 0 || kk >= MAXX || mm < 0 || mm >= MAXY) {
+ nomove = 1;
+ return (yrepcount = 0);
+ }
+ i = item[kk][mm];
+ j = mitem[kk][mm];
+ if (i == OWALL && c[WTW] == 0) {
+ nomove = 1;
+ return (yrepcount = 0);
+ } /* hit a wall */
+ if (kk == 33 && mm == MAXY - 1 && level == 1) {
+ newcavelevel(0);
+ for (kk = 0; kk < MAXX; kk++)
+ for (mm = 0; mm < MAXY; mm++)
+ if (item[kk][mm] == OENTRANCE) {
+ playerx = kk;
+ playery = mm;
+ positionplayer();
+ drawscreen();
+ return (0);
+ }
+ }
+ if (j > 0) {
+ hitmonster(kk, mm);
+ return (yrepcount = 0);
+ } /* hit a monster */
+ lastpx = playerx;
+ lastpy = playery;
+ playerx = kk;
+ playery = mm;
+ if (i && i != OTRAPARROWIV && i != OIVTELETRAP && i != OIVDARTRAP && i != OIVTRAPDOOR)
+ return (yrepcount = 0);
+ else
+ return (1);
+}
+
+
+/*
+ * function to show what magic items have been discovered thus far
+ * enter with -1 for just spells, anything else will give scrolls & potions
+ */
+static int lincount, count;
+void
+seemagic(int arg)
+{
+ int i, number = 0;
+ count = lincount = 0;
+ nosignal = 1;
+
+ if (arg == -1) { /* if display spells while casting one */
+ for (number = i = 0; i < SPNUM; i++)
+ if (spelknow[i])
+ number++;
+ number = (number + 2) / 3 + 4; /* # lines needed to display */
+ cl_up(79, number);
+ cursor(1, 1);
+ } else {
+ resetscroll();
+ clear();
+ }
+
+ lprcat("The magic spells you have discovered thus far:\n\n");
+ for (i = 0; i < SPNUM; i++)
+ if (spelknow[i]) {
+ lprintf("%s %-20s ", spelcode[i], spelname[i]);
+ seepage();
+ }
+ if (arg == -1) {
+ seepage();
+ more();
+ nosignal = 0;
+ draws(0, MAXX, 0, number);
+ return;
+ }
+ lincount += 3;
+ if (count != 0) {
+ count = 2;
+ seepage();
+ }
+ lprcat("\nThe magic scrolls you have found to date are:\n\n");
+ count = 0;
+ for (i = 0; i < MAXSCROLL; i++)
+ if (scrollname[i][0])
+ if (scrollname[i][1] != ' ') {
+ lprintf("%-26s", &scrollname[i][1]);
+ seepage();
+ }
+ lincount += 3;
+ if (count != 0) {
+ count = 2;
+ seepage();
+ }
+ lprcat("\nThe magic potions you have found to date are:\n\n");
+ count = 0;
+ for (i = 0; i < MAXPOTION; i++)
+ if (potionname[i][0])
+ if (potionname[i][1] != ' ') {
+ lprintf("%-26s", &potionname[i][1]);
+ seepage();
+ }
+ if (lincount != 0)
+ more();
+ nosignal = 0;
+ setscroll();
+ drawscreen();
+}
+
+/*
+ * subroutine to paginate the seemagic function
+ */
+static void
+seepage(void)
+{
+ if (++count == 3) {
+ lincount++;
+ count = 0;
+ lprc('\n');
+ if (lincount > 17) {
+ lincount = 0;
+ more();
+ clear();
+ }
+ }
+}
diff --git a/larn/extern.h b/larn/extern.h
new file mode 100644
index 0000000..f0c681f
--- /dev/null
+++ b/larn/extern.h
@@ -0,0 +1,226 @@
+/* $NetBSD: extern.h,v 1.16 2011/08/29 20:30:37 joerg Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/* action.c */
+void act_remove_gems(int);
+void act_sit_throne(int);
+void act_drink_fountain(void);
+void act_wash_fountain(void);
+void act_desecrate_altar(void);
+void act_donation_pray(void);
+void act_just_pray(void);
+void act_ignore_altar(void);
+void act_open_chest(int, int);
+
+/* bill.c */
+__dead void mailbill(void);
+
+/* config.c */
+
+/* create.c */
+void makeplayer(void);
+void newcavelevel(int);
+void eat(int, int);
+int fillmonst(int);
+
+/* data.c */
+
+/* diag.c */
+void diag(void);
+int dcount(int);
+void diagdrawscreen(void);
+int savegame(char *);
+void restoregame(char *);
+
+/* display.c */
+void bottomline(void);
+void bottomhp(void);
+void bottomspell(void);
+void bottomdo(void);
+void bot_linex(void);
+void bottomgold(void);
+void draws(int, int, int, int);
+void drawscreen(void);
+void showcell(int, int);
+void show1cell(int, int);
+void showplayer(void);
+int moveplayer(int);
+void seemagic(int);
+
+/* fortune.c */
+const char *fortune(void);
+
+/* global.c */
+void raiselevel(void);
+void loselevel(void);
+void raiseexperience(long);
+void loseexperience(long);
+void losehp(int);
+void losemhp(int);
+void raisehp(int);
+void raisemhp(int);
+void raisemspells(int);
+void losemspells(int);
+int makemonst(int);
+void positionplayer(void);
+void recalc(void);
+void quit(void);
+void more(void);
+int take(int, int);
+int drop_object(int);
+void enchantarmor(void);
+void enchweapon(void);
+int pocketfull(void);
+int nearbymonst(void);
+int stealsomething(void);
+int emptyhanded(void);
+void creategem(void);
+void adjustcvalues(int, int);
+int getpassword(void);
+int getyn(void);
+int packweight(void);
+int rnd(int);
+int rund(int);
+
+/* help.c */
+void help(void);
+void welcome(void);
+
+/* io.c */
+void setupvt100(void);
+void clearvt100(void);
+int ttgetch(void);
+void scbr(void);
+void sncbr(void);
+void newgame(void);
+void lprintf(const char *, ...) __printflike(1, 2);
+void lprint(long);
+void lwrite(char *, int);
+long lgetc(void);
+long larn_lrint(void);
+void lrfill(char *, int);
+char *lgetw(void);
+char *lgetl(void);
+int lcreat(char *);
+int lopen(char *);
+int lappend(char *);
+void lrclose(void);
+void lwclose(void);
+void lprcat(const char *);
+void cursor(int, int);
+void cursors(void);
+void init_term(void);
+void cl_line(int, int);
+void cl_up(int, int);
+void cl_dn(int, int);
+void standout(const char *);
+void set_score_output(void);
+void lflush(void);
+char *tmcapcnv(char *, char *);
+void beep(void);
+
+/* main.c */
+int main(int, char **);
+void qshowstr(void);
+void show3(int);
+void parse2(void);
+unsigned long readnum(long);
+void szero(char *);
+
+/* monster.c */
+void createmonster(int);
+void createitem(int, int);
+void cast(void);
+void godirect(int, int, const char *, int, int);
+int vxy(int *, int *);
+void hitmonster(int, int);
+void hitplayer(int, int);
+void dropgold(int);
+void something(int);
+int newobject(int, int *);
+void checkloss(int);
+int annihilate(void);
+int newsphere(int, int, int, int);
+int rmsphere(int, int);
+
+/* moreobj.c */
+void oaltar(void);
+void othrone(int);
+void odeadthrone(void);
+void ochest(void);
+void ofountain(void);
+void fntchange(int);
+
+/* movem.c */
+void movemonst(void);
+
+/* nap.c */
+void nap(int);
+
+/* object.c */
+void lookforobject(void);
+void oteleport(int);
+void quaffpotion(int);
+void adjusttime(long);
+void read_scroll(int);
+void readbook(int);
+void iopts(void);
+void ignore(void);
+
+/* regen.c */
+void regen(void);
+
+/* savelev.c */
+void savelevel(void);
+void getlevel(void);
+
+/* scores.c */
+int makeboard(void);
+int hashewon(void);
+long paytaxes(long);
+void showscores(void);
+void showallscores(void);
+void died(int);
+void diedlog(void);
+int getplid(char *);
+
+/* signal.c */
+void sigsetup(void);
+
+/* store.c */
+void dndstore(void);
+void oschool(void);
+void obank(void);
+void obank2(void);
+void ointerest(void);
+void otradepost(void);
+void olrs(void);
+
+/* tok.c */
+int yylex(void);
+void flushall(void);
+void sethard(int);
+void readopts(void);
diff --git a/larn/fortune.c b/larn/fortune.c
new file mode 100644
index 0000000..4eb7f67
--- /dev/null
+++ b/larn/fortune.c
@@ -0,0 +1,94 @@
+/* $NetBSD: fortune.c,v 1.7 2009/08/12 08:04:05 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1991 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)fortune.c 5.5 (Berkeley) 6/10/91";
+#else
+__RCSID("$NetBSD: fortune.c,v 1.7 2009/08/12 08:04:05 dholland Exp $");
+#endif
+#endif /* not lint */
+
+#include <stdlib.h>
+
+#include "header.h"
+#include "extern.h"
+/* fortune.c Larn is copyrighted 1986 by Noah Morgan. */
+
+/*
+ * function to return a random fortune from the fortune file
+ */
+
+static const char *flines[] = {
+ "gem value = gem * 2 ^ perfection",
+ "sitting down can have unexpected results",
+ "don't pry into the affairs of others",
+ "drinking can be hazardous to your health",
+ "beware of the gusher!",
+ "some monsters are greedy",
+ "nymphs have light fingers",
+ "try kissing a disenchantress!",
+ "hammers and brains don't mix",
+ "what does a potion of cure dianthroritis taste like?",
+ "hit point gain/loss when raising a level depends on constitution",
+ "healing a mighty wizard can be exhilarating",
+ "be sure to pay your taxes",
+ "are Vampires afraid of something?",
+ "some dragons can fly",
+ "dos thou strive for perfection?",
+ "patience is a virtue, unless your daughter dies",
+ "what does the Eye of Larn see in its guardian?",
+ "a level 25 player casts like crazy!",
+ "energy rings affect spell regeneration",
+ "difficulty affects regeneration",
+ "control of the pesty spirits is most helpful",
+ "don't fall into a bottomless pit",
+ "dexterity allows you to carry more",
+ "you can get 2 points of WC for the price of one",
+ "never enter the dungeon naked! the monsters will laugh at you!",
+ "did someone put itching powder in your armor?",
+ "you klutz!",
+ "avoid opening doors. you never know whats on the other side.",
+ "infinite regeneration ---> temptation",
+ "the greatest weapon in the game has not the highest Weapon Class",
+ "you can't buy the most powerful scroll",
+ "identify things before you use them",
+ "there's more than one way through a wall"
+};
+
+#define NFORTUNES 34
+
+const char *
+fortune(void)
+{
+ return (flines[random() % NFORTUNES]);
+}
diff --git a/larn/global.c b/larn/global.c
new file mode 100644
index 0000000..013aa6f
--- /dev/null
+++ b/larn/global.c
@@ -0,0 +1,861 @@
+/* $NetBSD: global.c,v 1.14 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * global.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * raiselevel() subroutine to raise the player one level
+ * loselevel() subroutine to lower the player by one level
+ * raiseexperience(x) subroutine to increase experience points
+ * loseexperience(x) subroutine to lose experience points
+ * losehp(x) subroutine to remove hit points from the player
+ * losemhp(x) subroutine to remove max # hit points from the player
+ * raisehp(x) subroutine to gain hit points
+ * raisemhp(x) subroutine to gain maximum hit points
+ * losemspells(x) subroutine to lose maximum spells
+ * raisemspells(x) subroutine to gain maximum spells
+ * makemonst(lev) function to return monster number for a randomly
+ * selected monster
+ * positionplayer() function to be sure player is not in a wall
+ * recalc() function to recalculate the armor class of the player
+ * quit() subroutine to ask if the player really wants to quit
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: global.c,v 1.14 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <string.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+extern int score[], dropflag;
+extern char *what[], *who[];
+extern char winner[];
+extern char sciv[SCORESIZE + 1][26][2];
+extern const char *password;
+
+/*
+ raiselevel()
+
+ subroutine to raise the player one level
+ uses the skill[] array to find level boundarys
+ uses c[EXPERIENCE] c[LEVEL]
+ */
+void
+raiselevel(void)
+{
+ if (c[LEVEL] < MAXPLEVEL)
+ raiseexperience((long) (skill[c[LEVEL]] - c[EXPERIENCE]));
+}
+
+/*
+ loselevel()
+
+ subroutine to lower the players character level by one
+ */
+void
+loselevel(void)
+{
+ if (c[LEVEL] > 1)
+ loseexperience((long) (c[EXPERIENCE] - skill[c[LEVEL] - 1] + 1));
+}
+
+/*
+ raiseexperience(x)
+
+ subroutine to increase experience points
+ */
+void
+raiseexperience(long x)
+{
+ int i, tmp;
+ i = c[LEVEL];
+ c[EXPERIENCE] += x;
+ while (c[EXPERIENCE] >= skill[c[LEVEL]] && (c[LEVEL] < MAXPLEVEL)) {
+ tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1;
+ c[LEVEL]++;
+ raisemhp((int) (rnd(3) + rnd((tmp > 0) ? tmp : 1)));
+ raisemspells((int) rund(3));
+ if (c[LEVEL] < 7 - c[HARDGAME])
+ raisemhp((int) (c[CONSTITUTION] >> 2));
+ }
+ if (c[LEVEL] != i) {
+ cursors();
+ beep();
+ lprintf("\nWelcome to level %ld", (long) c[LEVEL]); /* if we changed levels */
+ }
+ bottomline();
+}
+
+/*
+ loseexperience(x)
+
+ subroutine to lose experience points
+ */
+void
+loseexperience(long x)
+{
+ int i, tmp;
+ i = c[LEVEL];
+ c[EXPERIENCE] -= x;
+ if (c[EXPERIENCE] < 0)
+ c[EXPERIENCE] = 0;
+ while (c[EXPERIENCE] < skill[c[LEVEL] - 1]) {
+ if (--c[LEVEL] <= 1)
+ c[LEVEL] = 1; /* down one level */
+ tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1; /* lose hpoints */
+ losemhp((int) rnd((tmp > 0) ? tmp : 1)); /* lose hpoints */
+ if (c[LEVEL] < 7 - c[HARDGAME])
+ losemhp((int) (c[CONSTITUTION] >> 2));
+ losemspells((int) rund(3)); /* lose spells */
+ }
+ if (i != c[LEVEL]) {
+ cursors();
+ beep();
+ lprintf("\nYou went down to level %ld!", (long) c[LEVEL]);
+ }
+ bottomline();
+}
+
+/*
+ losehp(x)
+ losemhp(x)
+
+ subroutine to remove hit points from the player
+ warning -- will kill player if hp goes to zero
+ */
+void
+losehp(int x)
+{
+ if ((c[HP] -= x) <= 0) {
+ beep();
+ lprcat("\n");
+ nap(3000);
+ died(lastnum);
+ }
+}
+
+void
+losemhp(int x)
+{
+ c[HP] -= x;
+ if (c[HP] < 1)
+ c[HP] = 1;
+ c[HPMAX] -= x;
+ if (c[HPMAX] < 1)
+ c[HPMAX] = 1;
+}
+
+/*
+ raisehp(x)
+ raisemhp(x)
+
+ subroutine to gain maximum hit points
+ */
+void
+raisehp(int x)
+{
+ if ((c[HP] += x) > c[HPMAX])
+ c[HP] = c[HPMAX];
+}
+
+void
+raisemhp(int x)
+{
+ c[HPMAX] += x;
+ c[HP] += x;
+}
+
+/*
+ raisemspells(x)
+
+ subroutine to gain maximum spells
+ */
+void
+raisemspells(int x)
+{
+ c[SPELLMAX] += x;
+ c[SPELLS] += x;
+}
+
+/*
+ losemspells(x)
+
+ subroutine to lose maximum spells
+ */
+void
+losemspells(int x)
+{
+ if ((c[SPELLMAX] -= x) < 0)
+ c[SPELLMAX] = 0;
+ if ((c[SPELLS] -= x) < 0)
+ c[SPELLS] = 0;
+}
+
+/*
+ makemonst(lev)
+ int lev;
+
+ function to return monster number for a randomly selected monster
+ for the given cave level
+ */
+int
+makemonst(int lev)
+{
+ int tmp, x;
+ if (lev < 1)
+ lev = 1;
+ if (lev > 12)
+ lev = 12;
+ tmp = WATERLORD;
+ if (lev < 5)
+ while (tmp == WATERLORD)
+ tmp = rnd((x = monstlevel[lev - 1]) ? x : 1);
+ else
+ while (tmp == WATERLORD)
+ tmp = rnd((x = monstlevel[lev - 1] - monstlevel[lev - 4]) ? x : 1) + monstlevel[lev - 4];
+
+ while (monster[tmp].genocided && tmp < MAXMONST)
+ tmp++; /* genocided? */
+ return (tmp);
+}
+
+/*
+ positionplayer()
+
+ function to be sure player is not in a wall
+ */
+void
+positionplayer(void)
+{
+ int try;
+ try = 2;
+ while ((item[playerx][playery] || mitem[playerx][playery]) && (try))
+ if (++playerx >= MAXX - 1) {
+ playerx = 1;
+ if (++playery >= MAXY - 1) {
+ playery = 1;
+ --try;
+ }
+ }
+ if (try == 0)
+ lprcat("Failure in positionplayer\n");
+}
+
+/*
+ recalc() function to recalculate the armor class of the player
+ */
+void
+recalc(void)
+{
+ int i, j, k;
+ c[AC] = c[MOREDEFENSES];
+ if (c[WEAR] >= 0)
+ switch (iven[c[WEAR]]) {
+ case OSHIELD:
+ c[AC] += 2 + ivenarg[c[WEAR]];
+ break;
+ case OLEATHER:
+ c[AC] += 2 + ivenarg[c[WEAR]];
+ break;
+ case OSTUDLEATHER:
+ c[AC] += 3 + ivenarg[c[WEAR]];
+ break;
+ case ORING:
+ c[AC] += 5 + ivenarg[c[WEAR]];
+ break;
+ case OCHAIN:
+ c[AC] += 6 + ivenarg[c[WEAR]];
+ break;
+ case OSPLINT:
+ c[AC] += 7 + ivenarg[c[WEAR]];
+ break;
+ case OPLATE:
+ c[AC] += 9 + ivenarg[c[WEAR]];
+ break;
+ case OPLATEARMOR:
+ c[AC] += 10 + ivenarg[c[WEAR]];
+ break;
+ case OSSPLATE:
+ c[AC] += 12 + ivenarg[c[WEAR]];
+ break;
+ }
+
+ if (c[SHIELD] >= 0)
+ if (iven[c[SHIELD]] == OSHIELD)
+ c[AC] += 2 + ivenarg[c[SHIELD]];
+ if (c[WIELD] < 0)
+ c[WCLASS] = 0;
+ else {
+ i = ivenarg[c[WIELD]];
+ switch (iven[c[WIELD]]) {
+ case ODAGGER:
+ c[WCLASS] = 3 + i;
+ break;
+ case OBELT:
+ c[WCLASS] = 7 + i;
+ break;
+ case OSHIELD:
+ c[WCLASS] = 8 + i;
+ break;
+ case OSPEAR:
+ c[WCLASS] = 10 + i;
+ break;
+ case OFLAIL:
+ c[WCLASS] = 14 + i;
+ break;
+ case OBATTLEAXE:
+ c[WCLASS] = 17 + i;
+ break;
+ case OLANCE:
+ c[WCLASS] = 19 + i;
+ break;
+ case OLONGSWORD:
+ c[WCLASS] = 22 + i;
+ break;
+ case O2SWORD:
+ c[WCLASS] = 26 + i;
+ break;
+ case OSWORD:
+ c[WCLASS] = 32 + i;
+ break;
+ case OSWORDofSLASHING:
+ c[WCLASS] = 30 + i;
+ break;
+ case OHAMMER:
+ c[WCLASS] = 35 + i;
+ break;
+ default:
+ c[WCLASS] = 0;
+ }
+ }
+ c[WCLASS] += c[MOREDAM];
+
+ /* now for regeneration abilities based on rings */
+ c[REGEN] = 1;
+ c[ENERGY] = 0;
+ j = 0;
+ for (k = 25; k > 0; k--)
+ if (iven[k]) {
+ j = k;
+ k = 0;
+ }
+ for (i = 0; i <= j; i++) {
+ switch (iven[i]) {
+ case OPROTRING:
+ c[AC] += ivenarg[i] + 1;
+ break;
+ case ODAMRING:
+ c[WCLASS] += ivenarg[i] + 1;
+ break;
+ case OBELT:
+ c[WCLASS] += ((ivenarg[i] << 1)) + 2;
+ break;
+
+ case OREGENRING:
+ c[REGEN] += ivenarg[i] + 1;
+ break;
+ case ORINGOFEXTRA:
+ c[REGEN] += 5 * (ivenarg[i] + 1);
+ break;
+ case OENERGYRING:
+ c[ENERGY] += ivenarg[i] + 1;
+ break;
+ }
+ }
+}
+
+
+/*
+ quit()
+
+ subroutine to ask if the player really wants to quit
+ */
+void
+quit(void)
+{
+ int i;
+ cursors();
+ strcpy(lastmonst, "");
+ lprcat("\n\nDo you really want to quit?");
+ while (1) {
+ i = ttgetch();
+ if (i == 'y') {
+ died(300);
+ return;
+ }
+ if ((i == 'n') || (i == '\33')) {
+ lprcat(" no");
+ lflush();
+ return;
+ }
+ lprcat("\n");
+ setbold();
+ lprcat("Yes");
+ resetbold();
+ lprcat(" or ");
+ setbold();
+ lprcat("No");
+ resetbold();
+ lprcat(" please? Do you want to quit? ");
+ }
+}
+
+/*
+ function to ask --more-- then the user must enter a space
+ */
+void
+more(void)
+{
+ lprcat("\n --- press ");
+ standout("space");
+ lprcat(" to continue --- ");
+ while (ttgetch() != ' ');
+}
+
+/*
+ function to put something in the players inventory
+ returns 0 if success, 1 if a failure
+ */
+int
+take(int theitem, int arg)
+{
+ int i, limit;
+ /* cursors(); */
+ if ((limit = 15 + (c[LEVEL] >> 1)) > 26)
+ limit = 26;
+ for (i = 0; i < limit; i++)
+ if (iven[i] == 0) {
+ iven[i] = theitem;
+ ivenarg[i] = arg;
+ limit = 0;
+ switch (theitem) {
+ case OPROTRING:
+ case ODAMRING:
+ case OBELT:
+ limit = 1;
+ break;
+ case ODEXRING:
+ c[DEXTERITY] += ivenarg[i] + 1;
+ limit = 1;
+ break;
+ case OSTRRING:
+ c[STREXTRA] += ivenarg[i] + 1;
+ limit = 1;
+ break;
+ case OCLEVERRING:
+ c[INTELLIGENCE] += ivenarg[i] + 1;
+ limit = 1;
+ break;
+ case OHAMMER:
+ c[DEXTERITY] += 10;
+ c[STREXTRA] += 10;
+ c[INTELLIGENCE] -= 10;
+ limit = 1;
+ break;
+
+ case OORBOFDRAGON:
+ c[SLAYING]++;
+ break;
+ case OSPIRITSCARAB:
+ c[NEGATESPIRIT]++;
+ break;
+ case OCUBEofUNDEAD:
+ c[CUBEofUNDEAD]++;
+ break;
+ case ONOTHEFT:
+ c[NOTHEFT]++;
+ break;
+ case OSWORDofSLASHING:
+ c[DEXTERITY] += 5;
+ limit = 1;
+ break;
+ };
+ lprcat("\nYou pick up:");
+ srcount = 0;
+ show3(i);
+ if (limit)
+ bottomline();
+ return (0);
+ }
+ lprcat("\nYou can't carry anything else");
+ return (1);
+}
+
+/*
+ subroutine to drop an object
+ returns 1 if something there already else 0
+ */
+int
+drop_object(int k)
+{
+ int theitem;
+ if ((k < 0) || (k > 25))
+ return (0);
+ theitem = iven[k];
+ cursors();
+ if (theitem == 0) {
+ lprintf("\nYou don't have item %c! ", k + 'a');
+ return (1);
+ }
+ if (item[playerx][playery]) {
+ beep();
+ lprcat("\nThere's something here already");
+ return (1);
+ }
+ if (playery == MAXY - 1 && playerx == 33)
+ return (1); /* not in entrance */
+ item[playerx][playery] = theitem;
+ iarg[playerx][playery] = ivenarg[k];
+ srcount = 0;
+ lprcat("\n You drop:");
+ show3(k); /* show what item you dropped */
+ know[playerx][playery] = 0;
+ iven[k] = 0;
+ if (c[WIELD] == k)
+ c[WIELD] = -1;
+ if (c[WEAR] == k)
+ c[WEAR] = -1;
+ if (c[SHIELD] == k)
+ c[SHIELD] = -1;
+ adjustcvalues(theitem, ivenarg[k]);
+ dropflag = 1; /* say dropped an item so wont ask to pick it
+ * up right away */
+ return (0);
+}
+
+/*
+ function to enchant armor player is currently wearing
+ */
+void
+enchantarmor(void)
+{
+ int tmp;
+ if (c[WEAR] < 0) {
+ if (c[SHIELD] < 0) {
+ cursors();
+ beep();
+ lprcat("\nYou feel a sense of loss");
+ return;
+ } else {
+ tmp = iven[c[SHIELD]];
+ if (tmp != OSCROLL)
+ if (tmp != OPOTION) {
+ ivenarg[c[SHIELD]]++;
+ bottomline();
+ }
+ }
+ }
+ tmp = iven[c[WEAR]];
+ if (tmp != OSCROLL)
+ if (tmp != OPOTION) {
+ ivenarg[c[WEAR]]++;
+ bottomline();
+ }
+}
+
+/*
+ function to enchant a weapon presently being wielded
+ */
+void
+enchweapon(void)
+{
+ int tmp;
+ if (c[WIELD] < 0) {
+ cursors();
+ beep();
+ lprcat("\nYou feel a sense of loss");
+ return;
+ }
+ tmp = iven[c[WIELD]];
+ if (tmp != OSCROLL)
+ if (tmp != OPOTION) {
+ ivenarg[c[WIELD]]++;
+ if (tmp == OCLEVERRING)
+ c[INTELLIGENCE]++;
+ else if (tmp == OSTRRING)
+ c[STREXTRA]++;
+ else if (tmp == ODEXRING)
+ c[DEXTERITY]++;
+ bottomline();
+ }
+}
+
+/*
+ routine to tell if player can carry one more thing
+ returns 1 if pockets are full, else 0
+ */
+int
+pocketfull(void)
+{
+ int i, limit;
+ if ((limit = 15 + (c[LEVEL] >> 1)) > 26)
+ limit = 26;
+ for (i = 0; i < limit; i++)
+ if (iven[i] == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ function to return 1 if a monster is next to the player else returns 0
+ */
+int
+nearbymonst(void)
+{
+ int tmp, tmp2;
+ for (tmp = playerx - 1; tmp < playerx + 2; tmp++)
+ for (tmp2 = playery - 1; tmp2 < playery + 2; tmp2++)
+ if (mitem[tmp][tmp2])
+ return (1); /* if monster nearby */
+ return (0);
+}
+
+/*
+ function to steal an item from the players pockets
+ returns 1 if steals something else returns 0
+ */
+int
+stealsomething(void)
+{
+ int i, j;
+ j = 100;
+ while (1) {
+ i = rund(26);
+ if (iven[i])
+ if (c[WEAR] != i)
+ if (c[WIELD] != i)
+ if (c[SHIELD] != i) {
+ srcount = 0;
+ show3(i);
+ adjustcvalues(iven[i], ivenarg[i]);
+ iven[i] = 0;
+ return (1);
+ }
+ if (--j <= 0)
+ return (0);
+ }
+}
+
+/*
+ function to return 1 is player carrys nothing else return 0
+ */
+int
+emptyhanded(void)
+{
+ int i;
+ for (i = 0; i < 26; i++)
+ if (iven[i])
+ if (i != c[WIELD])
+ if (i != c[WEAR])
+ if (i != c[SHIELD])
+ return (0);
+ return (1);
+}
+
+/*
+ function to create a gem on a square near the player
+ */
+void
+creategem(void)
+{
+ int i, j;
+ switch (rnd(4)) {
+ case 1:
+ i = ODIAMOND;
+ j = 50;
+ break;
+ case 2:
+ i = ORUBY;
+ j = 40;
+ break;
+ case 3:
+ i = OEMERALD;
+ j = 30;
+ break;
+ default:
+ i = OSAPPHIRE;
+ j = 20;
+ break;
+ };
+ createitem(i, rnd(j) + j / 10);
+}
+
+/*
+ function to change character levels as needed when dropping an object
+ that affects these characteristics
+ */
+void
+adjustcvalues(int theitem, int arg)
+{
+ int flag;
+ flag = 0;
+ switch (theitem) {
+ case ODEXRING:
+ c[DEXTERITY] -= arg + 1;
+ flag = 1;
+ break;
+ case OSTRRING:
+ c[STREXTRA] -= arg + 1;
+ flag = 1;
+ break;
+ case OCLEVERRING:
+ c[INTELLIGENCE] -= arg + 1;
+ flag = 1;
+ break;
+ case OHAMMER:
+ c[DEXTERITY] -= 10;
+ c[STREXTRA] -= 10;
+ c[INTELLIGENCE] += 10;
+ flag = 1;
+ break;
+ case OSWORDofSLASHING:
+ c[DEXTERITY] -= 5;
+ flag = 1;
+ break;
+ case OORBOFDRAGON:
+ --c[SLAYING];
+ return;
+ case OSPIRITSCARAB:
+ --c[NEGATESPIRIT];
+ return;
+ case OCUBEofUNDEAD:
+ --c[CUBEofUNDEAD];
+ return;
+ case ONOTHEFT:
+ --c[NOTHEFT];
+ return;
+ case OLANCE:
+ c[LANCEDEATH] = 0;
+ return;
+ case OPOTION:
+ case OSCROLL:
+ return;
+
+ default:
+ flag = 1;
+ };
+ if (flag)
+ bottomline();
+}
+
+/*
+ function to ask user for a password (no echo)
+ returns 1 if entered correctly, 0 if not
+ */
+static char gpwbuf[33];
+int
+getpassword(void)
+{
+ int i, j;
+ char *gpwp;
+ scbr(); /* system("stty -echo cbreak"); */
+ gpwp = gpwbuf;
+ lprcat("\nEnter Password: ");
+ lflush();
+ i = strlen(password);
+ for (j = 0; j < i; j++)
+ *gpwp++ = ttgetch();
+ gpwbuf[i] = 0;
+ sncbr(); /* system("stty echo -cbreak"); */
+ if (strcmp(gpwbuf, password) != 0) {
+ lprcat("\nSorry\n");
+ lflush();
+ return (0);
+ } else
+ return (1);
+}
+
+/*
+ subroutine to get a yes or no response from the user
+ returns y or n
+ */
+int
+getyn(void)
+{
+ int i;
+ i = 0;
+ while (i != 'y' && i != 'n' && i != '\33')
+ i = ttgetch();
+ return (i);
+}
+
+/*
+ function to calculate the pack weight of the player
+ returns the number of pounds the player is carrying
+ */
+int
+packweight(void)
+{
+ int i, j, k;
+ k = c[GOLD] / 1000;
+ j = 25;
+ while ((iven[j] == 0) && (j > 0))
+ --j;
+ for (i = 0; i <= j; i++)
+ switch (iven[i]) {
+ case 0:
+ break;
+ case OSSPLATE:
+ case OPLATEARMOR:
+ k += 40;
+ break;
+ case OPLATE:
+ k += 35;
+ break;
+ case OHAMMER:
+ k += 30;
+ break;
+ case OSPLINT:
+ k += 26;
+ break;
+ case OSWORDofSLASHING:
+ case OCHAIN:
+ case OBATTLEAXE:
+ case O2SWORD:
+ k += 23;
+ break;
+ case OLONGSWORD:
+ case OSWORD:
+ case ORING:
+ case OFLAIL:
+ k += 20;
+ break;
+ case OLANCE:
+ case OSTUDLEATHER:
+ k += 15;
+ break;
+ case OLEATHER:
+ case OSPEAR:
+ k += 8;
+ break;
+ case OORBOFDRAGON:
+ case OBELT:
+ k += 4;
+ break;
+ case OSHIELD:
+ k += 7;
+ break;
+ case OCHEST:
+ k += 30 + ivenarg[i];
+ break;
+ default:
+ k++;
+ };
+ return (k);
+}
+
+#ifndef MACRORND
+/* macros to generate random numbers 1<=rnd(N)<=N 0<=rund(N)<=N-1 */
+int
+rnd(int x)
+{
+ return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)) + 1);
+}
+
+int
+rund(int x)
+{
+ return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)));
+}
+#endif /* MACRORND */
diff --git a/larn/header.h b/larn/header.h
new file mode 100644
index 0000000..121a472
--- /dev/null
+++ b/larn/header.h
@@ -0,0 +1,444 @@
+/* $NetBSD: header.h,v 1.22 2008/08/29 00:37:38 gmcgarry Exp $ */
+
+/* header.h Larn is copyrighted 1986 by Noah Morgan. */
+
+#include <sys/types.h>
+
+#define MAXLEVEL 11
+/* max # levels in the dungeon */
+#define MAXVLEVEL 3
+/* max # of levels in the temple of the luran */
+#define MAXX 67
+#define MAXY 17
+
+#define SCORESIZE 10
+/* this is the number of people on a scoreboard max */
+#define MAXPLEVEL 100
+/* maximum player level allowed */
+#define MAXMONST 56
+/* maximum # monsters in the dungeon */
+#define SPNUM 38
+/* maximum number of spells in existence */
+#define MAXSCROLL 28
+/* maximum number of scrolls that are possible */
+#define MAXPOTION 35
+/* maximum number of potions that are possible */
+#define TIMELIMIT 30000
+/* the maximum number of moves before the game is called */
+#define TAXRATE 1/20
+/* the tax rate for the LRS */
+#define MAXOBJ 93
+/* the maximum number of objects n < MAXOBJ */
+
+/* this is the structure definition of the monster data */
+struct monst {
+ const char *name;
+ char level;
+ short armorclass;
+ char damage;
+ char attack;
+ char defense;
+ char genocided;
+ char intelligence; /* monsters intelligence -- used to
+ * choose movement */
+ short gold;
+ short hitpoints;
+ unsigned long experience;
+};
+
+/* this is the structure definition for the items in the dnd store */
+struct _itm {
+ short price;
+ u_char obj;
+ u_char arg;
+ char qty;
+};
+
+/* this is the structure that holds the entire dungeon specifications */
+struct cel {
+ short hitp; /* monster's hit points */
+ char mitem; /* the monster ID */
+ char item; /* the object's ID */
+ short iarg; /* the object's argument */
+ char know; /* have we been here before */
+};
+
+/* this is the structure for maintaining & moving the spheres of annihilation */
+struct sphere {
+ struct sphere *p; /* pointer to next structure */
+ char x, y, lev; /* location of the sphere */
+ char dir; /* direction sphere is going in */
+ short lifetime; /* duration of the sphere */
+};
+
+/* defines for the character attribute array c[] */
+#define STRENGTH 0 /* characters physical strength not due to
+ * objects */
+#define INTELLIGENCE 1
+#define WISDOM 2
+#define CONSTITUTION 3
+#define DEXTERITY 4
+#define CHARISMA 5
+#define HPMAX 6
+#define HP 7
+#define GOLD 8
+#define EXPERIENCE 9
+#define LEVEL 10
+#define REGEN 11
+#define WCLASS 12
+#define AC 13
+#define BANKACCOUNT 14
+#define SPELLMAX 15
+#define SPELLS 16
+#define ENERGY 17
+#define ECOUNTER 18
+#define MOREDEFENSES 19
+#define WEAR 20
+#define PROTECTIONTIME 21
+#define WIELD 22
+#define AMULET 23
+#define REGENCOUNTER 24
+#define MOREDAM 25
+#define DEXCOUNT 26
+#define STRCOUNT 27
+#define BLINDCOUNT 28
+#define CAVELEVEL 29
+#define CONFUSE 30
+#define ALTPRO 31
+#define HERO 32
+#define CHARMCOUNT 33
+#define INVISIBILITY 34
+#define CANCELLATION 35
+#define HASTESELF 36
+#define EYEOFLARN 37
+#define AGGRAVATE 38
+#define GLOBE 39
+#define TELEFLAG 40
+#define SLAYING 41
+#define NEGATESPIRIT 42
+#define SCAREMONST 43
+#define AWARENESS 44
+#define HOLDMONST 45
+#define TIMESTOP 46
+#define HASTEMONST 47
+#define CUBEofUNDEAD 48
+#define GIANTSTR 49
+#define FIRERESISTANCE 50
+#define BESSMANN 51
+#define NOTHEFT 52
+#define HARDGAME 53
+#define CPUTIME 54
+#define BYTESIN 55
+#define BYTESOUT 56
+#define MOVESMADE 57
+#define MONSTKILLED 58
+#define SPELLSCAST 59
+#define LANCEDEATH 60
+#define SPIRITPRO 61
+#define UNDEADPRO 62
+#define SHIELD 63
+#define STEALTH 64
+#define ITCHING 65
+#define LAUGHING 66
+#define DRAINSTRENGTH 67
+#define CLUMSINESS 68
+#define INFEEBLEMENT 69
+#define HALFDAM 70
+#define SEEINVISIBLE 71
+#define FILLROOM 72
+#define RANDOMWALK 73
+#define SPHCAST 74 /* nz if an active sphere of annihilation */
+#define WTW 75 /* walk through walls */
+#define STREXTRA 76 /* character strength due to objects or
+ * enchantments */
+#define TMP 77 /* misc scratch space */
+#define LIFEPROT 78 /* life protection counter */
+
+/* defines for the objects in the game */
+
+#define OALTAR 1
+#define OTHRONE 2
+#define OORB 3
+#define OPIT 4
+#define OSTAIRSUP 5
+#define OELEVATORUP 6
+#define OFOUNTAIN 7
+#define OSTATUE 8
+#define OTELEPORTER 9
+#define OSCHOOL 10
+#define OMIRROR 11
+#define ODNDSTORE 12
+#define OSTAIRSDOWN 13
+#define OELEVATORDOWN 14
+#define OBANK2 15
+#define OBANK 16
+#define ODEADFOUNTAIN 17
+#define OMAXGOLD 70
+#define OGOLDPILE 18
+#define OOPENDOOR 19
+#define OCLOSEDDOOR 20
+#define OWALL 21
+#define OTRAPARROW 66
+#define OTRAPARROWIV 67
+
+#define OLARNEYE 22
+
+#define OPLATE 23
+#define OCHAIN 24
+#define OLEATHER 25
+#define ORING 60
+#define OSTUDLEATHER 61
+#define OSPLINT 62
+#define OPLATEARMOR 63
+#define OSSPLATE 64
+#define OSHIELD 68
+#define OELVENCHAIN 92
+
+#define OSWORDofSLASHING 26
+#define OHAMMER 27
+#define OSWORD 28
+#define O2SWORD 29
+#define OSPEAR 30
+#define ODAGGER 31
+#define OBATTLEAXE 57
+#define OLONGSWORD 58
+#define OFLAIL 59
+#define OLANCE 65
+#define OVORPAL 90
+#define OSLAYER 91
+
+#define ORINGOFEXTRA 32
+#define OREGENRING 33
+#define OPROTRING 34
+#define OENERGYRING 35
+#define ODEXRING 36
+#define OSTRRING 37
+#define OCLEVERRING 38
+#define ODAMRING 39
+
+#define OBELT 40
+
+#define OSCROLL 41
+#define OPOTION 42
+#define OBOOK 43
+#define OCHEST 44
+#define OAMULET 45
+
+#define OORBOFDRAGON 46
+#define OSPIRITSCARAB 47
+#define OCUBEofUNDEAD 48
+#define ONOTHEFT 49
+
+#define ODIAMOND 50
+#define ORUBY 51
+#define OEMERALD 52
+#define OSAPPHIRE 53
+
+#define OENTRANCE 54
+#define OVOLDOWN 55
+#define OVOLUP 56
+#define OHOME 69
+
+#define OKGOLD 71
+#define ODGOLD 72
+#define OIVDARTRAP 73
+#define ODARTRAP 74
+#define OTRAPDOOR 75
+#define OIVTRAPDOOR 76
+#define OTRADEPOST 77
+#define OIVTELETRAP 78
+#define ODEADTHRONE 79
+#define OANNIHILATION 80 /* sphere of annihilation */
+#define OTHRONE2 81
+#define OLRS 82 /* Larn Revenue Service */
+#define OCOOKIE 83
+#define OURN 84
+#define OBRASSLAMP 85
+#define OHANDofFEAR 86 /* hand of fear */
+#define OSPHTAILSMAN 87 /* tailsman of the sphere */
+#define OWWAND 88 /* wand of wonder */
+#define OPSTAFF 89 /* staff of power */
+/* used up to 92 */
+
+/* defines for the monsters as objects */
+
+#define BAT 1
+#define GNOME 2
+#define HOBGOBLIN 3
+#define JACKAL 4
+#define KOBOLD 5
+#define ORC 6
+#define SNAKE 7
+#define CENTIPEDE 8
+#define JACULI 9
+#define TROGLODYTE 10
+#define ANT 11
+#define EYE 12
+#define LEPRECHAUN 13
+#define NYMPH 14
+#define QUASIT 15
+#define RUSTMONSTER 16
+#define ZOMBIE 17
+#define ASSASSINBUG 18
+#define BUGBEAR 19
+#define HELLHOUND 20
+#define ICELIZARD 21
+#define CENTAUR 22
+#define TROLL 23
+#define YETI 24
+#define WHITEDRAGON 25
+#define ELF 26
+#define CUBE 27
+#define METAMORPH 28
+#define VORTEX 29
+#define ZILLER 30
+#define VIOLETFUNGI 31
+#define WRAITH 32
+#define FORVALAKA 33
+#define LAMANOBE 34
+#define OSEQUIP 35
+#define ROTHE 36
+#define XORN 37
+#define VAMPIRE 38
+#define INVISIBLESTALKER 39
+#define POLTERGEIST 40
+#define DISENCHANTRESS 41
+#define SHAMBLINGMOUND 42
+#define YELLOWMOLD 43
+#define UMBERHULK 44
+#define GNOMEKING 45
+#define MIMIC 46
+#define WATERLORD 47
+#define BRONZEDRAGON 48
+#define GREENDRAGON 49
+#define PURPLEWORM 50
+#define XVART 51
+#define SPIRITNAGA 52
+#define SILVERDRAGON 53
+#define PLATINUMDRAGON 54
+#define GREENURCHIN 55
+#define REDDRAGON 56
+#define DEMONLORD 57
+#define DEMONPRINCE 64
+
+#ifndef NULL
+#define NULL 0
+#endif
+#define BUFBIG 4096 /* size of the output buffer */
+#define MAXIBUF 4096 /* size of the input buffer */
+#define LOGNAMESIZE 40 /* max size of the players name */
+#define PSNAMESIZE 40 /* max size of the process name */
+
+#ifndef NODEFS
+extern char VERSION, SUBVERSION;
+extern u_char beenhere[], boldon, cheat, ckpflag;
+extern const char *class[];
+extern u_char course[];
+extern char diagfile[], helpfile[], ckpfile[], larnlevels[],
+ playerids[], optsfile[1024], psname[], savefilename[],
+ scorefile[];
+extern u_char *inbuffer;
+extern u_char item[MAXX][MAXY], iven[], know[MAXX][MAXY];
+extern const char *levelname[];
+extern char logfile[], loginname[], logname[], lastmonst[];
+extern u_char *lpbuf, *lpend;
+extern u_char *lpnt, moved[MAXX][MAXY], mitem[MAXX][MAXY], monstlevel[];
+extern char monstnamelist[], objnamelist[];
+extern u_char nch[], ndgg[], nlpts[], nomove, nosignal, nowelcome;
+extern u_char nplt[], nsw[];
+extern const char *objectname[];
+extern const char *potionhide[], *potionname[];
+extern const char *spelcode[], *spelname[], *spelmes[];
+extern char aborted[], spelweird[MAXMONST + 8][SPNUM];
+extern u_char potprob[];
+extern u_char predostuff, restorflag, scprob[];
+extern u_char screen[MAXX][MAXY], sex;
+extern const char *speldescript[];
+extern const char *scrollhide[], *scrollname[];
+extern u_char spelknow[];
+extern u_char splev[], stealth[MAXX][MAXY], wizard;
+extern short diroffx[], diroffy[], hitflag, hit2flag, hit3flag, hitp[MAXX][MAXY];
+extern short iarg[MAXX][MAXY], ivenarg[], lasthx, lasthy, lastnum, lastpx,
+ lastpy;
+extern short nobeep, oldx, oldy, playerx, playery, level;
+extern int enable_scroll, srcount, yrepcount, userid, wisid,
+ io_outfd, io_infd;
+extern gid_t gid, egid;
+extern long outstanding_taxes, skill[], gltime, c[], cbak[];
+extern time_t initialtime;
+extern unsigned long randx;
+extern struct cel *cell;
+extern struct monst monster[];
+extern struct sphere *spheres;
+extern struct _itm itm[];
+extern int rmst, lasttime;
+
+/* macro to create scroll #'s with probability of occurrence */
+#define newscroll() (scprob[rund(81)])
+/* macro to return a potion # created with probability of occurrence */
+#define newpotion() (potprob[rund(41)])
+/* macro to return the + points on created leather armor */
+#define newleather() (nlpts[rund(c[HARDGAME]?13:15)])
+/* macro to return the + points on chain armor */
+#define newchain() (nch[rund(10)])
+/* macro to return + points on plate armor */
+#define newplate() (nplt[rund(c[HARDGAME]?4:12)])
+/* macro to return + points on new daggers */
+#define newdagger() (ndgg[rund(13)])
+/* macro to return + points on new swords */
+#define newsword() (nsw[rund(c[HARDGAME]?6:13)])
+/* macro to destroy object at present location */
+#define forget() (item[playerx][playery]=know[playerx][playery]=0)
+/* macro to wipe out a monster at a location */
+#define disappear(x,y) (mitem[x][y]=know[x][y]=0)
+
+#ifdef VT100
+/* macro to turn on bold display for the terminal */
+#define setbold() (lprcat(boldon?"\33[1m":"\33[7m"))
+/* macro to turn off bold display for the terminal */
+#define resetbold() (lprcat("\33[m"))
+/* macro to setup the scrolling region for the terminal */
+#define setscroll() (lprcat("\33[20;24r"))
+/* macro to clear the scrolling region for the terminal */
+#define resetscroll() (lprcat("\33[;24r"))
+/* macro to clear the screen and home the cursor */
+#define clear() (lprcat("\33[2J\33[f"), cbak[SPELLS]= -50)
+#define cltoeoln() lprcat("\33[K")
+#else /* VT100 */
+/* defines below are for use in the termcap mode only */
+#define ST_START 1
+#define ST_END 2
+#define BOLD 3
+#define END_BOLD 4
+#define CLEAR 5
+#define CL_LINE 6
+#define CL_DOWN 14
+#define CURSOR 15
+/* macro to turn on bold display for the terminal */
+#define setbold() (*lpnt++ = ST_START)
+/* macro to turn off bold display for the terminal */
+#define resetbold() (*lpnt++ = ST_END)
+/* macro to setup the scrolling region for the terminal */
+#define setscroll() enable_scroll=1
+/* macro to clear the scrolling region for the terminal */
+#define resetscroll() enable_scroll=0
+/* macro to clear the screen and home the cursor */
+#define clear() (*lpnt++ =CLEAR, cbak[SPELLS]= -50)
+/* macro to clear to end of line */
+#define cltoeoln() (*lpnt++ = CL_LINE)
+#endif /* VT100 */
+
+/* macro to output one byte to the output buffer */
+#define lprc(ch) ((lpnt>=lpend)?(void)(*lpnt++ = (ch), lflush()):(void)(*lpnt++ = (ch)))
+
+/* macro to seed the random number generator */
+#define seedrand(x) (randx=x)
+#ifdef MACRORND
+/* macros to generate random numbers 1<=rnd(N)<=N 0<=rund(N)<=N-1 */
+#define rnd(x) ((((randx=randx*1103515245+12345)>>7)%(x))+1)
+#define rund(x) ((((randx=randx*1103515245+12345)>>7)%(x)) )
+#endif /* MACRORND */
+/* macros for miscellaneous data conversion */
+#define min(x,y) (((x)>(y))?(y):(x))
+#define max(x,y) (((x)>(y))?(x):(y))
+#endif /* NODEFS */
diff --git a/larn/help.c b/larn/help.c
new file mode 100644
index 0000000..477052c
--- /dev/null
+++ b/larn/help.c
@@ -0,0 +1,129 @@
+/* $NetBSD: help.c,v 1.9 2012/06/19 05:30:43 dholland Exp $ */
+
+/* help.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: help.c,v 1.9 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <unistd.h>
+
+#include "header.h"
+#include "extern.h"
+
+static void retcont(void);
+static int openhelp(void);
+
+/*
+ * help function to display the help info
+ *
+ * format of the .larn.help file
+ *
+ * 1st character of file: # of pages of help available (ascii digit)
+ * page (23 lines) for the introductory message (not counted in above)
+ * pages of help text (23 lines per page)
+ */
+void
+help(void)
+{
+ int i, j;
+#ifndef VT100
+ char tmbuf[128]; /* intermediate translation buffer
+ * when not a VT100 */
+#endif /* VT100 */
+ if ((j = openhelp()) < 0)
+ return; /* open the help file and get # pages */
+ for (i = 0; i < 23; i++)
+ lgetl(); /* skip over intro message */
+ for (; j > 0; j--) {
+ clear();
+ for (i = 0; i < 23; i++)
+#ifdef VT100
+ lprcat(lgetl()); /* print out each line that
+ * we read in */
+#else /* VT100 */
+ {
+ tmcapcnv(tmbuf, lgetl());
+ lprcat(tmbuf);
+ } /* intercept \33's */
+#endif /* VT100 */
+ if (j > 1) {
+ lprcat(" ---- Press ");
+ standout("return");
+ lprcat(" to exit, ");
+ standout("space");
+ lprcat(" for more help ---- ");
+ i = 0;
+ while ((i != ' ') && (i != '\n') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\n') || (i == '\33')) {
+ lrclose();
+ setscroll();
+ drawscreen();
+ return;
+ }
+ }
+ }
+ lrclose();
+ retcont();
+ drawscreen();
+}
+
+/*
+ * function to display the welcome message and background
+ */
+void
+welcome(void)
+{
+ int i;
+#ifndef VT100
+ char tmbuf[128]; /* intermediate translation buffer
+ * when not a VT100 */
+#endif /* VT100 */
+ if (openhelp() < 0)
+ return; /* open the help file */
+ clear();
+ for (i = 0; i < 23; i++)
+#ifdef VT100
+ lprcat(lgetl());/* print out each line that we read in */
+#else /* VT100 */
+ {
+ tmcapcnv(tmbuf, lgetl());
+ lprcat(tmbuf);
+ } /* intercept \33's */
+#endif /* VT100 */
+ lrclose();
+ retcont(); /* press return to continue */
+}
+
+/*
+ * function to say press return to continue and reset scroll when done
+ */
+static void
+retcont(void)
+{
+ cursor(1, 24);
+ lprcat("Press ");
+ standout("return");
+ lprcat(" to continue: ");
+ while (ttgetch() != '\n');
+ setscroll();
+}
+
+/*
+ * routine to open the help file and return the first character - '0'
+ */
+static int
+openhelp(void)
+{
+ if (lopen(helpfile) < 0) {
+ lprintf("Can't open help file \"%s\" ", helpfile);
+ lflush();
+ sleep(4);
+ drawscreen();
+ setscroll();
+ return (-1);
+ }
+ resetscroll();
+ return (lgetc() - '0');
+}
diff --git a/larn/io.c b/larn/io.c
new file mode 100644
index 0000000..245ce8a
--- /dev/null
+++ b/larn/io.c
@@ -0,0 +1,991 @@
+/* $NetBSD: io.c,v 1.27 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * io.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Below are the functions in this file:
+ *
+ * setupvt100() Subroutine to set up terminal in correct mode for game
+ * clearvt100() Subroutine to clean up terminal when the game is over
+ * ttgetch() Routine to read in one character from the terminal
+ * scbr() Function to set cbreak -echo for the terminal
+ * sncbr() Function to set -cbreak echo for the terminal
+ * newgame() Subroutine to save the initial time and seed rnd()
+ *
+ * FILE OUTPUT ROUTINES
+ *
+ * lprintf(format,args . . .) printf to the output buffer lprint(integer)
+ * end binary integer to output buffer lwrite(buf,len)
+ * rite a buffer to the output buffer lprcat(str)
+ * ent string to output buffer
+ *
+ * FILE OUTPUT MACROS (in header.h)
+ *
+ * lprc(character) put the character into the output
+ * buffer
+ *
+ * FILE INPUT ROUTINES
+ *
+ * long lgetc() read one character from input buffer
+ * long larn_lrint() read one integer from input buffer
+ * lrfill(address,number) put input bytes into a buffer char
+ * *lgetw() get a whitespace ended word from
+ * input char *lgetl() get a \n or EOF ended line
+ * from input
+ *
+ * FILE OPEN / CLOSE ROUTINES
+ *
+ * lcreat(filename) create a new file for write
+ * lopen(filename) open a file for read
+ * lappend(filename) open for append to an existing file
+ * lrclose() close the input file
+ * lwclose() close output file lflush()
+ * lush the output buffer
+ *
+ * Other Routines
+ *
+ * cursor(x,y) position cursor at [x,y]
+ * cursors() position cursor at [1,24]
+ * (saves memory) cl_line(x,y) Clear line at [1,y] and leave
+ * cursor at [x,y] cl_up(x,y) Clear screen
+ * from [x,1] to current line. cl_dn(x,y)
+ * lear screen from [1,y] to end of display. standout(str)
+ * rint the string in standout mode. set_score_output()
+ * alled when output should be literally printed. * ttputch(ch)
+ * rint one character in decoded output buffer. * flush_buf()
+ * lush buffer with decoded output. * init_term()
+ * erminal initialization -- setup termcap info * char *tmcapcnv(sd,ss)
+ * outine to convert VT100 \33's to termcap format beep()
+ * e to emit a beep if enabled (see no-beep in .larnopts)
+ *
+ * Note: ** entries are available only in termcap mode.
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: io.c,v 1.27 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <term.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifdef TERMIO
+#include <termio.h>
+#define sgttyb termio
+#define stty(_a,_b) ioctl(_a,TCSETA,_b)
+#define gtty(_a,_b) ioctl(_a,TCGETA,_b)
+#endif
+#ifdef TERMIOS
+#include <termios.h>
+#define sgttyb termios
+#define stty(_a,_b) tcsetattr(_a,TCSADRAIN,_b)
+#define gtty(_a,_b) tcgetattr(_a,_b)
+#endif
+
+#if defined(TERMIO) || defined(TERMIOS)
+static int rawflg = 0;
+static char saveeof, saveeol;
+#define doraw(_a) \
+ if(!rawflg) { \
+ ++rawflg; \
+ saveeof = _a.c_cc[VMIN]; \
+ saveeol = _a.c_cc[VTIME]; \
+ } \
+ _a.c_cc[VMIN] = 1; \
+ _a.c_cc[VTIME] = 1; \
+ _a.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL)
+#define unraw(_a) \
+ _a.c_cc[VMIN] = saveeof; \
+ _a.c_cc[VTIME] = saveeol; \
+ _a.c_lflag |= ICANON|ECHO|ECHOE|ECHOK|ECHONL
+
+#else /* not TERMIO or TERMIOS */
+
+#ifndef BSD
+#define CBREAK RAW /* V7 has no CBREAK */
+#endif
+
+#define doraw(_a) (_a.sg_flags |= CBREAK,_a.sg_flags &= ~ECHO)
+#define unraw(_a) (_a.sg_flags &= ~CBREAK,_a.sg_flags |= ECHO)
+#include <sgtty.h>
+#endif /* not TERMIO or TERMIOS */
+
+#ifndef NOVARARGS /* if we have varargs */
+#include <stdarg.h>
+#else /* NOVARARGS */ /* if we don't have varargs */
+typedef char *va_list;
+#define va_dcl int va_alist;
+#define va_start(plist) plist = (char *) &va_alist
+#define va_end(plist)
+#define va_arg(plist,mode) ((mode *)(plist += sizeof(mode)))[-1]
+#endif /* NOVARARGS */
+
+static int ttputch(int ch);
+static void flush_buf(void);
+
+#define LINBUFSIZE 128 /* size of the lgetw() and lgetl() buffer */
+int io_outfd; /* output file numbers */
+int io_infd; /* input file numbers */
+static struct sgttyb ttx;/* storage for the tty modes */
+static int ipoint = MAXIBUF, iepoint = MAXIBUF; /* input buffering
+ * pointers */
+static char lgetwbuf[LINBUFSIZE]; /* get line (word) buffer */
+
+/*
+ * setupvt100() Subroutine to set up terminal in correct mode for game
+ *
+ * Attributes off, clear screen, set scrolling region, set tty mode
+ */
+void
+setupvt100(void)
+{
+ clear();
+ setscroll();
+ scbr(); /* system("stty cbreak -echo"); */
+}
+
+/*
+ * clearvt100() Subroutine to clean up terminal when the game is over
+ *
+ * Attributes off, clear screen, unset scrolling region, restore tty mode
+ */
+void
+clearvt100(void)
+{
+ resetscroll();
+ clear();
+ sncbr(); /* system("stty -cbreak echo"); */
+}
+
+/*
+ * ttgetch() Routine to read in one character from the terminal
+ */
+int
+ttgetch(void)
+{
+ char byt;
+#ifdef EXTRA
+ c[BYTESIN]++;
+#endif
+ lflush(); /* be sure output buffer is flushed */
+ read(0, &byt, 1); /* get byte from terminal */
+ return (byt);
+}
+
+/*
+ * scbr() Function to set cbreak -echo for the terminal
+ *
+ * like: system("stty cbreak -echo")
+ */
+void
+scbr(void)
+{
+ gtty(0, &ttx);
+ doraw(ttx);
+ stty(0, &ttx);
+}
+
+/*
+ * sncbr() Function to set -cbreak echo for the terminal
+ *
+ * like: system("stty -cbreak echo")
+ */
+void
+sncbr(void)
+{
+ gtty(0, &ttx);
+ unraw(ttx);
+ stty(0, &ttx);
+}
+
+/*
+ * newgame() Subroutine to save the initial time and seed rnd()
+ */
+void
+newgame(void)
+{
+ long *p, *pe;
+ for (p = c, pe = c + 100; p < pe; *p++ = 0);
+ time(&initialtime);
+ seedrand(initialtime);
+ srandom(initialtime);
+ lcreat((char *) 0); /* open buffering for output to terminal */
+}
+
+/*
+ * lprintf(format,args . . .) printf to the output buffer
+ * char *format;
+ * ??? args . . .
+ *
+ * Enter with the format string in "format", as per printf() usage
+ * and any needed arguments following it
+ * Note: lprintf() only supports %s, %c and %d, with width modifier and left
+ * or right justification.
+ * No correct checking for output buffer overflow is done, but flushes
+ * are done beforehand if needed.
+ * Returns nothing of value.
+ */
+void
+lprintf(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFBIG/2];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (lpnt >= lpend)
+ lflush();
+
+ lprcat(buf);
+}
+
+/*
+ * lprint(long-integer) send binary integer to output buffer
+ * long integer;
+ *
+ * +---------+---------+---------+---------+
+ * | high | | | low |
+ * | order | | | order |
+ * | byte | | | byte |
+ * +---------+---------+---------+---------+
+ * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
+ *
+ * The save order is low order first, to high order (4 bytes total)
+ * and is written to be system independent.
+ * No checking for output buffer overflow is done, but flushes if needed!
+ * Returns nothing of value.
+ */
+void
+lprint(long x)
+{
+ if (lpnt >= lpend)
+ lflush();
+ *lpnt++ = 255 & x;
+ *lpnt++ = 255 & (x >> 8);
+ *lpnt++ = 255 & (x >> 16);
+ *lpnt++ = 255 & (x >> 24);
+}
+
+/*
+ * lwrite(buf,len) write a buffer to the output buffer
+ * char *buf;
+ * int len;
+ *
+ * Enter with the address and number of bytes to write out
+ * Returns nothing of value
+ */
+void
+lwrite(char *buf, int len)
+{
+ char *s;
+ u_char *t;
+ int num2;
+
+ if (len > 399) { /* don't copy data if can just write it */
+#ifdef EXTRA
+ c[BYTESOUT] += len;
+#endif
+
+#ifndef VT100
+ for (s = buf; len > 0; --len)
+ lprc(*s++);
+#else /* VT100 */
+ lflush();
+ write(io_outfd, buf, len);
+#endif /* VT100 */
+ } else
+ while (len) {
+ if (lpnt >= lpend)
+ lflush(); /* if buffer is full flush it */
+ num2 = lpbuf + BUFBIG - lpnt; /* # bytes left in
+ * output buffer */
+ if (num2 > len)
+ num2 = len;
+ t = lpnt;
+ len -= num2;
+ while (num2--)
+ *t++ = *buf++; /* copy in the bytes */
+ lpnt = t;
+ }
+}
+
+/*
+ * long lgetc() Read one character from input buffer
+ *
+ * Returns 0 if EOF, otherwise the character
+ */
+long
+lgetc(void)
+{
+ int i;
+ if (ipoint != iepoint)
+ return (inbuffer[ipoint++]);
+ if (iepoint != MAXIBUF)
+ return (0);
+ if ((i = read(io_infd, inbuffer, MAXIBUF)) <= 0) {
+ if (i != 0)
+ write(1, "error reading from input file\n", 30);
+ iepoint = ipoint = 0;
+ return (0);
+ }
+ ipoint = 1;
+ iepoint = i;
+ return (*inbuffer);
+}
+
+/*
+ * long lrint() Read one integer from input buffer
+ *
+ * +---------+---------+---------+---------+
+ * | high | | | low |
+ * | order | | | order |
+ * | byte | | | byte |
+ * +---------+---------+---------+---------+
+ * 31 --- 24 23 --- 16 15 --- 8 7 --- 0
+ *
+ * The save order is low order first, to high order (4 bytes total)
+ * Returns the int read
+ */
+long
+larn_lrint(void)
+{
+ unsigned long i;
+ i = 255 & lgetc();
+ i |= (255 & lgetc()) << 8;
+ i |= (255 & lgetc()) << 16;
+ i |= (255 & lgetc()) << 24;
+ return (i);
+}
+
+/*
+ * lrfill(address,number) put input bytes into a buffer
+ * char *address;
+ * int number;
+ *
+ * Reads "number" bytes into the buffer pointed to by "address".
+ * Returns nothing of value
+ */
+void
+lrfill(char *adr, int num)
+{
+ u_char *pnt;
+ int num2;
+
+ while (num) {
+ if (iepoint == ipoint) {
+ if (num > 5) { /* fast way */
+ if (read(io_infd, adr, num) != num)
+ write(2, "error reading from input file\n", 30);
+ num = 0;
+ } else {
+ *adr++ = lgetc();
+ --num;
+ }
+ } else {
+ num2 = iepoint - ipoint; /* # of bytes left in
+ * the buffer */
+ if (num2 > num)
+ num2 = num;
+ pnt = inbuffer + ipoint;
+ num -= num2;
+ ipoint += num2;
+ while (num2--)
+ *adr++ = *pnt++;
+ }
+ }
+}
+
+/*
+ * char *lgetw() Get a whitespace ended word from input
+ *
+ * Returns pointer to a buffer that contains word. If EOF, returns a NULL
+ */
+char *
+lgetw(void)
+{
+ char *lgp, cc;
+ int n = LINBUFSIZE, quote = 0;
+ lgp = lgetwbuf;
+ do
+ cc = lgetc();
+ while ((cc <= 32) && (cc > '\0')); /* eat whitespace */
+ for (;; --n, cc = lgetc()) {
+ if ((cc == '\0') && (lgp == lgetwbuf))
+ return (NULL); /* EOF */
+ if ((n <= 1) || ((cc <= 32) && (quote == 0))) {
+ *lgp = '\0';
+ return (lgetwbuf);
+ }
+ if (cc != '"')
+ *lgp++ = cc;
+ else
+ quote ^= 1;
+ }
+}
+
+/*
+ * char *lgetl() Function to read in a line ended by newline or EOF
+ *
+ * Returns pointer to a buffer that contains the line. If EOF, returns NULL
+ */
+char *
+lgetl(void)
+{
+ int i = LINBUFSIZE, ch;
+ char *str = lgetwbuf;
+ for (;; --i) {
+ if ((*str++ = ch = lgetc()) == '\0') {
+ if (str == lgetwbuf + 1)
+ return (NULL); /* EOF */
+ ot: *str = '\0';
+ return (lgetwbuf); /* line ended by EOF */
+ }
+ if ((ch == '\n') || (i <= 1))
+ goto ot;/* line ended by \n */
+ }
+}
+
+/*
+ * lcreat(filename) Create a new file for write
+ * char *filename;
+ *
+ * lcreat((char*)0); means to the terminal
+ * Returns -1 if error, otherwise the file descriptor opened.
+ */
+int
+lcreat(char *str)
+{
+ lflush();
+ lpnt = lpbuf;
+ lpend = lpbuf + BUFBIG;
+ if (str == NULL)
+ return (io_outfd = 1);
+ if ((io_outfd = creat(str, 0644)) < 0) {
+ io_outfd = 1;
+ lprintf("error creating file <%s>: %s\n", str,
+ strerror(errno));
+ lflush();
+ return (-1);
+ }
+ return (io_outfd);
+}
+
+/*
+ * lopen(filename) Open a file for read
+ * char *filename;
+ *
+ * lopen(0) means from the terminal
+ * Returns -1 if error, otherwise the file descriptor opened.
+ */
+int
+lopen(char *str)
+{
+ ipoint = iepoint = MAXIBUF;
+ if (str == NULL)
+ return (io_infd = 0);
+ if ((io_infd = open(str, O_RDONLY)) < 0) {
+ lwclose();
+ io_outfd = 1;
+ lpnt = lpbuf;
+ return (-1);
+ }
+ return (io_infd);
+}
+
+/*
+ * lappend(filename) Open for append to an existing file
+ * char *filename;
+ *
+ * lappend(0) means to the terminal
+ * Returns -1 if error, otherwise the file descriptor opened.
+ */
+int
+lappend(char *str)
+{
+ lpnt = lpbuf;
+ lpend = lpbuf + BUFBIG;
+ if (str == NULL)
+ return (io_outfd = 1);
+ if ((io_outfd = open(str, 2)) < 0) {
+ io_outfd = 1;
+ return (-1);
+ }
+ lseek(io_outfd, 0, SEEK_END); /* seek to end of file */
+ return (io_outfd);
+}
+
+/*
+ * lrclose() close the input file
+ *
+ * Returns nothing of value.
+ */
+void
+lrclose(void)
+{
+ if (io_infd > 0) {
+ close(io_infd);
+ io_infd = 0;
+ }
+}
+
+/*
+ * lwclose() close output file flushing if needed
+ *
+ * Returns nothing of value.
+ */
+void
+lwclose(void)
+{
+ lflush();
+ if (io_outfd > 2) {
+ close(io_outfd);
+ io_outfd = 1;
+ }
+}
+
+/*
+ * lprcat(string) append a string to the output buffer
+ * avoids calls to lprintf (time consuming)
+ */
+void
+lprcat(const char *str)
+{
+ u_char *str2;
+ if (lpnt >= lpend)
+ lflush();
+ str2 = lpnt;
+ while ((*str2++ = *str++) != '\0')
+ continue;
+ lpnt = str2 - 1;
+}
+
+#ifdef VT100
+/*
+ * cursor(x,y) Subroutine to set the cursor position
+ *
+ * x and y are the cursor coordinates, and lpbuff is the output buffer where
+ * escape sequence will be placed.
+ */
+static char *y_num[] = {
+"\33[", "\33[", "\33[2", "\33[3", "\33[4", "\33[5", "\33[6",
+"\33[7", "\33[8", "\33[9", "\33[10", "\33[11", "\33[12", "\33[13", "\33[14",
+"\33[15", "\33[16", "\33[17", "\33[18", "\33[19", "\33[20", "\33[21", "\33[22",
+"\33[23", "\33[24"};
+
+static char *x_num[] = {
+"H", "H", ";2H", ";3H", ";4H", ";5H", ";6H", ";7H", ";8H", ";9H",
+";10H", ";11H", ";12H", ";13H", ";14H", ";15H", ";16H", ";17H", ";18H", ";19H",
+";20H", ";21H", ";22H", ";23H", ";24H", ";25H", ";26H", ";27H", ";28H", ";29H",
+";30H", ";31H", ";32H", ";33H", ";34H", ";35H", ";36H", ";37H", ";38H", ";39H",
+";40H", ";41H", ";42H", ";43H", ";44H", ";45H", ";46H", ";47H", ";48H", ";49H",
+";50H", ";51H", ";52H", ";53H", ";54H", ";55H", ";56H", ";57H", ";58H", ";59H",
+";60H", ";61H", ";62H", ";63H", ";64H", ";65H", ";66H", ";67H", ";68H", ";69H",
+";70H", ";71H", ";72H", ";73H", ";74H", ";75H", ";76H", ";77H", ";78H", ";79H",
+";80H"};
+
+void
+cursor(x, y)
+ int x, y;
+{
+ char *p;
+ if (lpnt >= lpend)
+ lflush();
+
+ p = y_num[y]; /* get the string to print */
+ while (*p)
+ *lpnt++ = *p++; /* print the string */
+
+ p = x_num[x]; /* get the string to print */
+ while (*p)
+ *lpnt++ = *p++; /* print the string */
+}
+#else /* VT100 */
+/*
+ * cursor(x,y) Put cursor at specified coordinates staring at [1,1] (termcap)
+ */
+void
+cursor(int x, int y)
+{
+ if (lpnt >= lpend)
+ lflush();
+
+ *lpnt++ = CURSOR;
+ *lpnt++ = x;
+ *lpnt++ = y;
+}
+#endif /* VT100 */
+
+/*
+ * Routine to position cursor at beginning of 24th line
+ */
+void
+cursors(void)
+{
+ cursor(1, 24);
+}
+
+#ifndef VT100
+/*
+ * Warning: ringing the bell is control code 7. Don't use in defines.
+ * Don't change the order of these defines.
+ * Also used in helpfiles. Codes used in helpfiles should be \E[1 to \E[7 with
+ * obvious meanings.
+ */
+
+static char *outbuf = 0; /* translated output buffer */
+/*
+ * init_term() Terminal initialization -- setup termcap info
+ */
+void
+init_term(void)
+{
+ setupterm(NULL, 0, NULL); /* will exit if invalid term */
+ if (!cursor_address) {
+ fprintf(stderr, "term does not have cursor_address.\n");
+ exit(1);
+ }
+ if (!clr_eol) {
+ fprintf(stderr, "term does not have clr_eol.\n");
+ exit(1);
+ }
+ if (!clear_screen) {
+ fprintf(stderr, "term does not have clear_screen.\n");
+ exit(1);
+ }
+ if ((outbuf = malloc(BUFBIG + 16)) == 0) { /* get memory for
+ * decoded output buffer */
+ fprintf(stderr, "Error malloc'ing memory for decoded output buffer\n");
+ died(-285); /* malloc() failure */
+ }
+
+}
+#endif /* VT100 */
+
+/*
+ * cl_line(x,y) Clear the whole line indicated by 'y' and leave cursor at [x,y]
+ */
+void
+cl_line(int x, int y)
+{
+#ifdef VT100
+ cursor(x, y);
+ lprcat("\33[2K");
+#else /* VT100 */
+ cursor(1, y);
+ *lpnt++ = CL_LINE;
+ cursor(x, y);
+#endif /* VT100 */
+}
+
+/*
+ * cl_up(x,y) Clear screen from [x,1] to current position. Leave cursor at [x,y]
+ */
+void
+cl_up(int x, int y)
+{
+#ifdef VT100
+ cursor(x, y);
+ lprcat("\33[1J\33[2K");
+#else /* VT100 */
+ int i;
+ cursor(1, 1);
+ for (i = 1; i <= y; i++) {
+ *lpnt++ = CL_LINE;
+ *lpnt++ = '\n';
+ }
+ cursor(x, y);
+#endif /* VT100 */
+}
+
+/*
+ * cl_dn(x,y) Clear screen from [1,y] to end of display. Leave cursor at [x,y]
+ */
+void
+cl_dn(int x, int y)
+{
+#ifdef VT100
+ cursor(x, y);
+ lprcat("\33[J\33[2K");
+#else /* VT100 */
+ int i;
+ cursor(1, y);
+ if (!clr_eos) {
+ *lpnt++ = CL_LINE;
+ for (i = y; i <= 24; i++) {
+ *lpnt++ = CL_LINE;
+ if (i != 24)
+ *lpnt++ = '\n';
+ }
+ cursor(x, y);
+ } else
+ *lpnt++ = CL_DOWN;
+ cursor(x, y);
+#endif /* VT100 */
+}
+
+/*
+ * standout(str) Print the argument string in inverse video (standout mode).
+ */
+void
+standout(const char *str)
+{
+#ifdef VT100
+ setbold();
+ while (*str)
+ *lpnt++ = *str++;
+ resetbold();
+#else /* VT100 */
+ *lpnt++ = ST_START;
+ while (*str)
+ *lpnt++ = *str++;
+ *lpnt++ = ST_END;
+#endif /* VT100 */
+}
+
+/*
+ * set_score_output() Called when output should be literally printed.
+ */
+void
+set_score_output(void)
+{
+ enable_scroll = -1;
+}
+
+/*
+ * lflush() Flush the output buffer
+ *
+ * Returns nothing of value.
+ * for termcap version: Flush output in output buffer according to output
+ * status as indicated by `enable_scroll'
+ */
+#ifndef VT100
+static int scrline = 18; /* line # for wraparound instead of scrolling
+ * if no DL */
+void
+lflush(void)
+{
+ int lpoint;
+ u_char *str;
+ static int curx = 0;
+ static int cury = 0;
+
+ if ((lpoint = lpnt - lpbuf) > 0) {
+#ifdef EXTRA
+ c[BYTESOUT] += lpoint;
+#endif
+ if (enable_scroll <= -1) {
+ flush_buf();
+ if (write(io_outfd, lpbuf, lpoint) != lpoint)
+ write(2, "error writing to output file\n", 29);
+ lpnt = lpbuf; /* point back to beginning of buffer */
+ return;
+ }
+ for (str = lpbuf; str < lpnt; str++) {
+ if (*str >= 32) {
+ ttputch(*str);
+ curx++;
+ } else
+ switch (*str) {
+ case CLEAR:
+ tputs(clear_screen, 0, ttputch);
+ curx = cury = 0;
+ break;
+
+ case CL_LINE:
+ tputs(clr_eol, 0, ttputch);
+ break;
+
+ case CL_DOWN:
+ tputs(clr_eos, 0, ttputch);
+ break;
+
+ case ST_START:
+ tputs(enter_standout_mode, 0, ttputch);
+ break;
+
+ case ST_END:
+ tputs(exit_standout_mode, 0, ttputch);
+ break;
+
+ case CURSOR:
+ curx = *++str - 1;
+ cury = *++str - 1;
+ tputs(tiparm(cursor_address,
+ cury, curx), 0, ttputch);
+ break;
+
+ case '\n':
+ if ((cury == 23) && enable_scroll) {
+ if (!delete_line ||
+ !insert_line)
+ { /* wraparound or scroll? */
+ if (++scrline > 23)
+ scrline = 19;
+
+ if (++scrline > 23)
+ scrline = 19;
+ tputs(tiparm(
+ cursor_address,
+ scrline, 0),
+ 0, ttputch);
+ tputs(clr_eol, 0,
+ ttputch);
+
+ if (--scrline < 19)
+ scrline = 23;
+ tputs(tiparm(
+ cursor_address,
+ scrline, 0),
+ 0, ttputch);
+ tputs(clr_eol, 0,
+ ttputch);
+ } else {
+ tputs(tiparm(
+ cursor_address,
+ 19, 0),
+ 0, ttputch);
+ tputs(delete_line, 0,
+ ttputch);
+ tputs(tiparm(
+ cursor_address,
+ 23, 0),
+ 0, ttputch);
+ /*
+ * tputs (AL, 0,
+ * ttputch);
+ */
+ }
+ } else {
+ ttputch('\n');
+ cury++;
+ }
+ curx = 0;
+ break;
+
+ default:
+ ttputch(*str);
+ curx++;
+ };
+ }
+ }
+ lpnt = lpbuf;
+ flush_buf(); /* flush real output buffer now */
+}
+#else /* VT100 */
+/*
+ * lflush() flush the output buffer
+ *
+ * Returns nothing of value.
+ */
+void
+lflush()
+{
+ int lpoint;
+ if ((lpoint = lpnt - lpbuf) > 0) {
+#ifdef EXTRA
+ c[BYTESOUT] += lpoint;
+#endif
+ if (write(io_outfd, lpbuf, lpoint) != lpoint)
+ write(2, "error writing to output file\n", 29);
+ }
+ lpnt = lpbuf; /* point back to beginning of buffer */
+}
+#endif /* VT100 */
+
+#ifndef VT100
+static int vindex = 0;
+/*
+ * ttputch(ch) Print one character in decoded output buffer.
+ */
+static int
+ttputch(int ch)
+{
+ outbuf[vindex++] = ch;
+ if (vindex >= BUFBIG)
+ flush_buf();
+ return (0);
+}
+
+/*
+ * flush_buf() Flush buffer with decoded output.
+ */
+static void
+flush_buf(void)
+{
+ if (vindex)
+ write(io_outfd, outbuf, vindex);
+ vindex = 0;
+}
+
+/*
+ * char *tmcapcnv(sd,ss) Routine to convert VT100 escapes to termcap
+ * format
+ * Processes only the \33[#m sequence (converts . files for termcap use
+ */
+char *
+tmcapcnv(char *sd, char *ss)
+{
+ int tmstate = 0; /* 0=normal, 1=\33 2=[ 3=# */
+ char tmdigit = 0; /* the # in \33[#m */
+ while (*ss) {
+ switch (tmstate) {
+ case 0:
+ if (*ss == '\33') {
+ tmstate++;
+ break;
+ }
+ ign: *sd++ = *ss;
+ ign2: tmstate = 0;
+ break;
+ case 1:
+ if (*ss != '[')
+ goto ign;
+ tmstate++;
+ break;
+ case 2:
+ if (isdigit((u_char)*ss)) {
+ tmdigit = *ss - '0';
+ tmstate++;
+ break;
+ }
+ if (*ss == 'm') {
+ *sd++ = ST_END;
+ goto ign2;
+ }
+ goto ign;
+ case 3:
+ if (*ss == 'm') {
+ if (tmdigit)
+ *sd++ = ST_START;
+ else
+ *sd++ = ST_END;
+ goto ign2;
+ }
+ default:
+ goto ign;
+ };
+ ss++;
+ }
+ *sd = 0; /* NULL terminator */
+ return (sd);
+}
+#endif /* VT100 */
+
+/*
+ * beep() Routine to emit a beep if enabled (see no-beep in .larnopts)
+ */
+void
+beep(void)
+{
+ if (!nobeep)
+ *lpnt++ = '\7';
+}
diff --git a/larn/larn.6 b/larn/larn.6
new file mode 100644
index 0000000..7fadc6b
--- /dev/null
+++ b/larn/larn.6
@@ -0,0 +1,157 @@
+.\" $NetBSD: larn.6,v 1.14 2010/04/24 01:13:37 dholland Exp $
+.\"
+.\" Copyright (c) 1990 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.
+.\"
+.\" @(#)larn.6 5.5 (Berkeley) 12/30/93
+.\"
+.Dd April 23, 2010
+.Dt LARN 6
+.Os
+.Sh NAME
+.Nm larn
+.Nd exploring the caverns of Larn
+.Sh SYNOPSIS
+.Nm larn
+.Op Fl chilns
+.\".Op Fl H Ar number
+.Op Fl o Ar optsfile
+.Op Fl ##
+.Op ++
+.Sh DESCRIPTION
+.Nm
+is a fantasy games in which your child has contracted
+a strange disease, and none of your home remedies seem to have any effect.
+You set out to find a remedy in a limited
+amount of time, and to collect gold along the way of course!
+.Pp
+The options are:
+.Pp
+.Bl -tag -width flag
+.It Fl c
+Clear the high scores file.
+.\" .It Fl H
+.\" Set the difficulty (hardness) level.
+.It Fl h
+Show the command line options.
+.It Fl i
+Show the high scores, including inventory data.
+.It Fl l
+Show the log of all games.
+.It Fl n
+Suppress the welcome message and start the game immediately.
+.It Fl o
+.\" Ar optsfile
+Read the specified options file instead of
+.Pa ~/.larnopts .
+.It Fl s
+Show the high scores.
+.It Fl ##
+Set the difficulty (hardness) level.
+.It ++
+Restore game from the checkpoint (auto-save) file.
+.El
+.Sh COMMANDS
+These are the movement commands:
+.Bl -column " print program version" " give present pack weight"
+.It h move to the left H run left . stay here
+.It j move down J run down Z teleport yourself
+.It k move up K run up c cast a spell
+.It l move to the right L run right r read a scroll
+.It y move northwest Y run northwest q quaff a potion
+.It u move northeast U run northeast W wear armor
+.It b move southwest B run southwest T take off armor
+.It n move southeast N run southeast w wield a weapon
+.It ^ identify a trap g give present pack weight P give tax status
+.It d drop an item i inventory your pockets Q quit the game
+.It v print program version S save the game D list all items found
+.It ? this help screen A create diagnostic file e eat something
+.It (wizards only)
+.El
+.Sh OPTIONS FILE
+The file
+.Pa ~/.larnopts
+may be used to set a few options for
+.Nm .
+A sequence of words terminated by whitespace is used to specify options.
+.Pp
+.Bl -tag -width "savefile: xxsave-file-namexx" -compact
+.It Sy Word
+.Sy Meaning
+.Pp
+.It bold-objects
+Select bold display of objects.
+.It inverse-objects
+Select inverse video display of objects.
+.It no-introduction
+Do not display intro message.
+.It enable-checkpointing
+Turn on periodic checkpointing.
+.It no-beep
+Disable beeping of the terminal.
+.It male
+Choose your sex to be a man.
+.It female
+Choose your sex to be a woman.
+.It name: Dq your name
+Choose your playing name.
+.It monster: Dq monst name
+Choose a name for a monster.
+.It savefile: Dq save-file-name
+Define what the savegame filename will be.
+.El
+.Pp
+Your name and monster names must be enclosed in double quotation marks and may
+be up to 34 characters long.
+Longer names are truncated.
+Anything enclosed in quotation marks is considered one word, and must be
+separated from other words by whitespace.
+.Sh SPECIAL NOTES
+When
+.Sy dropping gold ,
+if you type '*' as your amount, all your gold gets dropped.
+In general, typing in '*' means all of what you are interested in.
+This is true when visiting the bank, or when contributing at altars.
+.Pp
+You can get out of the store, trading post, school, or home by hitting
+.Aq Sy esc .
+.Pp
+When casting a spell, if you need a list of spells you can cast, type
+.Ic D
+as the first letter of your spell.
+The available list of spells will be shown,
+after which you may enter the spell code.
+This only works on the 1st letter of the spell you are casting.
+.Sh FILES
+.Bl -tag -width "/var/games/larn.scores" -compact
+.It Pa /var/games/larn.scores
+Score file.
+.It Pa ~/.larnopts
+Options file.
+.El
+.Sh AUTHORS
+.An Noah Morgan
diff --git a/larn/main.c b/larn/main.c
new file mode 100644
index 0000000..4a5af8f
--- /dev/null
+++ b/larn/main.c
@@ -0,0 +1,1341 @@
+/* $NetBSD: main.c,v 1.25 2012/06/19 05:30:43 dholland Exp $ */
+
+/* main.c */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: main.c,v 1.25 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "header.h"
+#include "extern.h"
+
+static void showstr(void);
+static void t_setup(int);
+static void t_endup(int);
+static void showwear(void);
+static void showwield(void);
+static void showread(void);
+static void showeat(void);
+static void showquaff(void);
+static void show1(int, const char *[]);
+static void randmonst(void);
+static void parse(void);
+static void run(int);
+static void wield(void);
+static void ydhi(int);
+static void ycwi(int);
+static void wear(void);
+static void dropobj(void);
+static void readscr(void);
+static void eatcookie(void);
+static void quaff(void);
+static int whatitem(const char *);
+
+static char copyright[] = "\nLarn is copyrighted 1986 by Noah Morgan.\n";
+int srcount = 0; /* line counter for showstr() */
+int dropflag = 0; /* if 1 then don't lookforobject() next round */
+int rmst = 80; /* random monster creation counter */
+int userid; /* the players login user id number */
+gid_t gid, egid; /* used for security */
+u_char nowelcome = 0, nomove = 0; /* if (nomove) then don't
+ * count next iteration as a
+ * move */
+static char viewflag = 0;
+/*
+ * if viewflag then we have done a 99 stay here and don't showcell in the
+ * main loop
+ */
+u_char restorflag = 0; /* 1 means restore has been done */
+static char cmdhelp[] = "\
+Cmd line format: larn [-slicnh] [-o<optsfile>] [-##] [++]\n\
+ -s show the scoreboard\n\
+ -l show the logfile (wizard id only)\n\
+ -i show scoreboard with inventories of dead characters\n\
+ -c create new scoreboard (wizard id only)\n\
+ -n suppress welcome message on starting game\n\
+ -## specify level of difficulty (example: -5)\n\
+ -h print this help text\n\
+ ++ restore game from checkpoint file\n\
+ -o<optsfile> specify .larnopts filename to be used instead of \"~/.larnopts\"\n\
+";
+#ifdef VT100
+/*
+20150211 bkw: assume any term type is vt100-compatible
+static char *termtypes[] = {"vt100", "vt101", "vt102", "vt103", "vt125",
+ "vt131", "vt140", "vt180", "vt220", "vt240", "vt241", "vt320", "vt340",
+"vt341"};
+*/
+#endif /* VT100 */
+/*
+ ************
+ MAIN PROGRAM
+ ************
+ */
+int
+main(int argc, char **argv)
+{
+ int i;
+ int hard;
+ const char *ptr = 0;
+ struct passwd *pwe;
+
+ i = 0;
+ egid = getegid();
+ gid = getgid();
+ setegid(gid); /* give up "games" if we have it */
+ /*
+ * first task is to identify the player
+ */
+#ifndef VT100
+ init_term(); /* setup the terminal (find out what type)
+ * for termcap */
+#endif /* VT100 */
+ /* try to get login name */
+ if (((ptr = getlogin()) == 0) || (*ptr == 0)) {
+ /* can we get it from /etc/passwd? */
+ if ((pwe = getpwuid(getuid())) != NULL)
+ ptr = pwe->pw_name;
+ else if ((ptr = getenv("USER")) == 0)
+ if ((ptr = getenv("LOGNAME")) == 0) {
+ noone: write(2, "Can't find your logname. Who Are You?\n", 39);
+ exit(1);
+ }
+ }
+ if (ptr == 0)
+ goto noone;
+ if (strlen(ptr) == 0)
+ goto noone;
+ /*
+ * second task is to prepare the pathnames the player will need
+ */
+ strcpy(loginname, ptr); /* save loginname of the user for logging
+ * purposes */
+ strcpy(logname, ptr); /* this will be overwritten with the players
+ * name */
+ if ((ptr = getenv("HOME")) == NULL)
+ ptr = ".";
+ strcpy(savefilename, ptr);
+ strcat(savefilename, "/Larn.sav"); /* save file name in home
+ * directory */
+ snprintf(optsfile, sizeof(optsfile), "%s/.larnopts", ptr);
+ /* the .larnopts filename */
+
+ /*
+ * now malloc the memory for the dungeon
+ */
+ cell = (struct cel *) malloc(sizeof(struct cel) * (MAXLEVEL + MAXVLEVEL) * MAXX * MAXY);
+ if (cell == 0)
+ died(-285); /* malloc failure */
+ lpbuf = malloc((5 * BUFBIG) >> 2); /* output buffer */
+ inbuffer = malloc((5 * MAXIBUF) >> 2); /* output buffer */
+ if ((lpbuf == 0) || (inbuffer == 0))
+ died(-285); /* malloc() failure */
+
+ lcreat((char *) 0);
+ newgame(); /* set the initial clock */
+ hard = -1;
+
+#ifdef VT100
+ /*
+ * check terminal type to avoid users who have not vt100 type terminals
+ */
+ /*
+ // 20150211 bkw: assume any term type is vt100-compatible
+ ttype = getenv("TERM");
+ for (j = 1, i = 0; i < sizeof(termtypes) / sizeof(char *); i++)
+ if (strcmp(ttype, termtypes[i]) == 0) {
+ j = 0;
+ break;
+ }
+ if (j) {
+ lprcat("Sorry, Larn needs a VT100 family terminal for all its features.\n");
+ lflush();
+ exit(1);
+ }
+ */
+#endif /* VT100 */
+
+ /*
+ * now make scoreboard if it is not there (don't clear)
+ */
+ if (access(scorefile, 0) == -1) /* not there */
+ makeboard();
+
+ /*
+ * now process the command line arguments
+ */
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-')
+ switch (argv[i][1]) {
+ case 's':
+ showscores();
+ exit(0); /* show scoreboard */
+
+ case 'l': /* show log file */
+ diedlog();
+ exit(0);
+
+ case 'i':
+ showallscores();
+ exit(0); /* show all scoreboard */
+
+ case 'c': /* anyone with password can create
+ * scoreboard */
+ lprcat("Preparing to initialize the scoreboard.\n");
+ if (getpassword() != 0) { /* make new scoreboard */
+ makeboard();
+ lprc('\n');
+ showscores();
+ }
+ exit(0);
+
+ case 'n': /* no welcome msg */
+ nowelcome = 1;
+ argv[i][0] = 0;
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': /* for hardness */
+ sscanf(&argv[i][1], "%d", &hard);
+ break;
+
+ case 'h': /* print out command line arguments */
+ write(1, cmdhelp, sizeof(cmdhelp));
+ exit(0);
+
+ case 'o': /* specify a .larnopts filename */
+ strncpy(optsfile, argv[i] + 2, 127);
+ break;
+
+ default:
+ printf("Unknown option <%s>\n", argv[i]);
+ exit(1);
+ };
+
+ if (argv[i][0] == '+') {
+ clear();
+ restorflag = 1;
+ if (argv[i][1] == '+') {
+ hitflag = 1;
+ restoregame(ckpfile); /* restore checkpointed
+ * game */
+ }
+ i = argc;
+ }
+ }
+
+ readopts(); /* read the options file if there is one */
+
+
+#ifdef UIDSCORE
+ userid = geteuid(); /* obtain the user's effective id number */
+#else /* UIDSCORE */
+ userid = getplid(logname); /* obtain the players id number */
+#endif /* UIDSCORE */
+ if (userid < 0) {
+ write(2, "Can't obtain playerid\n", 22);
+ exit(1);
+ }
+#ifdef HIDEBYLINK
+ /*
+ * this section of code causes the program to look like something else to ps
+ */
+ if (strcmp(psname, argv[0])) { /* if a different process name only */
+ if ((i = access(psname, 1)) < 0) { /* link not there */
+ if (link(argv[0], psname) >= 0) {
+ argv[0] = psname;
+ execv(psname, argv);
+ }
+ } else
+ unlink(psname);
+ }
+ for (i = 1; i < argc; i++) {
+ szero(argv[i]); /* zero the argument to avoid ps snooping */
+ }
+#endif /* HIDEBYLINK */
+
+ if (access(savefilename, 0) == 0) { /* restore game if need to */
+ clear();
+ restorflag = 1;
+ hitflag = 1;
+ restoregame(savefilename); /* restore last game */
+ }
+ sigsetup(); /* trap all needed signals */
+ sethard(hard); /* set up the desired difficulty */
+ setupvt100(); /* setup the terminal special mode */
+ if (c[HP] == 0) { /* create new game */
+ makeplayer(); /* make the character that will play */
+ newcavelevel(0);/* make the dungeon */
+ predostuff = 1; /* tell signals that we are in the welcome
+ * screen */
+ if (nowelcome == 0)
+ welcome(); /* welcome the player to the game */
+ }
+ drawscreen(); /* show the initial dungeon */
+ predostuff = 2; /* tell the trap functions that they must do
+ * a showplayer() from here on */
+#if 0
+ nice(1); /* games should be run niced */
+#endif
+ yrepcount = hit2flag = 0;
+ while (1) {
+ if (dropflag == 0)
+ lookforobject(); /* see if there is an object
+ * here */
+ else
+ dropflag = 0; /* don't show it just dropped an item */
+ if (hitflag == 0) {
+ if (c[HASTEMONST])
+ movemonst();
+ movemonst();
+ } /* move the monsters */
+ if (viewflag == 0)
+ showcell(playerx, playery);
+ else
+ viewflag = 0; /* show stuff around player */
+ if (hit3flag)
+ flushall();
+ hitflag = hit3flag = 0;
+ nomove = 1;
+ bot_linex(); /* update bottom line */
+ while (nomove) {
+ if (hit3flag)
+ flushall();
+ nomove = 0;
+ parse();
+ } /* get commands and make moves */
+ regen(); /* regenerate hp and spells */
+ if (c[TIMESTOP] == 0)
+ if (--rmst <= 0) {
+ rmst = 120 - (level << 2);
+ fillmonst(makemonst(level));
+ }
+ }
+}
+
+
+/*
+ showstr()
+
+ show character's inventory
+ */
+static void
+showstr(void)
+{
+ int i, number;
+ for (number = 3, i = 0; i < 26; i++)
+ if (iven[i])
+ number++; /* count items in inventory */
+ t_setup(number);
+ qshowstr();
+ t_endup(number);
+}
+
+void
+qshowstr(void)
+{
+ int i, j, k, sigsav;
+ srcount = 0;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ if (c[GOLD]) {
+ lprintf(".) %ld gold pieces", (long) c[GOLD]);
+ srcount++;
+ }
+ for (k = 26; k >= 0; k--)
+ if (iven[k]) {
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= k; j++)
+ if (i == iven[j])
+ show3(j);
+ k = 0;
+ }
+ lprintf("\nElapsed time is %ld. You have %ld mobuls left", (long) ((gltime + 99) / 100 + 1), (long) ((TIMELIMIT - gltime) / 100));
+ more();
+ nosignal = sigsav;
+}
+
+/*
+ * subroutine to clear screen depending on # lines to display
+ */
+static void
+t_setup(int count)
+{
+ if (count < 20) { /* how do we clear the screen? */
+ cl_up(79, count);
+ cursor(1, 1);
+ } else {
+ resetscroll();
+ clear();
+ }
+}
+
+/*
+ * subroutine to restore normal display screen depending on t_setup()
+ */
+static void
+t_endup(int count)
+{
+ if (count < 18) /* how did we clear the screen? */
+ draws(0, MAXX, 0, (count > MAXY) ? MAXY : count);
+ else {
+ drawscreen();
+ setscroll();
+ }
+}
+
+/*
+ function to show the things player is wearing only
+ */
+static void
+showwear(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++) /* count number of items we
+ * will display */
+ if ((i = iven[j]) != 0)
+ switch (i) {
+ case OLEATHER:
+ case OPLATE:
+ case OCHAIN:
+ case ORING:
+ case OSTUDLEATHER:
+ case OSPLINT:
+ case OPLATEARMOR:
+ case OSSPLATE:
+ case OSHIELD:
+ count++;
+ };
+
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OLEATHER:
+ case OPLATE:
+ case OCHAIN:
+ case ORING:
+ case OSTUDLEATHER:
+ case OSPLINT:
+ case OPLATEARMOR:
+ case OSSPLATE:
+ case OSHIELD:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ function to show the things player can wield only
+ */
+static void
+showwield(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++) /* count how many items */
+ if ((i = iven[j]) != 0)
+ switch (i) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ case OBOOK:
+ case OCHEST:
+ case OLARNEYE:
+ case ONOTHEFT:
+ case OSPIRITSCARAB:
+ case OCUBEofUNDEAD:
+ case OPOTION:
+ case OSCROLL:
+ break;
+ default:
+ count++;
+ };
+
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ case OBOOK:
+ case OCHEST:
+ case OLARNEYE:
+ case ONOTHEFT:
+ case OSPIRITSCARAB:
+ case OCUBEofUNDEAD:
+ case OPOTION:
+ case OSCROLL:
+ break;
+ default:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ * function to show the things player can read only
+ */
+static void
+showread(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++)
+ switch (iven[j]) {
+ case OBOOK:
+ case OSCROLL:
+ count++;
+ };
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OBOOK:
+ case OSCROLL:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ * function to show the things player can eat only
+ */
+static void
+showeat(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++)
+ switch (iven[j]) {
+ case OCOOKIE:
+ count++;
+ };
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OCOOKIE:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+/*
+ function to show the things player can quaff only
+ */
+static void
+showquaff(void)
+{
+ int i, j, sigsav, count;
+ sigsav = nosignal;
+ nosignal = 1; /* don't allow ^c etc */
+ srcount = 0;
+
+ for (count = 2, j = 0; j <= 26; j++)
+ switch (iven[j]) {
+ case OPOTION:
+ count++;
+ };
+ t_setup(count);
+
+ for (i = 22; i < 84; i++)
+ for (j = 0; j <= 26; j++)
+ if (i == iven[j])
+ switch (i) {
+ case OPOTION:
+ show3(j);
+ };
+ more();
+ nosignal = sigsav;
+ t_endup(count);
+}
+
+static void
+show1(int idx, const char *str2[])
+{
+ lprintf("\n%c) %s", idx + 'a', objectname[iven[idx]]);
+ if (str2 != 0 && str2[ivenarg[idx]][0] != 0)
+ lprintf(" of%s", str2[ivenarg[idx]]);
+}
+
+void
+show3(int indx)
+{
+ switch (iven[indx]) {
+ case OPOTION:
+ show1(indx, potionname);
+ break;
+ case OSCROLL:
+ show1(indx, scrollname);
+ break;
+
+ case OLARNEYE:
+ case OBOOK:
+ case OSPIRITSCARAB:
+ case ODIAMOND:
+ case ORUBY:
+ case OCUBEofUNDEAD:
+ case OEMERALD:
+ case OCHEST:
+ case OCOOKIE:
+ case OSAPPHIRE:
+ case ONOTHEFT:
+ show1(indx, NULL);
+ break;
+
+ default:
+ lprintf("\n%c) %s", indx + 'a', objectname[iven[indx]]);
+ if (ivenarg[indx] > 0)
+ lprintf(" + %ld", (long) ivenarg[indx]);
+ else if (ivenarg[indx] < 0)
+ lprintf(" %ld", (long) ivenarg[indx]);
+ break;
+ }
+ if (c[WIELD] == indx)
+ lprcat(" (weapon in hand)");
+ if ((c[WEAR] == indx) || (c[SHIELD] == indx))
+ lprcat(" (being worn)");
+ if (++srcount >= 22) {
+ srcount = 0;
+ more();
+ clear();
+ }
+}
+
+/*
+ subroutine to randomly create monsters if needed
+ */
+static void
+randmonst(void)
+{
+ if (c[TIMESTOP])
+ return; /* don't make monsters if time is stopped */
+ if (--rmst <= 0) {
+ rmst = 120 - (level << 2);
+ fillmonst(makemonst(level));
+ }
+}
+
+
+
+/*
+ parse()
+
+ get and execute a command
+ */
+static void
+parse(void)
+{
+ int i, j, k, flag;
+ while (1) {
+ k = yylex();
+ switch (k) { /* get the token from the input and switch on
+ * it */
+ case 'h':
+ moveplayer(4);
+ return; /* west */
+ case 'H':
+ run(4);
+ return; /* west */
+ case 'l':
+ moveplayer(2);
+ return; /* east */
+ case 'L':
+ run(2);
+ return; /* east */
+ case 'j':
+ moveplayer(1);
+ return; /* south */
+ case 'J':
+ run(1);
+ return; /* south */
+ case 'k':
+ moveplayer(3);
+ return; /* north */
+ case 'K':
+ run(3);
+ return; /* north */
+ case 'u':
+ moveplayer(5);
+ return; /* northeast */
+ case 'U':
+ run(5);
+ return; /* northeast */
+ case 'y':
+ moveplayer(6);
+ return; /* northwest */
+ case 'Y':
+ run(6);
+ return; /* northwest */
+ case 'n':
+ moveplayer(7);
+ return; /* southeast */
+ case 'N':
+ run(7);
+ return; /* southeast */
+ case 'b':
+ moveplayer(8);
+ return; /* southwest */
+ case 'B':
+ run(8);
+ return; /* southwest */
+
+ case '.':
+ if (yrepcount)
+ viewflag = 1;
+ return; /* stay here */
+
+ case 'w':
+ yrepcount = 0;
+ wield();
+ return; /* wield a weapon */
+
+ case 'W':
+ yrepcount = 0;
+ wear();
+ return; /* wear armor */
+
+ case 'r':
+ yrepcount = 0;
+ if (c[BLINDCOUNT]) {
+ cursors();
+ lprcat("\nYou can't read anything when you're blind!");
+ } else if (c[TIMESTOP] == 0)
+ readscr();
+ return; /* to read a scroll */
+
+ case 'q':
+ yrepcount = 0;
+ if (c[TIMESTOP] == 0)
+ quaff();
+ return; /* quaff a potion */
+
+ case 'd':
+ yrepcount = 0;
+ if (c[TIMESTOP] == 0)
+ dropobj();
+ return; /* to drop an object */
+
+ case 'c':
+ yrepcount = 0;
+ cast();
+ return; /* cast a spell */
+
+ case 'i':
+ yrepcount = 0;
+ nomove = 1;
+ showstr();
+ return; /* status */
+
+ case 'e':
+ yrepcount = 0;
+ if (c[TIMESTOP] == 0)
+ eatcookie();
+ return; /* to eat a fortune cookie */
+
+ case 'D':
+ yrepcount = 0;
+ seemagic(0);
+ nomove = 1;
+ return; /* list spells and scrolls */
+
+ case '?':
+ yrepcount = 0;
+ help();
+ nomove = 1;
+ return; /* give the help screen */
+
+ case 'S':
+ clear();
+ lprcat("Saving . . .");
+ lflush();
+ savegame(savefilename);
+ wizard = 1;
+ died(-257); /* save the game - doesn't return */
+
+ case 'Z':
+ yrepcount = 0;
+ if (c[LEVEL] > 9) {
+ oteleport(1);
+ return;
+ }
+ cursors();
+ lprcat("\nAs yet, you don't have enough experience to use teleportation");
+ return; /* teleport yourself */
+
+ case '^': /* identify traps */
+ flag = yrepcount = 0;
+ cursors();
+ lprc('\n');
+ for (j = playery - 1; j < playery + 2; j++) {
+ if (j < 0)
+ j = 0;
+ if (j >= MAXY)
+ break;
+ for (i = playerx - 1; i < playerx + 2; i++) {
+ if (i < 0)
+ i = 0;
+ if (i >= MAXX)
+ break;
+ switch (item[i][j]) {
+ case OTRAPDOOR:
+ case ODARTRAP:
+ case OTRAPARROW:
+ case OTELEPORTER:
+ lprcat("\nIt's ");
+ lprcat(objectname[item[i][j]]);
+ flag++;
+ };
+ }
+ }
+ if (flag == 0)
+ lprcat("\nNo traps are visible");
+ return;
+
+#if WIZID
+ case '_': /* this is the fudge player password for
+ * wizard mode */
+ yrepcount = 0;
+ cursors();
+ nomove = 1;
+ if (userid != wisid) {
+ lprcat("Sorry, you are not empowered to be a wizard.\n");
+ scbr(); /* system("stty -echo cbreak"); */
+ lflush();
+ return;
+ }
+ if (getpassword() == 0) {
+ scbr(); /* system("stty -echo cbreak"); */
+ return;
+ }
+ wizard = 1;
+ scbr(); /* system("stty -echo cbreak"); */
+ for (i = 0; i < 6; i++)
+ c[i] = 70;
+ iven[0] = iven[1] = 0;
+ take(OPROTRING, 50);
+ take(OLANCE, 25);
+ c[WIELD] = 1;
+ c[LANCEDEATH] = 1;
+ c[WEAR] = c[SHIELD] = -1;
+ raiseexperience(6000000L);
+ c[AWARENESS] += 25000;
+ {
+ int i, j;
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = 1;
+ for (i = 0; i < SPNUM; i++)
+ spelknow[i] = 1;
+ for (i = 0; i < MAXSCROLL; i++)
+ scrollname[i] = scrollhide[i];
+ for (i = 0; i < MAXPOTION; i++)
+ potionname[i] = potionhide[i];
+ }
+ for (i = 0; i < MAXSCROLL; i++)
+ if (strlen(scrollname[i]) > 2) { /* no null items */
+ item[i][0] = OSCROLL;
+ iarg[i][0] = i;
+ }
+ for (i = MAXX - 1; i > MAXX - 1 - MAXPOTION; i--)
+ if (strlen(potionname[i - MAXX + MAXPOTION]) > 2) { /* no null items */
+ item[i][0] = OPOTION;
+ iarg[i][0] = i - MAXX + MAXPOTION;
+ }
+ for (i = 1; i < MAXY; i++) {
+ item[0][i] = i;
+ iarg[0][i] = 0;
+ }
+ for (i = MAXY; i < MAXY + MAXX; i++) {
+ item[i - MAXY][MAXY - 1] = i;
+ iarg[i - MAXY][MAXY - 1] = 0;
+ }
+ for (i = MAXX + MAXY; i < MAXX + MAXY + MAXY; i++) {
+ item[MAXX - 1][i - MAXX - MAXY] = i;
+ iarg[MAXX - 1][i - MAXX - MAXY] = 0;
+ }
+ c[GOLD] += 25000;
+ drawscreen();
+ return;
+#endif
+
+ case 'T':
+ yrepcount = 0;
+ cursors();
+ if (c[SHIELD] != -1) {
+ c[SHIELD] = -1;
+ lprcat("\nYour shield is off");
+ bottomline();
+ } else if (c[WEAR] != -1) {
+ c[WEAR] = -1;
+ lprcat("\nYour armor is off");
+ bottomline();
+ } else
+ lprcat("\nYou aren't wearing anything");
+ return;
+
+ case 'g':
+ cursors();
+ lprintf("\nThe stuff you are carrying presently weighs %ld pounds", (long) packweight());
+ case ' ':
+ yrepcount = 0;
+ nomove = 1;
+ return;
+
+ case 'v':
+ yrepcount = 0;
+ cursors();
+ lprintf("\nCaverns of Larn, Version %ld.%ld, Diff=%ld",
+ (long) VERSION, (long) SUBVERSION,
+ (long) c[HARDGAME]);
+ if (wizard)
+ lprcat(" Wizard");
+ nomove = 1;
+ if (cheat)
+ lprcat(" Cheater");
+ lprcat(copyright);
+ return;
+
+ case 'Q':
+ yrepcount = 0;
+ quit();
+ nomove = 1;
+ return; /* quit */
+
+ case 'L' - 64:
+ yrepcount = 0;
+ drawscreen();
+ nomove = 1;
+ return; /* look */
+
+#if WIZID
+#ifdef EXTRA
+ case 'A':
+ yrepcount = 0;
+ nomove = 1;
+ if (wizard) {
+ diag();
+ return;
+ } /* create diagnostic file */
+ return;
+#endif
+#endif
+ case 'P':
+ cursors();
+ if (outstanding_taxes > 0)
+ lprintf("\nYou presently owe %ld gp in taxes.",
+ (long) outstanding_taxes);
+ else
+ lprcat("\nYou do not owe any taxes.");
+ return;
+ };
+ }
+}
+
+void
+parse2(void)
+{
+ if (c[HASTEMONST])
+ movemonst();
+ movemonst(); /* move the monsters */
+ randmonst();
+ regen();
+}
+
+static void
+run(int dir)
+{
+ int i;
+ i = 1;
+ while (i) {
+ i = moveplayer(dir);
+ if (i > 0) {
+ if (c[HASTEMONST])
+ movemonst();
+ movemonst();
+ randmonst();
+ regen();
+ }
+ if (hitflag)
+ i = 0;
+ if (i != 0)
+ showcell(playerx, playery);
+ }
+}
+
+/*
+ function to wield a weapon
+ */
+static void
+wield(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("wield")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showwield();
+ else if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ } else if (iven[i - 'a'] == OPOTION) {
+ ycwi(i);
+ return;
+ } else if (iven[i - 'a'] == OSCROLL) {
+ ycwi(i);
+ return;
+ } else if ((c[SHIELD] != -1) && (iven[i - 'a'] == O2SWORD)) {
+ lprcat("\nBut one arm is busy with your shield!");
+ return;
+ } else {
+ c[WIELD] = i - 'a';
+ if (iven[i - 'a'] == OLANCE)
+ c[LANCEDEATH] = 1;
+ else
+ c[LANCEDEATH] = 0;
+ bottomline();
+ return;
+ }
+ }
+ }
+}
+
+/*
+ common routine to say you don't have an item
+ */
+static void
+ydhi(int x)
+{
+ cursors();
+ lprintf("\nYou don't have item %c!", x);
+}
+static void
+ycwi(int x)
+{
+ cursors();
+ lprintf("\nYou can't wield item %c!", x);
+}
+
+/*
+ function to wear armor
+ */
+static void
+wear(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("wear")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showwear();
+ else
+ switch (iven[i - 'a']) {
+ case 0:
+ ydhi(i);
+ return;
+ case OLEATHER:
+ case OCHAIN:
+ case OPLATE:
+ case OSTUDLEATHER:
+ case ORING:
+ case OSPLINT:
+ case OPLATEARMOR:
+ case OSSPLATE:
+ if (c[WEAR] != -1) {
+ lprcat("\nYou're already wearing some armor");
+ return;
+ }
+ c[WEAR] = i - 'a';
+ bottomline();
+ return;
+ case OSHIELD:
+ if (c[SHIELD] != -1) {
+ lprcat("\nYou are already wearing a shield");
+ return;
+ }
+ if (iven[c[WIELD]] == O2SWORD) {
+ lprcat("\nYour hands are busy with the two handed sword!");
+ return;
+ }
+ c[SHIELD] = i - 'a';
+ bottomline();
+ return;
+ default:
+ lprcat("\nYou can't wear that!");
+ };
+ }
+ }
+}
+
+/*
+ function to drop an object
+ */
+static void
+dropobj(void)
+{
+ int i;
+ unsigned char *p;
+ long amt;
+ p = &item[playerx][playery];
+ while (1) {
+ if ((i = whatitem("drop")) == '\33')
+ return;
+ if (i == '*')
+ showstr();
+ else {
+ if (i == '.') { /* drop some gold */
+ if (*p) {
+ lprcat("\nThere's something here already!");
+ return;
+ }
+ lprcat("\n\n");
+ cl_dn(1, 23);
+ lprcat("How much gold do you drop? ");
+ if ((amt = readnum((long) c[GOLD])) == 0)
+ return;
+ if (amt > c[GOLD]) {
+ lprcat("\nYou don't have that much!");
+ return;
+ }
+ if (amt <= 32767) {
+ *p = OGOLDPILE;
+ i = amt;
+ } else if (amt <= 327670L) {
+ *p = ODGOLD;
+ i = amt / 10;
+ amt = 10 * i;
+ } else if (amt <= 3276700L) {
+ *p = OMAXGOLD;
+ i = amt / 100;
+ amt = 100 * i;
+ } else if (amt <= 32767000L) {
+ *p = OKGOLD;
+ i = amt / 1000;
+ amt = 1000 * i;
+ } else {
+ *p = OKGOLD;
+ i = 32767;
+ amt = 32767000L;
+ }
+ c[GOLD] -= amt;
+ lprintf("You drop %ld gold pieces", (long)amt);
+ iarg[playerx][playery] = i;
+ bottomgold();
+ know[playerx][playery] = 0;
+ dropflag = 1;
+ return;
+ }
+ drop_object(i - 'a');
+ return;
+ }
+ }
+}
+
+/*
+ * readscr() Subroutine to read a scroll one is carrying
+ */
+static void
+readscr(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("read")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showread();
+ else {
+ if (iven[i - 'a'] == OSCROLL) {
+ read_scroll(ivenarg[i - 'a']);
+ iven[i - 'a'] = 0;
+ return;
+ }
+ if (iven[i - 'a'] == OBOOK) {
+ readbook(ivenarg[i - 'a']);
+ iven[i - 'a'] = 0;
+ return;
+ }
+ if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ }
+ lprcat("\nThere's nothing on it to read");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * subroutine to eat a cookie one is carrying
+ */
+static void
+eatcookie(void)
+{
+ const char *p;
+ int i;
+
+ while (1) {
+ if ((i = whatitem("eat")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showeat();
+ else {
+ if (iven[i - 'a'] == OCOOKIE) {
+ lprcat("\nThe cookie was delicious.");
+ iven[i - 'a'] = 0;
+ if (!c[BLINDCOUNT]) {
+ if ((p = fortune()) != NULL) {
+ lprcat(" Inside you find a scrap of paper that says:\n");
+ lprcat(p);
+ }
+ }
+ return;
+ }
+ if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ }
+ lprcat("\nYou can't eat that!");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * subroutine to quaff a potion one is carrying
+ */
+static void
+quaff(void)
+{
+ int i;
+ while (1) {
+ if ((i = whatitem("quaff")) == '\33')
+ return;
+ if (i != '.') {
+ if (i == '*')
+ showquaff();
+ else {
+ if (iven[i - 'a'] == OPOTION) {
+ quaffpotion(ivenarg[i - 'a']);
+ iven[i - 'a'] = 0;
+ return;
+ }
+ if (iven[i - 'a'] == 0) {
+ ydhi(i);
+ return;
+ }
+ lprcat("\nYou wouldn't want to quaff that, would you? ");
+ return;
+ }
+ }
+ }
+}
+
+/*
+ function to ask what player wants to do
+ */
+static int
+whatitem(const char *str)
+{
+ int i;
+ cursors();
+ lprintf("\nWhat do you want to %s [* for all] ? ", str);
+ i = 0;
+ while (i > 'z' || (i < 'a' && i != '*' && i != '\33' && i != '.'))
+ i = ttgetch();
+ if (i == '\33')
+ lprcat(" aborted");
+ return (i);
+}
+
+/*
+ subroutine to get a number from the player
+ and allow * to mean return amt, else return the number entered
+ */
+unsigned long
+readnum(long mx)
+{
+ int i;
+ unsigned long amt = 0;
+ sncbr();
+ if ((i = ttgetch()) == '*')
+ amt = mx; /* allow him to say * for all gold */
+ else
+ while (i != '\n') {
+ if (i == '\033') {
+ scbr();
+ lprcat(" aborted");
+ return (0);
+ }
+ if ((i <= '9') && (i >= '0') && (amt < 99999999))
+ amt = amt * 10 + i - '0';
+ i = ttgetch();
+ }
+ scbr();
+ return (amt);
+}
+
+#ifdef HIDEBYLINK
+/*
+ * routine to zero every byte in a string
+ */
+void
+szero(str)
+ char *str;
+{
+ while (*str)
+ *str++ = 0;
+}
+#endif /* HIDEBYLINK */
diff --git a/larn/monster.c b/larn/monster.c
new file mode 100644
index 0000000..35a329e
--- /dev/null
+++ b/larn/monster.c
@@ -0,0 +1,1911 @@
+/* $NetBSD: monster.c,v 1.18 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * monster.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * This file contains the following functions:
+ * ----------------------------------------------------------------------------
+ *
+ * createmonster(monstno) Function to create a monster next to the player
+ * int monstno;
+ *
+ * int cgood(x,y,itm,monst)Function to check location for emptiness
+ * int x,y,itm,monst;
+ *
+ * createitem(it,arg) Routine to place an item next to the player
+ * int it,arg;
+ *
+ * cast() Subroutine called by parse to cast a spell for the user
+ *
+ * speldamage(x) Function to perform spell functions cast by the player
+ * int x;
+ *
+ * loseint() Routine to decrement your int (intelligence) if > 3
+ *
+ * isconfuse() Routine to check to see if player is confused
+ *
+ * nospell(x,monst)Routine to return 1 if a spell doesn't affect a monster
+ * int x,monst;
+ *
+ * fullhit(xx) Function to return full damage against a monst (aka web)
+ * int xx;
+ *
+ * direct(spnum,dam,str,arg)Routine to direct spell damage 1 square in 1 dir
+ * int spnum,dam,arg;
+ * char *str;
+ *
+ * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
+ * int spnum,dam,delay;
+ * char *str,cshow;
+ *
+ * ifblind(x,y)Routine to put "monster" or the monster name into lastmosnt
+ * int x,y;
+ *
+ * tdirect(spnum) Routine to teleport away a monster
+ * int spnum;
+ *
+ * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
+ * int sp,dam;
+ * char *str;
+ *
+ * dirsub(x,y) Routine to ask for direction, then modify x,y for it
+ * int *x,*y;
+ *
+ * vxy(x,y) Routine to verify/fix (*x,*y) for being within bounds
+ * int *x,*y;
+ *
+ * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
+ * int spnum;
+ *
+ * hitmonster(x,y) Function to hit a monster at the designated coordinates
+ * int x,y;
+ *
+ * hitm(x,y,amt) Function to just hit a monster at a given coordinates
+ * int x,y,amt;
+ *
+ * hitplayer(x,y) Function for the monster to hit the player from (x,y)
+ * int x,y;
+ *
+ * dropsomething(monst) Function to create an object when a monster dies
+ * int monst;
+ *
+ * dropgold(amount) Function to drop some gold around player
+ * int amount;
+ *
+ * something(level) Function to create a random item around player
+ * int level;
+ *
+ * newobject(lev,i) Routine to return a randomly selected new object
+ * int lev,*i;
+ *
+ * spattack(atckno,xx,yy) Function to process special attacks from monsters
+ * int atckno,xx,yy;
+ *
+ * checkloss(x) Routine to subtract hp from user and flag bottomline display
+ * int x;
+ *
+ * annihilate() Routine to annihilate monsters around player, playerx,playery
+ *
+ * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
+ * int x,y,dir,lifetime;
+ *
+ * rmsphere(x,y) Function to delete a sphere of annihilation from list
+ * int x,y;
+ *
+ * sphboom(x,y) Function to perform the effects of a sphere detonation
+ * int x,y;
+ *
+ * genmonst() Function to ask for monster and genocide from game
+ *
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: monster.c,v 1.18 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "header.h"
+#include "extern.h"
+
+struct isave { /* used for altar reality */
+ char type; /* 0=item, 1=monster */
+ char id; /* item number or monster number */
+ short arg; /* the type of item or hitpoints of monster */
+};
+
+static int cgood(int, int, int, int);
+static void speldamage(int);
+static void loseint(void);
+static int isconfuse(void);
+static int nospell(int, int);
+static int fullhit(int);
+static void direct(int, int, const char *, int);
+static void ifblind(int, int);
+static void tdirect(int);
+static void omnidirect(int, int, const char *);
+static int dirsub(int *, int *);
+static void dirpoly(int);
+static int hitm(int, int, int);
+static void dropsomething(int);
+static int spattack(int, int, int);
+static void sphboom(int, int);
+static void genmonst(void);
+
+/*
+ * createmonster(monstno) Function to create a monster next to the player
+ * int monstno;
+ *
+ * Enter with the monster number (1 to MAXMONST+8)
+ * Returns no value.
+ */
+void
+createmonster(int mon)
+{
+ int x, y, k, i;
+ if (mon < 1 || mon > MAXMONST + 8) { /* check for monster number
+ * out of bounds */
+ beep();
+ lprintf("\ncan't createmonst(%ld)\n", (long) mon);
+ nap(3000);
+ return;
+ }
+ while (monster[mon].genocided && mon < MAXMONST)
+ mon++; /* genocided? */
+ for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction,
+ * then try all */
+ if (k > 8)
+ k = 1; /* wraparound the diroff arrays */
+ x = playerx + diroffx[k];
+ y = playery + diroffy[k];
+ if (cgood(x, y, 0, 1)) { /* if we can create here */
+ mitem[x][y] = mon;
+ hitp[x][y] = monster[mon].hitpoints;
+ stealth[x][y] = know[x][y] = 0;
+ switch (mon) {
+ case ROTHE:
+ case POLTERGEIST:
+ case VAMPIRE:
+ stealth[x][y] = 1;
+ };
+ return;
+ }
+ }
+}
+
+/*
+ * int cgood(x,y,itm,monst) Function to check location for emptiness
+ * int x,y,itm,monst;
+ *
+ * Routine to return TRUE if a location does not have itm or monst there
+ * returns FALSE (0) otherwise
+ * Enter with itm or monst TRUE or FALSE if checking it
+ * Example: if itm==TRUE check for no item at this location
+ * if monst==TRUE check for no monster at this location
+ * This routine will return FALSE if at a wall or the dungeon exit on level 1
+ */
+static int
+cgood(int x, int y, int theitem, int monst)
+{
+#define itm __lose
+ if ((y >= 0) && (y <= MAXY - 1) && (x >= 0) && (x <= MAXX - 1))
+ /* within bounds? */
+ if (item[x][y] != OWALL) /* can't make anything on walls */
+ /* is it free of items? */
+ if (theitem == 0 || (item[x][y] == 0))
+ /* is it free of monsters? */
+ if (monst == 0 || (mitem[x][y] == 0))
+ if ((level != 1) || (x != 33) ||
+ (y != MAXY - 1))
+ /* not exit to level 1 */
+ return (1);
+ return (0);
+}
+
+/*
+ * createitem(it,arg) Routine to place an item next to the player
+ * int it,arg;
+ *
+ * Enter with the item number and its argument (iven[], ivenarg[])
+ * Returns no value, thus we don't know about createitem() failures.
+ */
+void
+createitem(int it, int arg)
+{
+ int x, y, k, i;
+ if (it >= MAXOBJ)
+ return; /* no such object */
+ for (k = rnd(8), i = -8; i < 0; i++, k++) { /* choose direction,
+ * then try all */
+ if (k > 8)
+ k = 1; /* wraparound the diroff arrays */
+ x = playerx + diroffx[k];
+ y = playery + diroffy[k];
+ if (cgood(x, y, 1, 0)) { /* if we can create here */
+ item[x][y] = it;
+ know[x][y] = 0;
+ iarg[x][y] = arg;
+ return;
+ }
+ }
+}
+
+/*
+ * cast() Subroutine called by parse to cast a spell for the user
+ *
+ * No arguments and no return value.
+ */
+static char eys[] = "\nEnter your spell: ";
+void
+cast(void)
+{
+ int i, j, a, b, d;
+ cursors();
+ if (c[SPELLS] <= 0) {
+ lprcat("\nYou don't have any spells!");
+ return;
+ }
+ lprcat(eys);
+ --c[SPELLS];
+ while ((a = ttgetch()) == 'D') {
+ seemagic(-1);
+ cursors();
+ lprcat(eys);
+ }
+ if (a == '\33')
+ goto over; /* to escape casting a spell */
+ if ((b = ttgetch()) == '\33')
+ goto over; /* to escape casting a spell */
+ if ((d = ttgetch()) == '\33') {
+over: lprcat(aborted);
+ c[SPELLS]++;
+ return;
+ } /* to escape casting a spell */
+#ifdef EXTRA
+ c[SPELLSCAST]++;
+#endif
+ for (lprc('\n'), j = -1, i = 0; i < SPNUM; i++) /* seq search for his
+ * spell, hash? */
+ if ((spelcode[i][0] == a) && (spelcode[i][1] == b) && (spelcode[i][2] == d))
+ if (spelknow[i]) {
+ speldamage(i);
+ j = 1;
+ i = SPNUM;
+ }
+ if (j == -1)
+ lprcat(" Nothing Happened ");
+ bottomline();
+}
+
+/*
+ * speldamage(x) Function to perform spell functions cast by the player
+ * int x;
+ *
+ * Enter with the spell number, returns no value.
+ * Please insure that there are 2 spaces before all messages here
+ */
+static void
+speldamage(int x)
+{
+ int i, j, clev;
+ int xl, xh, yl, yh;
+ u_char *p, *kn, *pm;
+
+ if (x >= SPNUM)
+ return; /* no such spell */
+ if (c[TIMESTOP]) {
+ lprcat(" It didn't seem to work");
+ return;
+ } /* not if time stopped */
+ clev = c[LEVEL];
+ if ((rnd(23) == 7) || (rnd(18) > c[INTELLIGENCE])) {
+ lprcat(" It didn't work!");
+ return;
+ }
+ if (clev * 3 + 2 < x) {
+ lprcat(" Nothing happens. You seem inexperienced at this");
+ return;
+ }
+ switch (x) {
+ /* ----- LEVEL 1 SPELLS ----- */
+
+ case 0:
+ if (c[PROTECTIONTIME] == 0)
+ c[MOREDEFENSES] += 2; /* protection field +2 */
+ c[PROTECTIONTIME] += 250;
+ return;
+
+ case 1:
+ i = rnd(((clev + 1) << 1)) + clev + 3;
+ godirect(x, i, (clev >= 2) ? " Your missiles hit the %s" : " Your missile hit the %s", 100, '+'); /* magic missile */
+
+ return;
+
+ case 2:
+ if (c[DEXCOUNT] == 0)
+ c[DEXTERITY] += 3; /* dexterity */
+ c[DEXCOUNT] += 400;
+ return;
+
+ case 3: /* sleep */
+ i = rnd(3) + 1;
+ direct(x, fullhit(i),
+ " While the %s slept, you smashed it %ld times", i);
+ return;
+
+ case 4: /* charm monster */
+ c[CHARMCOUNT] += c[CHARISMA] << 1;
+ return;
+
+ case 5:
+ godirect(x, rnd(10) + 15 + clev, " The sound damages the %s", 70, '@'); /* sonic spear */
+ return;
+
+ /* ----- LEVEL 2 SPELLS ----- */
+
+ case 6: /* web */
+ i = rnd(3) + 2;
+ direct(x, fullhit(i),
+ " While the %s is entangled, you hit %ld times", i);
+ return;
+
+ case 7:
+ if (c[STRCOUNT] == 0)
+ c[STREXTRA] += 3; /* strength */
+ c[STRCOUNT] += 150 + rnd(100);
+ return;
+
+ case 8:
+ yl = playery - 5; /* enlightenment */
+ yh = playery + 6;
+ xl = playerx - 15;
+ xh = playerx + 16;
+ vxy(&xl, &yl);
+ vxy(&xh, &yh); /* check bounds */
+ for (i = yl; i <= yh; i++) /* enlightenment */
+ for (j = xl; j <= xh; j++)
+ know[j][i] = 1;
+ draws(xl, xh + 1, yl, yh + 1);
+ return;
+
+ case 9:
+ raisehp(20 + (clev << 1));
+ return; /* healing */
+
+ case 10:
+ c[BLINDCOUNT] = 0;
+ return; /* cure blindness */
+
+ case 11:
+ createmonster(makemonst(level + 1) + 8);
+ return;
+
+ case 12:
+ if (rnd(11) + 7 <= c[WISDOM])
+ direct(x, rnd(20) + 20 + clev, " The %s believed!", 0);
+ else
+ lprcat(" It didn't believe the illusions!");
+ return;
+
+ case 13: /* if he has the amulet of invisibility then
+ * add more time */
+ for (j = i = 0; i < 26; i++)
+ if (iven[i] == OAMULET)
+ j += 1 + ivenarg[i];
+ c[INVISIBILITY] += (j << 7) + 12;
+ return;
+
+ /* ----- LEVEL 3 SPELLS ----- */
+
+ case 14:
+ godirect(x, rnd(25 + clev) + 25 + clev, " The fireball hits the %s", 40, '*');
+ return; /* fireball */
+
+ case 15:
+ godirect(x, rnd(25) + 20 + clev, " Your cone of cold strikes the %s", 60, 'O'); /* cold */
+ return;
+
+ case 16:
+ dirpoly(x);
+ return; /* polymorph */
+
+ case 17:
+ c[CANCELLATION] += 5 + clev;
+ return; /* cancellation */
+
+ case 18:
+ c[HASTESELF] += 7 + clev;
+ return; /* haste self */
+
+ case 19:
+ omnidirect(x, 30 + rnd(10), " The %s gasps for air"); /* cloud kill */
+ return;
+
+ case 20:
+ xh = min(playerx + 1, MAXX - 2);
+ yh = min(playery + 1, MAXY - 2);
+ for (i = max(playerx - 1, 1); i <= xh; i++) /* vaporize rock */
+ for (j = max(playery - 1, 1); j <= yh; j++) {
+ kn = &know[i][j];
+ pm = &mitem[i][j];
+ switch (*(p = &item[i][j])) {
+ case OWALL:
+ if (level < MAXLEVEL + MAXVLEVEL - 1)
+ *p = *kn = 0;
+ break;
+
+ case OSTATUE:
+ if (c[HARDGAME] < 3) {
+ *p = OBOOK;
+ iarg[i][j] = level;
+ *kn = 0;
+ }
+ break;
+
+ case OTHRONE:
+ *pm = GNOMEKING;
+ *kn = 0;
+ *p = OTHRONE2;
+ hitp[i][j] = monster[GNOMEKING].hitpoints;
+ break;
+
+ case OALTAR:
+ *pm = DEMONPRINCE;
+ *kn = 0;
+ hitp[i][j] = monster[DEMONPRINCE].hitpoints;
+ break;
+ };
+ switch (*pm) {
+ case XORN:
+ ifblind(i, j);
+ hitm(i, j, 200);
+ break; /* Xorn takes damage from vpr */
+ }
+ }
+ return;
+
+ /* ----- LEVEL 4 SPELLS ----- */
+
+ case 21:
+ direct(x, 100 + clev, " The %s shrivels up", 0); /* dehydration */
+ return;
+
+ case 22:
+ godirect(x, rnd(25) + 20 + (clev << 1), " A lightning bolt hits the %s", 1, '~'); /* lightning */
+ return;
+
+ case 23:
+ i = min(c[HP] - 1, c[HPMAX] / 2); /* drain life */
+ direct(x, i + i, "", 0);
+ c[HP] -= i;
+ return;
+
+ case 24:
+ if (c[GLOBE] == 0)
+ c[MOREDEFENSES] += 10;
+ c[GLOBE] += 200;
+ loseint(); /* globe of invulnerability */
+ return;
+
+ case 25:
+ omnidirect(x, 32 + clev, " The %s struggles for air in your flood!"); /* flood */
+ return;
+
+ case 26:
+ if (rnd(151) == 63) {
+ beep();
+ lprcat("\nYour heart stopped!\n");
+ nap(4000);
+ died(270);
+ return;
+ }
+ if (c[WISDOM] > rnd(10) + 10)
+ direct(x, 2000, " The %s's heart stopped", 0); /* finger of death */
+ else
+ lprcat(" It didn't work");
+ return;
+
+ /* ----- LEVEL 5 SPELLS ----- */
+
+ case 27:
+ c[SCAREMONST] += rnd(10) + clev;
+ return; /* scare monster */
+
+ case 28:
+ c[HOLDMONST] += rnd(10) + clev;
+ return; /* hold monster */
+
+ case 29:
+ c[TIMESTOP] += rnd(20) + (clev << 1);
+ return; /* time stop */
+
+ case 30:
+ tdirect(x);
+ return; /* teleport away */
+
+ case 31:
+ omnidirect(x, 35 + rnd(10) + clev, " The %s cringes from the flame"); /* magic fire */
+ return;
+
+ /* ----- LEVEL 6 SPELLS ----- */
+
+ case 32:
+ if ((rnd(23) == 5) && (wizard == 0)) { /* sphere of
+ * annihilation */
+ beep();
+ lprcat("\nYou have been enveloped by the zone of nothingness!\n");
+ nap(4000);
+ died(258);
+ return;
+ }
+ xl = playerx;
+ yl = playery;
+ loseint();
+ i = dirsub(&xl, &yl); /* get direction of sphere */
+ newsphere(xl, yl, i, rnd(20) + 11); /* make a sphere */
+ return;
+
+ case 33:
+ genmonst();
+ spelknow[33] = 0; /* genocide */
+ loseint();
+ return;
+
+ case 34: /* summon demon */
+ if (rnd(100) > 30) {
+ direct(x, 150, " The demon strikes at the %s", 0);
+ return;
+ }
+ if (rnd(100) > 15) {
+ lprcat(" Nothing seems to have happened");
+ return;
+ }
+ lprcat(" The demon turned on you and vanished!");
+ beep();
+ i = rnd(40) + 30;
+ lastnum = 277;
+ losehp(i); /* must say killed by a demon */
+ return;
+
+ case 35: /* walk through walls */
+ c[WTW] += rnd(10) + 5;
+ return;
+
+ case 36: /* alter reality */
+ {
+ struct isave *save; /* pointer to item save
+ * structure */
+ int sc;
+ sc = 0; /* # items saved */
+ save = (struct isave *) malloc(sizeof(struct isave) * MAXX * MAXY * 2);
+ for (j = 0; j < MAXY; j++)
+ for (i = 0; i < MAXX; i++) { /* save all items and
+ * monsters */
+ xl = item[i][j];
+ if (xl && xl != OWALL && xl != OANNIHILATION) {
+ save[sc].type = 0;
+ save[sc].id = item[i][j];
+ save[sc++].arg = iarg[i][j];
+ }
+ if (mitem[i][j]) {
+ save[sc].type = 1;
+ save[sc].id = mitem[i][j];
+ save[sc++].arg = hitp[i][j];
+ }
+ item[i][j] = OWALL;
+ mitem[i][j] = 0;
+ if (wizard)
+ know[i][j] = 1;
+ else
+ know[i][j] = 0;
+ }
+ eat(1, 1);
+ if (level == 1)
+ item[33][MAXY - 1] = 0;
+ for (j = rnd(MAXY - 2), i = 1; i < MAXX - 1; i++)
+ item[i][j] = 0;
+ while (sc > 0) { /* put objects back in level */
+ --sc;
+ if (save[sc].type == 0) {
+ int trys;
+ for (trys = 100, i = j = 1; --trys > 0 && item[i][j]; i = rnd(MAXX - 1), j = rnd(MAXY - 1));
+ if (trys) {
+ item[i][j] = save[sc].id;
+ iarg[i][j] = save[sc].arg;
+ }
+ } else { /* put monsters back in */
+ int trys;
+ for (trys = 100, i = j = 1; --trys > 0 && (item[i][j] == OWALL || mitem[i][j]); i = rnd(MAXX - 1), j = rnd(MAXY - 1));
+ if (trys) {
+ mitem[i][j] = save[sc].id;
+ hitp[i][j] = save[sc].arg;
+ }
+ }
+ }
+ loseint();
+ draws(0, MAXX, 0, MAXY);
+ if (wizard == 0)
+ spelknow[36] = 0;
+ free((char *) save);
+ positionplayer();
+ return;
+ }
+
+ case 37: /* permanence */
+ adjusttime(-99999L);
+ spelknow[37] = 0; /* forget */
+ loseint();
+ return;
+
+ default:
+ lprintf(" spell %ld not available!", (long) x);
+ beep();
+ return;
+ };
+}
+
+/*
+ * loseint() Routine to subtract 1 from your int (intelligence) if > 3
+ *
+ * No arguments and no return value
+ */
+static void
+loseint(void)
+{
+ if (--c[INTELLIGENCE] < 3)
+ c[INTELLIGENCE] = 3;
+}
+
+/*
+ * isconfuse() Routine to check to see if player is confused
+ *
+ * This routine prints out a message saying "You can't aim your magic!"
+ * returns 0 if not confused, non-zero (time remaining confused) if confused
+ */
+static int
+isconfuse(void)
+{
+ if (c[CONFUSE]) {
+ lprcat(" You can't aim your magic!");
+ beep();
+ }
+ return (c[CONFUSE]);
+}
+
+/*
+ * nospell(x,monst) Routine to return 1 if a spell doesn't affect a monster
+ * int x,monst;
+ *
+ * Subroutine to return 1 if the spell can't affect the monster
+ * otherwise returns 0
+ * Enter with the spell number in x, and the monster number in monst.
+ */
+static int
+nospell(int x, int monst)
+{
+ int tmp;
+ if (x >= SPNUM || monst >= MAXMONST + 8 || monst < 0 || x < 0)
+ return (0); /* bad spell or monst */
+ if ((tmp = spelweird[monst - 1][x]) == 0)
+ return (0);
+ cursors();
+ lprc('\n');
+ lprintf(spelmes[tmp], monster[monst].name);
+ return (1);
+}
+
+/*
+ * fullhit(xx) Function to return full damage against a monster (aka web)
+ * int xx;
+ *
+ * Function to return hp damage to monster due to a number of full hits
+ * Enter with the number of full hits being done
+ */
+static int
+fullhit(int xx)
+{
+ int i;
+ if (xx < 0 || xx > 20)
+ return (0); /* fullhits are out of range */
+ if (c[LANCEDEATH])
+ return (10000); /* lance of death */
+ i = xx * ((c[WCLASS] >> 1) + c[STRENGTH] + c[STREXTRA] - c[HARDGAME] - 12 + c[MOREDAM]);
+ return ((i >= 1) ? i : xx);
+}
+
+/*
+ * direct(spnum,dam,str,arg) Routine to direct spell damage 1 square in 1 dir
+ * int spnum,dam,arg;
+ * char *str;
+ *
+ * Routine to ask for a direction to a spell and then hit the monster
+ * Enter with the spell number in spnum, the damage to be done in dam,
+ * lprintf format string in str, and lprintf's argument in arg.
+ * Returns no value.
+ */
+static void
+direct(int spnum, int dam, const char *str, int arg)
+{
+ int x, y;
+ int m;
+ if (spnum < 0 || spnum >= SPNUM || str == 0)
+ return; /* bad arguments */
+ if (isconfuse())
+ return;
+ dirsub(&x, &y);
+ m = mitem[x][y];
+ if (item[x][y] == OMIRROR) {
+ if (spnum == 3) { /* sleep */
+ lprcat("You fall asleep! ");
+ beep();
+ fool:
+ arg += 2;
+ while (arg-- > 0) {
+ parse2();
+ nap(1000);
+ }
+ return;
+ } else if (spnum == 6) { /* web */
+ lprcat("You get stuck in your own web! ");
+ beep();
+ goto fool;
+ } else {
+ lastnum = 278;
+ lprintf(str, "spell caster (that's you)", (long) arg);
+ beep();
+ losehp(dam);
+ return;
+ }
+ }
+ if (m == 0) {
+ lprcat(" There wasn't anything there!");
+ return;
+ }
+ ifblind(x, y);
+ if (nospell(spnum, m)) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ lprintf(str, lastmonst, (long) arg);
+ hitm(x, y, dam);
+}
+
+/*
+ * godirect(spnum,dam,str,delay,cshow) Function to perform missile attacks
+ * int spnum,dam,delay;
+ * char *str,cshow;
+ *
+ * Function to hit in a direction from a missile weapon and have it keep
+ * on going in that direction until its power is exhausted
+ * Enter with the spell number in spnum, the power of the weapon in hp,
+ * lprintf format string in str, the # of milliseconds to delay between
+ * locations in delay, and the character to represent the weapon in cshow.
+ * Returns no value.
+ */
+void
+godirect(int spnum, int dam, const char *str, int delay, int cshow_i)
+{
+ u_char *p;
+ int x, y, m;
+ int dx, dy;
+ char cshow;
+
+ /* truncate to char width in case it matters */
+ cshow = (char)cshow_i;
+
+ if (spnum < 0 || spnum >= SPNUM || str == 0 || delay < 0)
+ return; /* bad args */
+ if (isconfuse())
+ return;
+ dirsub(&dx, &dy);
+ x = dx;
+ y = dy;
+ dx = x - playerx;
+ dy = y - playery;
+ x = playerx;
+ y = playery;
+ while (dam > 0) {
+ x += dx;
+ y += dy;
+ if ((x > MAXX - 1) || (y > MAXY - 1) || (x < 0) || (y < 0)) {
+ dam = 0;
+ break; /* out of bounds */
+ }
+ if ((x == playerx) && (y == playery)) { /* if energy hits player */
+ cursors();
+ lprcat("\nYou are hit by your own magic!");
+ beep();
+ lastnum = 278;
+ losehp(dam);
+ return;
+ }
+ if (c[BLINDCOUNT] == 0) { /* if not blind show effect */
+ cursor(x + 1, y + 1);
+ lprc(cshow);
+ nap(delay);
+ show1cell(x, y);
+ }
+ if ((m = mitem[x][y])) { /* is there a monster there? */
+ ifblind(x, y);
+ if (nospell(spnum, m)) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ cursors();
+ lprc('\n');
+ lprintf(str, lastmonst);
+ dam -= hitm(x, y, dam);
+ show1cell(x, y);
+ nap(1000);
+ x -= dx;
+ y -= dy;
+ } else
+ switch (*(p = &item[x][y])) {
+ case OWALL:
+ cursors();
+ lprc('\n');
+ lprintf(str, "wall");
+ if (dam >= 50 + c[HARDGAME]) /* enough damage? */
+ if (level < MAXLEVEL + MAXVLEVEL - 1) /* not on V3 */
+ if ((x < MAXX - 1) && (y < MAXY - 1) && (x) && (y)) {
+ lprcat(" The wall crumbles");
+ god3: *p = 0;
+ god: know[x][y] = 0;
+ show1cell(x, y);
+ }
+ god2: dam = 0;
+ break;
+
+ case OCLOSEDDOOR:
+ cursors();
+ lprc('\n');
+ lprintf(str, "door");
+ if (dam >= 40) {
+ lprcat(" The door is blasted apart");
+ goto god3;
+ }
+ goto god2;
+
+ case OSTATUE:
+ cursors();
+ lprc('\n');
+ lprintf(str, "statue");
+ if (c[HARDGAME] < 3)
+ if (dam > 44) {
+ lprcat(" The statue crumbles");
+ *p = OBOOK;
+ iarg[x][y] = level;
+ goto god;
+ }
+ goto god2;
+
+ case OTHRONE:
+ cursors();
+ lprc('\n');
+ lprintf(str, "throne");
+ if (dam > 39) {
+ mitem[x][y] = GNOMEKING;
+ hitp[x][y] = monster[GNOMEKING].hitpoints;
+ *p = OTHRONE2;
+ goto god;
+ }
+ goto god2;
+
+ case OMIRROR:
+ dx *= -1;
+ dy *= -1;
+ break;
+ };
+ dam -= 3 + (c[HARDGAME] >> 1);
+ }
+}
+
+/*
+ * ifblind(x,y) Routine to put "monster" or the monster name into lastmosnt
+ * int x,y;
+ *
+ * Subroutine to copy the word "monster" into lastmonst if the player is blind
+ * Enter with the coordinates (x,y) of the monster
+ * Returns no value.
+ */
+static void
+ifblind(int x, int y)
+{
+ const char *p;
+
+ vxy(&x, &y); /* verify correct x,y coordinates */
+ if (c[BLINDCOUNT]) {
+ lastnum = 279;
+ p = "monster";
+ } else {
+ lastnum = mitem[x][y];
+ p = monster[lastnum].name;
+ }
+ strcpy(lastmonst, p);
+}
+
+/*
+ * tdirect(spnum) Routine to teleport away a monster
+ * int spnum;
+ *
+ * Routine to ask for a direction to a spell and then teleport away monster
+ * Enter with the spell number that wants to teleport away
+ * Returns no value.
+ */
+static void
+tdirect(int spnum)
+{
+ int x, y;
+ int m;
+ if (spnum < 0 || spnum >= SPNUM)
+ return; /* bad args */
+ if (isconfuse())
+ return;
+ dirsub(&x, &y);
+ if ((m = mitem[x][y]) == 0) {
+ lprcat(" There wasn't anything there!");
+ return;
+ }
+ ifblind(x, y);
+ if (nospell(spnum, m)) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ fillmonst(m);
+ mitem[x][y] = know[x][y] = 0;
+}
+
+/*
+ * omnidirect(sp,dam,str) Routine to damage all monsters 1 square from player
+ * int sp,dam;
+ * char *str;
+ *
+ * Routine to cast a spell and then hit the monster in all directions
+ * Enter with the spell number in sp, the damage done to wach square in dam,
+ * and the lprintf string to identify the spell in str.
+ * Returns no value.
+ */
+static void
+omnidirect(int spnum, int dam, const char *str)
+{
+ int x, y, m;
+
+ if (spnum < 0 || spnum >= SPNUM || str == 0)
+ return; /* bad args */
+ for (x = playerx - 1; x < playerx + 2; x++)
+ for (y = playery - 1; y < playery + 2; y++) {
+ if ((m = mitem[x][y]) != 0) {
+ if (nospell(spnum, m) == 0) {
+ ifblind(x, y);
+ cursors();
+ lprc('\n');
+ lprintf(str, lastmonst);
+ hitm(x, y, dam);
+ nap(800);
+ } else {
+ lasthx = x;
+ lasthy = y;
+ }
+ }
+ }
+}
+
+/*
+ * static dirsub(x,y) Routine to ask for direction, then modify x,y for it
+ * int *x,*y;
+ *
+ * Function to ask for a direction and modify an x,y for that direction
+ * Enter with the origination coordinates in (x,y).
+ * Returns index into diroffx[] (0-8).
+ */
+static int
+dirsub(int *x, int *y)
+{
+ int i;
+ lprcat("\nIn What Direction? ");
+ for (i = 0;;)
+ switch (ttgetch()) {
+ case 'b':
+ i++;
+ case 'n':
+ i++;
+ case 'y':
+ i++;
+ case 'u':
+ i++;
+ case 'h':
+ i++;
+ case 'k':
+ i++;
+ case 'l':
+ i++;
+ case 'j':
+ i++;
+ goto out;
+ };
+out:
+ *x = playerx + diroffx[i];
+ *y = playery + diroffy[i];
+ vxy(x, y);
+ return (i);
+}
+
+/*
+ * vxy(x,y) Routine to verify/fix coordinates for being within bounds
+ * int *x,*y;
+ *
+ * Function to verify x & y are within the bounds for a level
+ * If *x or *y is not within the absolute bounds for a level, fix them so that
+ * they are on the level.
+ * Returns TRUE if it was out of bounds, and the *x & *y in the calling
+ * routine are affected.
+ */
+int
+vxy(int *x, int *y)
+{
+ int flag = 0;
+ if (*x < 0) {
+ *x = 0;
+ flag++;
+ }
+ if (*y < 0) {
+ *y = 0;
+ flag++;
+ }
+ if (*x >= MAXX) {
+ *x = MAXX - 1;
+ flag++;
+ }
+ if (*y >= MAXY) {
+ *y = MAXY - 1;
+ flag++;
+ }
+ return (flag);
+}
+
+/*
+ * dirpoly(spnum) Routine to ask for a direction and polymorph a monst
+ * int spnum;
+ *
+ * Subroutine to polymorph a monster and ask for the direction its in
+ * Enter with the spell number in spmun.
+ * Returns no value.
+ */
+static void
+dirpoly(int spnum)
+{
+ int x, y, m;
+ if (spnum < 0 || spnum >= SPNUM)
+ return; /* bad args */
+ if (isconfuse())
+ return; /* if he is confused, he can't aim his magic */
+ dirsub(&x, &y);
+ if (mitem[x][y] == 0) {
+ lprcat(" There wasn't anything there!");
+ return;
+ }
+ ifblind(x, y);
+ if (nospell(spnum, mitem[x][y])) {
+ lasthx = x;
+ lasthy = y;
+ return;
+ }
+ while (monster[m = mitem[x][y] = rnd(MAXMONST + 7)].genocided);
+ hitp[x][y] = monster[m].hitpoints;
+ show1cell(x, y); /* show the new monster */
+}
+
+/*
+ * hitmonster(x,y) Function to hit a monster at the designated coordinates
+ * int x,y;
+ *
+ * This routine is used for a bash & slash type attack on a monster
+ * Enter with the coordinates of the monster in (x,y).
+ * Returns no value.
+ */
+void
+hitmonster(int x, int y)
+{
+ int tmp, monst, damag = 0, flag;
+ if (c[TIMESTOP])
+ return; /* not if time stopped */
+ vxy(&x, &y); /* verify coordinates are within range */
+ if ((monst = mitem[x][y]) == 0)
+ return;
+ hit3flag = 1;
+ ifblind(x, y);
+ tmp = monster[monst].armorclass + c[LEVEL] + c[DEXTERITY] +
+ c[WCLASS] / 4 - 12;
+ cursors();
+ /* need at least random chance to hit */
+ if ((rnd(20) < tmp - c[HARDGAME]) || (rnd(71) < 5)) {
+ lprcat("\nYou hit");
+ flag = 1;
+ damag = fullhit(1);
+ if (damag < 9999)
+ damag = rnd(damag) + 1;
+ } else {
+ lprcat("\nYou missed");
+ flag = 0;
+ }
+ lprcat(" the ");
+ lprcat(lastmonst);
+ if (flag) /* if the monster was hit */
+ if ((monst == RUSTMONSTER) || (monst == DISENCHANTRESS) || (monst == CUBE))
+ if (c[WIELD] > 0)
+ if (ivenarg[c[WIELD]] > -10) {
+ lprintf("\nYour weapon is dulled by the %s", lastmonst);
+ beep();
+ --ivenarg[c[WIELD]];
+ }
+ if (flag)
+ hitm(x, y, damag);
+ if (monst == VAMPIRE)
+ if (hitp[x][y] < 25) {
+ mitem[x][y] = BAT;
+ know[x][y] = 0;
+ }
+}
+
+/*
+ * hitm(x,y,amt) Function to just hit a monster at a given coordinates
+ * int x,y,amt;
+ *
+ * Returns the number of hitpoints the monster absorbed
+ * This routine is used to specifically damage a monster at a location (x,y)
+ * Called by hitmonster(x,y)
+ */
+static int
+hitm(int x, int y, int amt)
+{
+ int monst;
+ int hpoints, amt2;
+ vxy(&x, &y); /* verify coordinates are within range */
+ amt2 = amt; /* save initial damage so we can return it */
+ monst = mitem[x][y];
+ if (c[HALFDAM])
+ amt >>= 1; /* if half damage curse adjust damage points */
+ if (amt <= 0)
+ amt2 = amt = 1;
+ lasthx = x;
+ lasthy = y;
+ stealth[x][y] = 1; /* make sure hitting monst breaks stealth
+ * condition */
+ c[HOLDMONST] = 0; /* hit a monster breaks hold monster spell */
+ switch (monst) { /* if a dragon and orb(s) of dragon slaying */
+ case WHITEDRAGON:
+ case REDDRAGON:
+ case GREENDRAGON:
+ case BRONZEDRAGON:
+ case PLATINUMDRAGON:
+ case SILVERDRAGON:
+ amt *= 1 + (c[SLAYING] << 1);
+ break;
+ }
+ /* invincible monster fix is here */
+ if (hitp[x][y] > monster[monst].hitpoints)
+ hitp[x][y] = monster[monst].hitpoints;
+ if ((hpoints = hitp[x][y]) <= amt) {
+#ifdef EXTRA
+ c[MONSTKILLED]++;
+#endif
+ lprintf("\nThe %s died!", lastmonst);
+ raiseexperience((long) monster[monst].experience);
+ amt = monster[monst].gold;
+ if (amt > 0)
+ dropgold(rnd(amt) + amt);
+ dropsomething(monst);
+ disappear(x, y);
+ bottomline();
+ return (hpoints);
+ }
+ hitp[x][y] = hpoints - amt;
+ return (amt2);
+}
+
+/*
+ * hitplayer(x,y) Function for the monster to hit the player from (x,y)
+ * int x,y;
+ *
+ * Function for the monster to hit the player with monster at location x,y
+ * Returns nothing of value.
+ */
+void
+hitplayer(int x, int y)
+{
+ int dam, tmp, mster, bias;
+ vxy(&x, &y); /* verify coordinates are within range */
+ lastnum = mster = mitem[x][y];
+ /*
+ * spirit nagas and poltergeists do nothing if scarab of negate
+ * spirit
+ */
+ if (c[NEGATESPIRIT] || c[SPIRITPRO])
+ if ((mster == POLTERGEIST) || (mster == SPIRITNAGA))
+ return;
+ /* if undead and cube of undead control */
+ if (c[CUBEofUNDEAD] || c[UNDEADPRO])
+ if ((mster == VAMPIRE) || (mster == WRAITH) || (mster == ZOMBIE))
+ return;
+ if ((know[x][y] & 1) == 0) {
+ know[x][y] = 1;
+ show1cell(x, y);
+ }
+ bias = (c[HARDGAME]) + 1;
+ hitflag = hit2flag = hit3flag = 1;
+ yrepcount = 0;
+ cursors();
+ ifblind(x, y);
+ if (c[INVISIBILITY])
+ if (rnd(33) < 20) {
+ lprintf("\nThe %s misses wildly", lastmonst);
+ return;
+ }
+ if (c[CHARMCOUNT])
+ if (rnd(30) + 5 * monster[mster].level - c[CHARISMA] < 30) {
+ lprintf("\nThe %s is awestruck at your magnificence!", lastmonst);
+ return;
+ }
+ if (mster == BAT)
+ dam = 1;
+ else {
+ dam = monster[mster].damage;
+ dam += rnd((int) ((dam < 1) ? 1 : dam)) + monster[mster].level;
+ }
+ tmp = 0;
+ if (monster[mster].attack > 0)
+ if (((dam + bias + 8) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
+ if (spattack(monster[mster].attack, x, y)) {
+ flushall();
+ return;
+ }
+ tmp = 1;
+ bias -= 2;
+ cursors();
+ }
+ if (((dam + bias) > c[AC]) || (rnd((int) ((c[AC] > 0) ? c[AC] : 1)) == 1)) {
+ lprintf("\n The %s hit you ", lastmonst);
+ tmp = 1;
+ if ((dam -= c[AC]) < 0)
+ dam = 0;
+ if (dam > 0) {
+ losehp(dam);
+ bottomhp();
+ flushall();
+ }
+ }
+ if (tmp == 0)
+ lprintf("\n The %s missed ", lastmonst);
+}
+
+/*
+ * dropsomething(monst) Function to create an object when a monster dies
+ * int monst;
+ *
+ * Function to create an object near the player when certain monsters are killed
+ * Enter with the monster number
+ * Returns nothing of value.
+ */
+static void
+dropsomething(int monst)
+{
+ switch (monst) {
+ case ORC:
+ case NYMPH:
+ case ELF:
+ case TROGLODYTE:
+ case TROLL:
+ case ROTHE:
+ case VIOLETFUNGI:
+ case PLATINUMDRAGON:
+ case GNOMEKING:
+ case REDDRAGON:
+ something(level);
+ return;
+
+ case LEPRECHAUN:
+ if (rnd(101) >= 75)
+ creategem();
+ if (rnd(5) == 1)
+ dropsomething(LEPRECHAUN);
+ return;
+ }
+}
+
+/*
+ * dropgold(amount) Function to drop some gold around player
+ * int amount;
+ *
+ * Enter with the number of gold pieces to drop
+ * Returns nothing of value.
+ */
+void
+dropgold(int amount)
+{
+ if (amount > 250)
+ createitem(OMAXGOLD, amount / 100);
+ else
+ createitem(OGOLDPILE, amount);
+}
+
+/*
+ * something(level) Function to create a random item around player
+ * int level;
+ *
+ * Function to create an item from a designed probability around player
+ * Enter with the cave level on which something is to be dropped
+ * Returns nothing of value.
+ */
+void
+something(int cavelevel)
+{
+ int j;
+ int i;
+ if (cavelevel < 0 || cavelevel > MAXLEVEL + MAXVLEVEL)
+ return; /* correct level? */
+ if (rnd(101) < 8)
+ something(cavelevel); /* possibly more than one item */
+ j = newobject(cavelevel, &i);
+ createitem(j, i);
+}
+
+/*
+ * newobject(lev,i) Routine to return a randomly selected new object
+ * int lev,*i;
+ *
+ * Routine to return a randomly selected object to be created
+ * Returns the object number created, and sets *i for its argument
+ * Enter with the cave level and a pointer to the items arg
+ */
+static char nobjtab[] = {
+ 0, OSCROLL, OSCROLL, OSCROLL, OSCROLL, OPOTION, OPOTION,
+ OPOTION, OPOTION, OGOLDPILE, OGOLDPILE, OGOLDPILE, OGOLDPILE,
+ OBOOK, OBOOK, OBOOK, OBOOK, ODAGGER, ODAGGER, ODAGGER,
+ OLEATHER, OLEATHER, OLEATHER, OREGENRING, OPROTRING,
+ OENERGYRING, ODEXRING, OSTRRING, OSPEAR, OBELT, ORING,
+ OSTUDLEATHER, OSHIELD, OFLAIL, OCHAIN, O2SWORD, OPLATE,
+ OLONGSWORD};
+
+int
+newobject(int lev, int *i)
+{
+ int tmp = 32, j;
+ if (level < 0 || level > MAXLEVEL + MAXVLEVEL)
+ return (0); /* correct level? */
+ if (lev > 6)
+ tmp = 37;
+ else if (lev > 4)
+ tmp = 35;
+ j = nobjtab[tmp = rnd(tmp)]; /* the object type */
+ switch (tmp) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ *i = newscroll();
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ *i = newpotion();
+ break;
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ *i = rnd((lev + 1) * 10) + lev * 10 + 10;
+ break;
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ *i = lev;
+ break;
+ case 17:
+ case 18:
+ case 19:
+ if (!(*i = newdagger()))
+ return (0);
+ break;
+ case 20:
+ case 21:
+ case 22:
+ if (!(*i = newleather()))
+ return (0);
+ break;
+ case 23:
+ case 32:
+ case 35:
+ *i = rund(lev / 3 + 1);
+ break;
+ case 24:
+ case 26:
+ *i = rnd(lev / 4 + 1);
+ break;
+ case 25:
+ *i = rund(lev / 4 + 1);
+ break;
+ case 27:
+ *i = rnd(lev / 2 + 1);
+ break;
+ case 30:
+ case 33:
+ *i = rund(lev / 2 + 1);
+ break;
+ case 28:
+ *i = rund(lev / 3 + 1);
+ if (*i == 0)
+ return (0);
+ break;
+ case 29:
+ case 31:
+ *i = rund(lev / 2 + 1);
+ if (*i == 0)
+ return (0);
+ break;
+ case 34:
+ *i = newchain();
+ break;
+ case 36:
+ *i = newplate();
+ break;
+ case 37:
+ *i = newsword();
+ break;
+ }
+ return (j);
+}
+
+/*
+ * spattack(atckno,xx,yy) Function to process special attacks from monsters
+ * int atckno,xx,yy;
+ *
+ * Enter with the special attack number, and the coordinates (xx,yy)
+ * of the monster that is special attacking
+ * Returns 1 if must do a show1cell(xx,yy) upon return, 0 otherwise
+ *
+ * atckno monster effect
+ * ---------------------------------------------------
+ * 0 none
+ * 1 rust monster eat armor
+ * 2 hell hound breathe light fire
+ * 3 dragon breathe fire
+ * 4 giant centipede weakening sing
+ * 5 white dragon cold breath
+ * 6 wraith drain level
+ * 7 waterlord water gusher
+ * 8 leprechaun steal gold
+ * 9 disenchantress disenchant weapon or armor
+ * 10 ice lizard hits with barbed tail
+ * 11 umber hulk confusion
+ * 12 spirit naga cast spells taken from special attacks
+ * 13 platinum dragon psionics
+ * 14 nymph steal objects
+ * 15 bugbear bite
+ * 16 osequip bite
+ *
+ * char rustarm[ARMORTYPES][2];
+ * special array for maximum rust damage to armor from rustmonster
+ * format is: { armor type , minimum attribute
+ */
+#define ARMORTYPES 6
+static char rustarm[ARMORTYPES][2] = {
+ { OSTUDLEATHER, -2 },
+ { ORING, -4 },
+ { OCHAIN, -5 },
+ { OSPLINT, -6 },
+ { OPLATE, -8 },
+ { OPLATEARMOR, -9}
+};
+static char spsel[] = {1, 2, 3, 5, 6, 8, 9, 11, 13, 14};
+static int
+spattack(int x, int xx, int yy)
+{
+ int i, j = 0, k, m;
+ const char *p = NULL;
+
+ if (c[CANCELLATION])
+ return (0);
+ vxy(&xx, &yy); /* verify x & y coordinates */
+ switch (x) {
+ case 1: /* rust your armor, j=1 when rusting has occurred */
+ m = k = c[WEAR];
+ if ((i = c[SHIELD]) != -1) {
+ if (--ivenarg[i] < -1)
+ ivenarg[i] = -1;
+ else
+ j = 1;
+ }
+ if ((j == 0) && (k != -1)) {
+ m = iven[k];
+ for (i = 0; i < ARMORTYPES; i++)
+ /* find his armor in table */
+ if (m == rustarm[i][0]) {
+ if (--ivenarg[k] < rustarm[i][1])
+ ivenarg[k] = rustarm[i][1];
+ else
+ j = 1;
+ break;
+ }
+ }
+ if (j == 0) /* if rusting did not occur */
+ switch (m) {
+ case OLEATHER:
+ p = "\nThe %s hit you -- You're lucky you have leather on";
+ break;
+ case OSSPLATE:
+ p = "\nThe %s hit you -- You're fortunate to have stainless steel armor!";
+ break;
+ }
+ else {
+ beep();
+ p = "\nThe %s hit you -- your armor feels weaker";
+ }
+ break;
+
+ case 2:
+ i = rnd(15) + 8 - c[AC];
+spout: p = "\nThe %s breathes fire at you!";
+ if (c[FIRERESISTANCE])
+ p = "\nThe %s's flame doesn't faze you!";
+ else
+spout2: if (p) {
+ lprintf(p, lastmonst);
+ beep();
+ }
+ checkloss(i);
+ return (0);
+
+ case 3:
+ i = rnd(20) + 25 - c[AC];
+ goto spout;
+
+ case 4:
+ if (c[STRENGTH] > 3) {
+ p = "\nThe %s stung you! You feel weaker";
+ beep();
+ --c[STRENGTH];
+ } else
+ p = "\nThe %s stung you!";
+ break;
+
+ case 5:
+ p = "\nThe %s blasts you with his cold breath";
+ i = rnd(15) + 18 - c[AC];
+ goto spout2;
+
+ case 6:
+ lprintf("\nThe %s drains you of your life energy!", lastmonst);
+ loselevel();
+ beep();
+ return (0);
+
+ case 7:
+ p = "\nThe %s got you with a gusher!";
+ i = rnd(15) + 25 - c[AC];
+ goto spout2;
+
+ case 8:
+ if (c[NOTHEFT])
+ return (0); /* he has a device of no theft */
+ if (c[GOLD]) {
+ p = "\nThe %s hit you -- Your purse feels lighter";
+ if (c[GOLD] > 32767)
+ c[GOLD] >>= 1;
+ else
+ c[GOLD] -= rnd((int) (1 + (c[GOLD] >> 1)));
+ if (c[GOLD] < 0)
+ c[GOLD] = 0;
+ } else
+ p = "\nThe %s couldn't find any gold to steal";
+ lprintf(p, lastmonst);
+ disappear(xx, yy);
+ beep();
+ bottomgold();
+ return (1);
+
+ case 9:
+ for (j = 50;;) {/* disenchant */
+ i = rund(26);
+ m = iven[i]; /* randomly select item */
+ if (m > 0 && ivenarg[i] > 0 && m != OSCROLL && m != OPOTION) {
+ if ((ivenarg[i] -= 3) < 0)
+ ivenarg[i] = 0;
+ lprintf("\nThe %s hits you -- you feel a sense of loss", lastmonst);
+ srcount = 0;
+ beep();
+ show3(i);
+ bottomline();
+ return (0);
+ }
+ if (--j <= 0) {
+ p = "\nThe %s nearly misses";
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 10:
+ p = "\nThe %s hit you with his barbed tail";
+ i = rnd(25) - c[AC];
+ goto spout2;
+
+ case 11:
+ p = "\nThe %s has confused you";
+ beep();
+ c[CONFUSE] += 10 + rnd(10);
+ break;
+
+ case 12: /* performs any number of other special
+ * attacks */
+ return (spattack(spsel[rund(10)], xx, yy));
+
+ case 13:
+ p = "\nThe %s flattens you with his psionics!";
+ i = rnd(15) + 30 - c[AC];
+ goto spout2;
+
+ case 14:
+ if (c[NOTHEFT])
+ return (0); /* he has device of no theft */
+ if (emptyhanded() == 1) {
+ p = "\nThe %s couldn't find anything to steal";
+ break;
+ }
+ lprintf("\nThe %s picks your pocket and takes:", lastmonst);
+ beep();
+ if (stealsomething() == 0)
+ lprcat(" nothing");
+ disappear(xx, yy);
+ bottomline();
+ return (1);
+
+ case 15:
+ i = rnd(10) + 5 - c[AC];
+spout3: p = "\nThe %s bit you!";
+ goto spout2;
+
+ case 16:
+ i = rnd(15) + 10 - c[AC];
+ goto spout3;
+ };
+ if (p) {
+ lprintf(p, lastmonst);
+ bottomline();
+ }
+ return (0);
+}
+
+/*
+ * checkloss(x) Routine to subtract hp from user and flag bottomline display
+ * int x;
+ *
+ * Routine to subtract hitpoints from the user and flag the bottomline display
+ * Enter with the number of hit points to lose
+ * Note: if x > c[HP] this routine could kill the player!
+ */
+void
+checkloss(int x)
+{
+ if (x > 0) {
+ losehp(x);
+ bottomhp();
+ }
+}
+
+/*
+ * annihilate() Routine to annihilate all monsters around player (playerx,playery)
+ *
+ * Gives player experience, but no dropped objects
+ * Returns the experience gained from all monsters killed
+ */
+int
+annihilate(void)
+{
+ int i, j;
+ long k;
+ u_char *p;
+ for (k = 0, i = playerx - 1; i <= playerx + 1; i++)
+ for (j = playery - 1; j <= playery + 1; j++)
+ if (!vxy(&i, &j)) { /* if not out of bounds */
+ if (*(p = &mitem[i][j])) { /* if a monster there */
+ if (*p < DEMONLORD + 2) {
+ k += monster[*p].experience;
+ *p = know[i][j] = 0;
+ } else {
+ lprintf("\nThe %s barely escapes being annihilated!", monster[*p].name);
+ hitp[i][j] = (hitp[i][j] >> 1) + 1; /* lose half hit points */
+ }
+ }
+ }
+ if (k > 0) {
+ lprcat("\nYou hear loud screams of agony!");
+ raiseexperience((long) k);
+ }
+ return (k);
+}
+
+/*
+ * newsphere(x,y,dir,lifetime) Function to create a new sphere of annihilation
+ * int x,y,dir,lifetime;
+ *
+ * Enter with the coordinates of the sphere in x,y
+ * the direction (0-8 diroffx format) in dir, and the lifespan of the
+ * sphere in lifetime (in turns)
+ * Returns the number of spheres currently in existence
+ */
+int
+newsphere(int x, int y, int dir, int life)
+{
+ int m;
+ struct sphere *sp;
+ if (((sp = (struct sphere *) malloc(sizeof(struct sphere)))) == 0)
+ return (c[SPHCAST]); /* can't malloc, therefore failure */
+ if (dir >= 9)
+ dir = 0; /* no movement if direction not found */
+ if (level == 0)
+ vxy(&x, &y); /* don't go out of bounds */
+ else {
+ if (x < 1)
+ x = 1;
+ if (x >= MAXX - 1)
+ x = MAXX - 2;
+ if (y < 1)
+ y = 1;
+ if (y >= MAXY - 1)
+ y = MAXY - 2;
+ }
+ if ((m = mitem[x][y]) >= DEMONLORD + 4) { /* demons dispel spheres */
+ know[x][y] = 1;
+ show1cell(x, y);/* show the demon (ha ha) */
+ cursors();
+ lprintf("\nThe %s dispels the sphere!", monster[m].name);
+ beep();
+ rmsphere(x, y); /* remove any spheres that are here */
+ free(sp);
+ return (c[SPHCAST]);
+ }
+ if (m == DISENCHANTRESS) { /* disenchantress cancels spheres */
+ cursors();
+ lprintf("\nThe %s causes cancellation of the sphere!", monster[m].name);
+ beep();
+boom: sphboom(x, y); /* blow up stuff around sphere */
+ rmsphere(x, y); /* remove any spheres that are here */
+ free(sp);
+ return (c[SPHCAST]);
+ }
+ if (c[CANCELLATION]) { /* cancellation cancels spheres */
+ cursors();
+ lprcat("\nAs the cancellation takes effect, you hear a great earth shaking blast!");
+ beep();
+ goto boom;
+ }
+ if (item[x][y] == OANNIHILATION) { /* collision of spheres
+ * detonates spheres */
+ cursors();
+ lprcat("\nTwo spheres of annihilation collide! You hear a great earth shaking blast!");
+ beep();
+ rmsphere(x, y);
+ goto boom;
+ }
+ if (playerx == x && playery == y) { /* collision of sphere and
+ * player! */
+ cursors();
+ lprcat("\nYou have been enveloped by the zone of nothingness!\n");
+ beep();
+ rmsphere(x, y); /* remove any spheres that are here */
+ nap(4000);
+ died(258);
+ }
+ item[x][y] = OANNIHILATION;
+ mitem[x][y] = 0;
+ know[x][y] = 1;
+ show1cell(x, y); /* show the new sphere */
+ sp->x = x;
+ sp->y = y;
+ sp->lev = level;
+ sp->dir = dir;
+ sp->lifetime = life;
+ sp->p = 0;
+ if (spheres == 0)
+ spheres = sp; /* if first node in the sphere list */
+ else { /* add sphere to beginning of linked list */
+ sp->p = spheres;
+ spheres = sp;
+ }
+ return (++c[SPHCAST]); /* one more sphere in the world */
+}
+
+/*
+ * rmsphere(x,y) Function to delete a sphere of annihilation from list
+ * int x,y;
+ *
+ * Enter with the coordinates of the sphere (on current level)
+ * Returns the number of spheres currently in existence
+ */
+int
+rmsphere(int x, int y)
+{
+ struct sphere *sp, *sp2 = 0;
+ for (sp = spheres; sp; sp2 = sp, sp = sp->p)
+ if (level == sp->lev) /* is sphere on this level? */
+ if ((x == sp->x) && (y == sp->y)) { /* locate sphere at this
+ * location */
+ item[x][y] = mitem[x][y] = 0;
+ know[x][y] = 1;
+ show1cell(x, y); /* show the now missing
+ * sphere */
+ --c[SPHCAST];
+ if (sp == spheres) {
+ sp2 = sp;
+ spheres = sp->p;
+ free((char *) sp2);
+ } else {
+ if (sp2)
+ sp2->p = sp->p;
+ free((char *) sp);
+ }
+ break;
+ }
+ return (c[SPHCAST]); /* return number of spheres in the world */
+}
+
+/*
+ * sphboom(x,y) Function to perform the effects of a sphere detonation
+ * int x,y;
+ *
+ * Enter with the coordinates of the blast, Returns no value
+ */
+static void
+sphboom(int x, int y)
+{
+ int i, j;
+ if (c[HOLDMONST])
+ c[HOLDMONST] = 1;
+ if (c[CANCELLATION])
+ c[CANCELLATION] = 1;
+ for (j = max(1, x - 2); j < min(x + 3, MAXX - 1); j++)
+ for (i = max(1, y - 2); i < min(y + 3, MAXY - 1); i++) {
+ item[j][i] = mitem[j][i] = 0;
+ show1cell(j, i);
+ if (playerx == j && playery == i) {
+ cursors();
+ beep();
+ lprcat("\nYou were too close to the sphere!");
+ nap(3000);
+ died(283); /* player killed in explosion */
+ }
+ }
+}
+
+/*
+ * genmonst() Function to ask for monster and genocide from game
+ *
+ * This is done by setting a flag in the monster[] structure
+ */
+static void
+genmonst(void)
+{
+ int i, j;
+ cursors();
+ lprcat("\nGenocide what monster? ");
+ for (i = 0; (!isalpha(i)) && (i != ' '); i = ttgetch());
+ lprc(i);
+ for (j = 0; j < MAXMONST; j++) /* search for the monster type */
+ if (monstnamelist[j] == i) { /* have we found it? */
+ monster[j].genocided = 1; /* genocided from game */
+ lprintf(" There will be no more %s's", monster[j].name);
+ /* now wipe out monsters on this level */
+ newcavelevel(level);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+ }
+ lprcat(" You sense failure!");
+}
diff --git a/larn/moreobj.c b/larn/moreobj.c
new file mode 100644
index 0000000..d36e6e1
--- /dev/null
+++ b/larn/moreobj.c
@@ -0,0 +1,296 @@
+/* $NetBSD: moreobj.c,v 1.12 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * moreobj.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Routines in this file:
+ *
+ * oaltar() othrone() ochest() ofountain()
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: moreobj.c,v 1.12 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+#include <stdlib.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void fch(int, long *);
+
+/*
+ * subroutine to process an altar object
+ */
+void
+oaltar(void)
+{
+
+ lprcat("\nDo you (p) pray (d) desecrate");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 'p':
+ lprcat(" pray\nDo you (m) give money or (j) just pray? ");
+ while (1)
+ switch (ttgetch()) {
+ case 'j':
+ act_just_pray();
+ return;
+
+ case 'm':
+ act_donation_pray();
+ return;
+
+ case '\33':
+ return;
+ };
+
+ case 'd':
+ lprcat(" desecrate");
+ act_desecrate_altar();
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ act_ignore_altar();
+ return;
+ };
+ }
+}
+
+/*
+ subroutine to process a throne object
+ */
+void
+othrone(int arg)
+{
+
+ lprcat("\nDo you (p) pry off jewels, (s) sit down");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 'p':
+ lprcat(" pry off");
+ act_remove_gems(arg);
+ return;
+
+ case 's':
+ lprcat(" sit down");
+ act_sit_throne(arg);
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ return;
+ };
+ }
+}
+
+void
+odeadthrone(void)
+{
+ int k;
+
+ lprcat("\nDo you (s) sit down");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 's':
+ lprcat(" sit down");
+ k = rnd(101);
+ if (k < 35) {
+ lprcat("\nZaaaappp! You've been teleported!\n");
+ beep();
+ oteleport(0);
+ } else
+ lprcat("\nnothing happens");
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ return;
+ };
+ }
+}
+
+/*
+ subroutine to process a throne object
+ */
+void
+ochest(void)
+{
+
+ lprcat("\nDo you (t) take it, (o) try to open it");
+ iopts();
+ while (1) {
+ while (1)
+ switch (ttgetch()) {
+ case 'o':
+ lprcat(" open it");
+ act_open_chest(playerx, playery);
+ return;
+
+ case 't':
+ lprcat(" take");
+ if (take(OCHEST, iarg[playerx][playery]) == 0)
+ item[playerx][playery] = know[playerx][playery] = 0;
+ return;
+
+ case 'i':
+ case '\33':
+ ignore();
+ return;
+ };
+ }
+}
+
+/*
+ process a fountain object
+ */
+void
+ofountain(void)
+{
+
+ cursors();
+ lprcat("\nDo you (d) drink, (w) wash yourself");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case 'd':
+ lprcat("drink");
+ act_drink_fountain();
+ return;
+
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'w':
+ lprcat("wash yourself");
+ act_wash_fountain();
+ return;
+ }
+}
+
+/*
+ ***
+ FCH
+ ***
+
+ subroutine to process an up/down of a character attribute for ofountain
+ */
+static void
+fch(int how, long *x)
+{
+ if (how < 0) {
+ lprcat(" went down by one!");
+ --(*x);
+ } else {
+ lprcat(" went up by one!");
+ (*x)++;
+ }
+ bottomline();
+}
+
+/*
+ a subroutine to raise or lower character levels
+ if x > 0 they are raised if x < 0 they are lowered
+ */
+void
+fntchange(int how)
+{
+ long j;
+ lprc('\n');
+ switch (rnd(9)) {
+ case 1:
+ lprcat("Your strength");
+ fch(how, &c[0]);
+ break;
+ case 2:
+ lprcat("Your intelligence");
+ fch(how, &c[1]);
+ break;
+ case 3:
+ lprcat("Your wisdom");
+ fch(how, &c[2]);
+ break;
+ case 4:
+ lprcat("Your constitution");
+ fch(how, &c[3]);
+ break;
+ case 5:
+ lprcat("Your dexterity");
+ fch(how, &c[4]);
+ break;
+ case 6:
+ lprcat("Your charm");
+ fch(how, &c[5]);
+ break;
+ case 7:
+ j = rnd(level + 1);
+ if (how < 0) {
+ lprintf("You lose %ld hit point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ losemhp((int) j);
+ } else {
+ lprintf("You gain %ld hit point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ raisemhp((int) j);
+ }
+ bottomline();
+ break;
+
+ case 8:
+ j = rnd(level + 1);
+ if (how > 0) {
+ lprintf("You just gained %ld spell", (long) j);
+ raisemspells((int) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ } else {
+ lprintf("You just lost %ld spell", (long) j);
+ losemspells((int) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ }
+ bottomline();
+ break;
+
+ case 9:
+ j = 5 * rnd((level + 1) * (level + 1));
+ if (how < 0) {
+ lprintf("You just lost %ld experience point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ loseexperience((long) j);
+ } else {
+ lprintf("You just gained %ld experience point", (long) j);
+ if (j > 1)
+ lprcat("s!");
+ else
+ lprc('!');
+ raiseexperience((long) j);
+ }
+ break;
+ }
+ cursors();
+}
diff --git a/larn/movem.c b/larn/movem.c
new file mode 100644
index 0000000..b9745fc
--- /dev/null
+++ b/larn/movem.c
@@ -0,0 +1,447 @@
+/* $NetBSD: movem.c,v 1.9 2012/06/19 05:30:43 dholland Exp $ */
+
+/*
+ * movem.c (move monster) Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Here are the functions in this file:
+ *
+ * movemonst() Routine to move the monsters toward the player
+ * movemt(x,y) Function to move a monster at (x,y) -- must determine where
+ * mmove(x,y,xd,yd) Function to actually perform the monster movement
+ * movsphere() Function to look for and move spheres of annihilation
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: movem.c,v 1.9 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+
+static void movemt(int, int);
+static void mmove(int, int, int, int);
+static void movsphere(void);
+
+/*
+ * movemonst() Routine to move the monsters toward the player
+ *
+ * This routine has the responsibility to determine which monsters are to
+ * move, and call movemt() to do the move.
+ * Returns no value.
+ */
+static short w1[9], w1x[9], w1y[9];
+static int tmp1, tmp2, tmp3, tmp4, distance;
+void
+movemonst(void)
+{
+ int i, j;
+ if (c[TIMESTOP])
+ return; /* no action if time is stopped */
+ if (c[HASTESELF])
+ if ((c[HASTESELF] & 1) == 0)
+ return;
+ if (spheres)
+ movsphere(); /* move the spheres of annihilation if any */
+ if (c[HOLDMONST])
+ return; /* no action if monsters are held */
+
+ if (c[AGGRAVATE]) { /* determine window of monsters to move */
+ tmp1 = playery - 5;
+ tmp2 = playery + 6;
+ tmp3 = playerx - 10;
+ tmp4 = playerx + 11;
+ distance = 40; /* depth of intelligent monster movement */
+ } else {
+ tmp1 = playery - 3;
+ tmp2 = playery + 4;
+ tmp3 = playerx - 5;
+ tmp4 = playerx + 6;
+ distance = 17; /* depth of intelligent monster movement */
+ }
+
+ if (level == 0) { /* if on outside level monsters can move in
+ * perimeter */
+ if (tmp1 < 0)
+ tmp1 = 0;
+ if (tmp2 > MAXY)
+ tmp2 = MAXY;
+ if (tmp3 < 0)
+ tmp3 = 0;
+ if (tmp4 > MAXX)
+ tmp4 = MAXX;
+ } else { /* if in a dungeon monsters can't be on the
+ * perimeter (wall there) */
+ if (tmp1 < 1)
+ tmp1 = 1;
+ if (tmp2 > MAXY - 1)
+ tmp2 = MAXY - 1;
+ if (tmp3 < 1)
+ tmp3 = 1;
+ if (tmp4 > MAXX - 1)
+ tmp4 = MAXX - 1;
+ }
+
+ for (j = tmp1; j < tmp2; j++) /* now reset monster moved flags */
+ for (i = tmp3; i < tmp4; i++)
+ moved[i][j] = 0;
+ moved[lasthx][lasthy] = 0;
+
+ if (c[AGGRAVATE] || !c[STEALTH]) { /* who gets moved? split for
+ * efficiency */
+ for (j = tmp1; j < tmp2; j++) /* look thru all locations in
+ * window */
+ for (i = tmp3; i < tmp4; i++)
+ if (mitem[i][j]) /* if there is a monster
+ * to move */
+ if (moved[i][j] == 0) /* if it has not already
+ * been moved */
+ movemt(i, j); /* go and move the
+ * monster */
+ } else { /* not aggravated and not stealth */
+ for (j = tmp1; j < tmp2; j++) /* look thru all locations in
+ * window */
+ for (i = tmp3; i < tmp4; i++)
+ if (mitem[i][j]) /* if there is a monster
+ * to move */
+ if (moved[i][j] == 0) /* if it has not already
+ * been moved */
+ if (stealth[i][j]) /* if it is asleep due
+ * to stealth */
+ movemt(i, j); /* go and move the
+ * monster */
+ }
+
+ if (mitem[lasthx][lasthy]) { /* now move monster last hit by
+ * player if not already moved */
+ if (moved[lasthx][lasthy] == 0) { /* if it has not already
+ * been moved */
+ movemt(lasthx, lasthy);
+ lasthx = w1x[0];
+ lasthy = w1y[0];
+ }
+ }
+}
+
+/*
+ * movemt(x,y) Function to move a monster at (x,y) -- must determine where
+ * int x,y;
+ *
+ * This routine is responsible for determining where one monster at (x,y) will
+ * move to. Enter with the monsters coordinates in (x,y).
+ * Returns no value.
+ */
+static int tmpitem, xl, xh, yl, yh;
+static void
+movemt(int i, int j)
+{
+ int k, m, z, tmp, xtmp, ytmp, monst;
+ switch (monst = mitem[i][j]) { /* for half speed monsters */
+ case TROGLODYTE:
+ case HOBGOBLIN:
+ case METAMORPH:
+ case XVART:
+ case INVISIBLESTALKER:
+ case ICELIZARD:
+ if ((gltime & 1) == 1)
+ return;
+ };
+
+ if (c[SCAREMONST]) { /* choose destination randomly if scared */
+ if ((xl = i + rnd(3) - 2) < 0)
+ xl = 0;
+ if (xl >= MAXX)
+ xl = MAXX - 1;
+ if ((yl = j + rnd(3) - 2) < 0)
+ yl = 0;
+ if (yl >= MAXY)
+ yl = MAXY - 1;
+ if ((tmp = item[xl][yl]) != OWALL)
+ if (mitem[xl][yl] == 0)
+ if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
+ if (tmp != OCLOSEDDOOR)
+ mmove(i, j, xl, yl);
+ return;
+ }
+ if (monster[monst].intelligence > 10 - c[HARDGAME]) { /* if smart monster */
+ /* intelligent movement here -- first setup screen array */
+ xl = tmp3 - 2;
+ yl = tmp1 - 2;
+ xh = tmp4 + 2;
+ yh = tmp2 + 2;
+ vxy(&xl, &yl);
+ vxy(&xh, &yh);
+ for (k = yl; k < yh; k++)
+ for (m = xl; m < xh; m++) {
+ switch (item[m][k]) {
+ case OWALL:
+ case OPIT:
+ case OTRAPARROW:
+ case ODARTRAP:
+ case OCLOSEDDOOR:
+ case OTRAPDOOR:
+ case OTELEPORTER:
+ smm: screen[m][k] = 127;
+ break;
+ case OMIRROR:
+ if (mitem[m][k] == VAMPIRE)
+ goto smm;
+ default:
+ screen[m][k] = 0;
+ break;
+ };
+ }
+ screen[playerx][playery] = 1;
+
+ /*
+ * now perform proximity ripple from playerx,playery to
+ * monster
+ */
+ xl = tmp3 - 1;
+ yl = tmp1 - 1;
+ xh = tmp4 + 1;
+ yh = tmp2 + 1;
+ vxy(&xl, &yl);
+ vxy(&xh, &yh);
+ for (tmp = 1; tmp < distance; tmp++) /* only up to 20 squares
+ * away */
+ for (k = yl; k < yh; k++)
+ for (m = xl; m < xh; m++)
+ if (screen[m][k] == tmp) /* if find proximity n
+ * advance it */
+ for (z = 1; z < 9; z++) { /* go around in a circle */
+ if (screen[xtmp = m + diroffx[z]][ytmp = k + diroffy[z]] == 0)
+ screen[xtmp][ytmp] = tmp + 1;
+ if (xtmp == i && ytmp == j)
+ goto out;
+ }
+
+out: if (tmp < distance) /* did find connectivity */
+ /* now select lowest value around playerx,playery */
+ for (z = 1; z < 9; z++) /* go around in a circle */
+ if (screen[xl = i + diroffx[z]][yl = j + diroffy[z]] == tmp)
+ if (!mitem[xl][yl]) {
+ mmove(i, j, w1x[0] = xl, w1y[0] = yl);
+ return;
+ }
+ }
+ /* dumb monsters move here */
+ xl = i - 1;
+ yl = j - 1;
+ xh = i + 2;
+ yh = j + 2;
+ if (i < playerx)
+ xl++;
+ else if (i > playerx)
+ --xh;
+ if (j < playery)
+ yl++;
+ else if (j > playery)
+ --yh;
+ for (k = 0; k < 9; k++)
+ w1[k] = 10000;
+
+ for (k = xl; k < xh; k++)
+ for (m = yl; m < yh; m++) { /* for each square compute
+ * distance to player */
+ tmp = k - i + 4 + 3 * (m - j);
+ tmpitem = item[k][m];
+ if (tmpitem != OWALL || (k == playerx && m == playery))
+ if (mitem[k][m] == 0)
+ if ((mitem[i][j] != VAMPIRE) || (tmpitem != OMIRROR))
+ if (tmpitem != OCLOSEDDOOR) {
+ w1[tmp] = (playerx - k) * (playerx - k) + (playery - m) * (playery - m);
+ w1x[tmp] = k;
+ w1y[tmp] = m;
+ }
+ }
+
+ tmp = 0;
+ for (k = 1; k < 9; k++)
+ if (w1[tmp] > w1[k])
+ tmp = k;
+
+ if (w1[tmp] < 10000)
+ if ((i != w1x[tmp]) || (j != w1y[tmp]))
+ mmove(i, j, w1x[tmp], w1y[tmp]);
+}
+
+/*
+ * mmove(x,y,xd,yd) Function to actually perform the monster movement
+ * int x,y,xd,yd;
+ *
+ * Enter with the from coordinates in (x,y) and the destination coordinates
+ * in (xd,yd).
+ */
+static void
+mmove(int aa, int bb, int cc, int dd)
+{
+ int tmp, i, flag;
+ const char *who = NULL;
+
+ flag = 0; /* set to 1 if monster hit by arrow trap */
+ if ((cc == playerx) && (dd == playery)) {
+ hitplayer(aa, bb);
+ moved[aa][bb] = 1;
+ return;
+ }
+ i = item[cc][dd];
+ if ((i == OPIT) || (i == OTRAPDOOR))
+ switch (mitem[aa][bb]) {
+ case SPIRITNAGA:
+ case PLATINUMDRAGON:
+ case WRAITH:
+ case VAMPIRE:
+ case SILVERDRAGON:
+ case POLTERGEIST:
+ case DEMONLORD:
+ case DEMONLORD + 1:
+ case DEMONLORD + 2:
+ case DEMONLORD + 3:
+ case DEMONLORD + 4:
+ case DEMONLORD + 5:
+ case DEMONLORD + 6:
+ case DEMONPRINCE:
+ break;
+
+ default:
+ mitem[aa][bb] = 0; /* fell in a pit or trapdoor */
+ };
+ tmp = mitem[cc][dd] = mitem[aa][bb];
+ if (i == OANNIHILATION) {
+ if (tmp >= DEMONLORD + 3) { /* demons dispel spheres */
+ cursors();
+ lprintf("\nThe %s dispels the sphere!", monster[tmp].name);
+ rmsphere(cc, dd); /* delete the sphere */
+ } else
+ i = tmp = mitem[cc][dd] = 0;
+ }
+ stealth[cc][dd] = 1;
+ if ((hitp[cc][dd] = hitp[aa][bb]) < 0)
+ hitp[cc][dd] = 1;
+ mitem[aa][bb] = 0;
+ moved[cc][dd] = 1;
+ if (tmp == LEPRECHAUN)
+ switch (i) {
+ case OGOLDPILE:
+ case OMAXGOLD:
+ case OKGOLD:
+ case ODGOLD:
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ item[cc][dd] = 0; /* leprechaun takes gold */
+ };
+
+ if (tmp == TROLL) /* if a troll regenerate him */
+ if ((gltime & 1) == 0)
+ if (monster[tmp].hitpoints > hitp[cc][dd])
+ hitp[cc][dd]++;
+
+ if (i == OTRAPARROW) { /* arrow hits monster */
+ who = "An arrow";
+ if ((hitp[cc][dd] -= rnd(10) + level) <= 0) {
+ mitem[cc][dd] = 0;
+ flag = 2;
+ } else
+ flag = 1;
+ }
+ if (i == ODARTRAP) { /* dart hits monster */
+ who = "A dart";
+ if ((hitp[cc][dd] -= rnd(6)) <= 0) {
+ mitem[cc][dd] = 0;
+ flag = 2;
+ } else
+ flag = 1;
+ }
+ if (i == OTELEPORTER) { /* monster hits teleport trap */
+ flag = 3;
+ fillmonst(mitem[cc][dd]);
+ mitem[cc][dd] = 0;
+ }
+ if (c[BLINDCOUNT])
+ return; /* if blind don't show where monsters are */
+ if (know[cc][dd] & 1) {
+ if (flag)
+ cursors();
+ switch (flag) {
+ case 1:
+ lprintf("\n%s hits the %s", who, monster[tmp].name);
+ beep();
+ break;
+ case 2:
+ lprintf("\n%s hits and kills the %s",
+ who, monster[tmp].name);
+ beep();
+ break;
+ case 3:
+ lprintf("\nThe %s gets teleported", monster[tmp].name);
+ beep();
+ break;
+ }
+ }
+ /*
+ * if (yrepcount>1) { know[aa][bb] &= 2; know[cc][dd] &= 2; return;
+ * }
+ */
+ if (know[aa][bb] & 1)
+ show1cell(aa, bb);
+ if (know[cc][dd] & 1)
+ show1cell(cc, dd);
+}
+
+/*
+ * movsphere() Function to look for and move spheres of annihilation
+ *
+ * This function works on the sphere linked list, first duplicating the list
+ * (the act of moving changes the list), then processing each sphere in order
+ * to move it. They eat anything in their way, including stairs, volcanic
+ * shafts, potions, etc, except for upper level demons, who can dispel
+ * spheres.
+ * No value is returned.
+ */
+#define SPHMAX 20 /* maximum number of spheres movsphere can
+ * handle */
+static void
+movsphere(void)
+{
+ int x, y, dir, len;
+ struct sphere *sp, *sp2;
+ struct sphere sph[SPHMAX];
+
+ /* first duplicate sphere list */
+ for (sp = 0, x = 0, sp2 = spheres; sp2; sp2 = sp2->p) /* look through sphere
+ * list */
+ if (sp2->lev == level) { /* only if this level */
+ sph[x] = *sp2;
+ sph[x++].p = 0; /* copy the struct */
+ if (x > 1)
+ sph[x - 2].p = &sph[x - 1]; /* link pointers */
+ }
+ if (x)
+ sp = sph; /* if any spheres, point to them */
+ else
+ return; /* no spheres */
+
+ for (sp = sph; sp; sp = sp->p) { /* look through sphere list */
+ x = sp->x;
+ y = sp->y;
+ if (item[x][y] != OANNIHILATION)
+ continue; /* not really there */
+ if (--(sp->lifetime) < 0) { /* has sphere run out of gas? */
+ rmsphere(x, y); /* delete sphere */
+ continue;
+ }
+ switch (rnd((int) max(7, c[INTELLIGENCE] >> 1))) { /* time to move the
+ * sphere */
+ case 1:
+ case 2: /* change direction to a random one */
+ sp->dir = rnd(8);
+ default: /* move in normal direction */
+ dir = sp->dir;
+ len = sp->lifetime;
+ rmsphere(x, y);
+ newsphere(x + diroffx[dir], y + diroffy[dir], dir, len);
+ };
+ }
+}
diff --git a/larn/nap.c b/larn/nap.c
new file mode 100644
index 0000000..1bd11ea
--- /dev/null
+++ b/larn/nap.c
@@ -0,0 +1,23 @@
+/* $NetBSD: nap.c,v 1.6 2012/06/19 05:30:43 dholland Exp $ */
+
+/* nap.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: nap.c,v 1.6 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+/*
+ * routine to take a nap for n milliseconds
+ */
+void
+nap(int x)
+{
+ if (x <= 0)
+ return; /* eliminate chance for infinite loop */
+ lflush();
+ usleep(x * 1000);
+}
diff --git a/larn/object.c b/larn/object.c
new file mode 100644
index 0000000..a8e1449
--- /dev/null
+++ b/larn/object.c
@@ -0,0 +1,1336 @@
+/* $NetBSD: object.c,v 1.16 2012/06/19 05:30:43 dholland Exp $ */
+
+/* object.c Larn is copyrighted 1986 by Noah Morgan. */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: object.c,v 1.16 2012/06/19 05:30:43 dholland Exp $");
+#endif /* not lint */
+#include "header.h"
+#include "extern.h"
+
+static void finditem(int);
+static void ostairs(int);
+static void opotion(int);
+static void oscroll(int);
+static void oorb(void);
+static void opit(void);
+static void obottomless(void);
+static void oelevator(int);
+static void ostatue(void);
+static void omirror(void);
+static void obook(void);
+static void ocookie(void);
+static void ogold(int);
+static void ohome(void);
+
+/*
+ lookforobject
+
+ subroutine to look for an object and give the player his options
+ if an object was found.
+ */
+void
+lookforobject(void)
+{
+ int i, j;
+ if (c[TIMESTOP])
+ return; /* can't find objects if time is stopped */
+ i = item[playerx][playery];
+ if (i == 0)
+ return;
+ showcell(playerx, playery);
+ cursors();
+ yrepcount = 0;
+ switch (i) {
+ case OGOLDPILE:
+ case OMAXGOLD:
+ case OKGOLD:
+ case ODGOLD:
+ lprcat("\n\nYou have found some gold!");
+ ogold(i);
+ break;
+
+ case OPOTION:
+ lprcat("\n\nYou have found a magic potion");
+ i = iarg[playerx][playery];
+ if (potionname[i][0] != 0)
+ lprintf(" of%s", potionname[i]);
+ opotion(i);
+ break;
+
+ case OSCROLL:
+ lprcat("\n\nYou have found a magic scroll");
+ i = iarg[playerx][playery];
+ if (scrollname[i][0] != 0)
+ lprintf(" of%s", scrollname[i]);
+ oscroll(i);
+ break;
+
+ case OALTAR:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a Holy Altar here!");
+ oaltar();
+ break;
+
+ case OBOOK:
+ lprcat("\n\nYou have found a book.");
+ obook();
+ break;
+
+ case OCOOKIE:
+ lprcat("\n\nYou have found a fortune cookie.");
+ ocookie();
+ break;
+
+ case OTHRONE:
+ if (nearbymonst())
+ return;
+ lprintf("\n\nThere is %s here!", objectname[i]);
+ othrone(0);
+ break;
+
+ case OTHRONE2:
+ if (nearbymonst())
+ return;
+ lprintf("\n\nThere is %s here!", objectname[i]);
+ othrone(1);
+ break;
+
+ case ODEADTHRONE:
+ lprintf("\n\nThere is %s here!", objectname[i]);
+ odeadthrone();
+ break;
+
+ case OORB:
+ lprcat("\n\nYou have found the Orb!!!!!");
+ oorb();
+ break;
+
+ case OPIT:
+ lprcat("\n\nYou're standing at the top of a pit.");
+ opit();
+ break;
+
+ case OSTAIRSUP:
+ lprcat("\n\nThere is a circular staircase here");
+ ostairs(1); /* up */
+ break;
+
+ case OELEVATORUP:
+ lprcat("\n\nYou feel heavy for a moment, but the feeling disappears");
+ oelevator(1); /* up */
+ break;
+
+ case OFOUNTAIN:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a fountain here");
+ ofountain();
+ break;
+
+ case OSTATUE:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nYou are standing in front of a statue");
+ ostatue();
+ break;
+
+ case OCHEST:
+ lprcat("\n\nThere is a chest here");
+ ochest();
+ break;
+
+ case OIVTELETRAP:
+ if (rnd(11) < 6)
+ return;
+ item[playerx][playery] = OTELEPORTER;
+ know[playerx][playery] = 1;
+
+ case OTELEPORTER:
+ lprcat("\nZaaaappp! You've been teleported!\n");
+ beep();
+ nap(3000);
+ oteleport(0);
+ break;
+
+ case OSCHOOL:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nYou have found the College of Larn.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g') {
+ oschool(); /* the college of larn */
+ } else
+ lprcat(" stay here");
+ break;
+
+ case OMIRROR:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a mirror here");
+ omirror();
+ break;
+
+ case OBANK2:
+ case OBANK:
+ if (nearbymonst())
+ return;
+ if (i == OBANK)
+ lprcat("\n\nYou have found the bank of Larn.");
+ else
+ lprcat("\n\nYou have found a branch office of the bank of Larn.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ j = 0;
+ while ((j != 'g') && (j != 'i') && (j != '\33'))
+ j = ttgetch();
+ if (j == 'g') {
+ if (i == OBANK)
+ obank();
+ else
+ obank2(); /* the bank of larn */
+ } else
+ lprcat(" stay here");
+ break;
+
+ case ODEADFOUNTAIN:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a dead fountain here");
+ break;
+
+ case ODNDSTORE:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is a DND store here.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ dndstore(); /* the dnd adventurers store */
+ else
+ lprcat(" stay here");
+ break;
+
+ case OSTAIRSDOWN:
+ lprcat("\n\nThere is a circular staircase here");
+ ostairs(-1); /* down */
+ break;
+
+ case OELEVATORDOWN:
+ lprcat("\n\nYou feel light for a moment, but the feeling disappears");
+ oelevator(-1); /* down */
+ break;
+
+ case OOPENDOOR:
+ lprintf("\n\nYou have found %s", objectname[i]);
+ lprcat("\nDo you (c) close it");
+ iopts();
+ i = 0;
+ while ((i != 'c') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ break;
+ }
+ lprcat("close");
+ forget();
+ item[playerx][playery] = OCLOSEDDOOR;
+ iarg[playerx][playery] = 0;
+ playerx = lastpx;
+ playery = lastpy;
+ break;
+
+ case OCLOSEDDOOR:
+ lprintf("\n\nYou have found %s", objectname[i]);
+ lprcat("\nDo you (o) try to open it");
+ iopts();
+ i = 0;
+ while ((i != 'o') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ playerx = lastpx;
+ playery = lastpy;
+ break;
+ } else {
+ lprcat("open");
+ if (rnd(11) < 7) {
+ switch (iarg[playerx][playery]) {
+ case 6:
+ c[AGGRAVATE] += rnd(400);
+ break;
+
+ case 7:
+ lprcat("\nYou are jolted by an electric shock ");
+ lastnum = 274;
+ losehp(rnd(20));
+ bottomline();
+ break;
+
+ case 8:
+ loselevel();
+ break;
+
+ case 9:
+ lprcat("\nYou suddenly feel weaker ");
+ if (c[STRENGTH] > 3)
+ c[STRENGTH]--;
+ bottomline();
+ break;
+
+ default:
+ break;
+ }
+ playerx = lastpx;
+ playery = lastpy;
+ } else {
+ forget();
+ item[playerx][playery] = OOPENDOOR;
+ }
+ }
+ break;
+
+ case OENTRANCE:
+ lprcat("\nYou have found ");
+ lprcat(objectname[OENTRANCE]);
+ lprcat("\nDo you (g) go inside");
+ iopts();
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g') {
+ newcavelevel(1);
+ playerx = 33;
+ playery = MAXY - 2;
+ item[33][MAXY - 1] = know[33][MAXY - 1] = mitem[33][MAXY - 1] = 0;
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+ } else
+ ignore();
+ break;
+
+ case OVOLDOWN:
+ lprcat("\nYou have found ");
+ lprcat(objectname[OVOLDOWN]);
+ lprcat("\nDo you (c) climb down");
+ iopts();
+ i = 0;
+ while ((i != 'c') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ break;
+ }
+ if (level != 0) {
+ lprcat("\nThe shaft only extends 5 feet downward!");
+ return;
+ }
+ if (packweight() > 45 + 3 * (c[STRENGTH] + c[STREXTRA])) {
+ lprcat("\nYou slip and fall down the shaft");
+ beep();
+ lastnum = 275;
+ losehp(30 + rnd(20));
+ bottomhp();
+ } else
+ lprcat("climb down");
+ nap(3000);
+ newcavelevel(MAXLEVEL);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++) /* put player near
+ * volcano shaft */
+ if (item[j][i] == OVOLUP) {
+ playerx = j;
+ playery = i;
+ j = MAXX;
+ i = MAXY;
+ positionplayer();
+ }
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+
+ case OVOLUP:
+ lprcat("\nYou have found ");
+ lprcat(objectname[OVOLUP]);
+ lprcat("\nDo you (c) climb up");
+ iopts();
+ i = 0;
+ while ((i != 'c') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if ((i == '\33') || (i == 'i')) {
+ ignore();
+ break;
+ }
+ if (level != 11) {
+ lprcat("\nThe shaft only extends 8 feet upwards before you find a blockage!");
+ return;
+ }
+ if (packweight() > 45 + 5 * (c[STRENGTH] + c[STREXTRA])) {
+ lprcat("\nYou slip and fall down the shaft");
+ beep();
+ lastnum = 275;
+ losehp(15 + rnd(20));
+ bottomhp();
+ return;
+ }
+ lprcat("climb up");
+ lflush();
+ nap(3000);
+ newcavelevel(0);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++) /* put player near
+ * volcano shaft */
+ if (item[j][i] == OVOLDOWN) {
+ playerx = j;
+ playery = i;
+ j = MAXX;
+ i = MAXY;
+ positionplayer();
+ }
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+
+ case OTRAPARROWIV:
+ if (rnd(17) < 13)
+ return; /* for an arrow trap */
+ item[playerx][playery] = OTRAPARROW;
+ know[playerx][playery] = 0;
+
+ case OTRAPARROW:
+ lprcat("\nYou are hit by an arrow");
+ beep(); /* for an arrow trap */
+ lastnum = 259;
+ losehp(rnd(10) + level);
+ bottomhp();
+ return;
+
+ case OIVDARTRAP:
+ if (rnd(17) < 13)
+ return; /* for a dart trap */
+ item[playerx][playery] = ODARTRAP;
+ know[playerx][playery] = 0;
+
+ case ODARTRAP:
+ lprcat("\nYou are hit by a dart");
+ beep(); /* for a dart trap */
+ lastnum = 260;
+ losehp(rnd(5));
+ if ((--c[STRENGTH]) < 3)
+ c[STRENGTH] = 3;
+ bottomline();
+ return;
+
+ case OIVTRAPDOOR:
+ if (rnd(17) < 13)
+ return; /* for a trap door */
+ item[playerx][playery] = OTRAPDOOR;
+ know[playerx][playery] = 1;
+
+ case OTRAPDOOR:
+ lastnum = 272; /* a trap door */
+ if ((level == MAXLEVEL - 1) || (level == MAXLEVEL + MAXVLEVEL - 1)) {
+ lprcat("\nYou fell through a bottomless trap door!");
+ beep();
+ nap(3000);
+ died(271);
+ }
+ lprcat("\nYou fall through a trap door!");
+ beep(); /* for a trap door */
+ losehp(rnd(5 + level));
+ nap(2000);
+ newcavelevel(level + 1);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ return;
+
+
+ case OTRADEPOST:
+ if (nearbymonst())
+ return;
+ lprcat("\nYou have found the Larn trading Post.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ otradepost();
+ else
+ lprcat("stay here");
+ return;
+
+ case OHOME:
+ if (nearbymonst())
+ return;
+ lprcat("\nYou have found your way home.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ ohome();
+ else
+ lprcat("stay here");
+ return;
+
+ case OWALL:
+ break;
+
+ case OANNIHILATION:
+ died(283);
+ return; /* annihilated by sphere of annihilation */
+
+ case OLRS:
+ if (nearbymonst())
+ return;
+ lprcat("\n\nThere is an LRS office here.");
+ lprcat("\nDo you (g) go inside, or (i) stay here? ");
+ i = 0;
+ while ((i != 'g') && (i != 'i') && (i != '\33'))
+ i = ttgetch();
+ if (i == 'g')
+ olrs(); /* the larn revenue service */
+ else
+ lprcat(" stay here");
+ break;
+
+ default:
+ finditem(i);
+ break;
+ };
+}
+
+/*
+ function to say what object we found and ask if player wants to take it
+ */
+static void
+finditem(int theitem)
+{
+ int tmp, i;
+ lprintf("\n\nYou have found %s ", objectname[theitem]);
+ tmp = iarg[playerx][playery];
+ switch (theitem) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ case OSPIRITSCARAB:
+ case OORBOFDRAGON:
+ case OCUBEofUNDEAD:
+ case ONOTHEFT:
+ break;
+
+ default:
+ if (tmp > 0)
+ lprintf("+ %ld", (long) tmp);
+ else if (tmp < 0)
+ lprintf(" %ld", (long) tmp);
+ }
+ lprcat("\nDo you want to (t) take it");
+ iopts();
+ i = 0;
+ while (i != 't' && i != 'i' && i != '\33')
+ i = ttgetch();
+ if (i == 't') {
+ lprcat("take");
+ if (take(theitem, tmp) == 0)
+ forget();
+ return;
+ }
+ ignore();
+}
+
+
+
+/*
+ subroutine to process the stair cases
+ if dir > 0 the up else down
+ */
+static void
+ostairs(int dir)
+{
+ int k;
+ lprcat("\nDo you (s) stay here ");
+ if (dir > 0)
+ lprcat("(u) go up ");
+ else
+ lprcat("(d) go down ");
+ lprcat("or (f) kick stairs? ");
+
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 's':
+ case 'i':
+ lprcat("stay here");
+ return;
+
+ case 'f':
+ lprcat("kick stairs");
+ if (rnd(2) == 1)
+ lprcat("\nI hope you feel better. Showing anger rids you of frustration.");
+ else {
+ k = rnd((level + 1) << 1);
+ lprintf("\nYou hurt your foot dumb dumb! You suffer %ld hit points", (long) k);
+ lastnum = 276;
+ losehp(k);
+ bottomline();
+ }
+ return;
+
+ case 'u':
+ lprcat("go up");
+ if (dir < 0)
+ lprcat("\nThe stairs don't go up!");
+ else if (level >= 2 && level != 11) {
+ k = level;
+ newcavelevel(level - 1);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ } else
+ lprcat("\nThe stairs lead to a dead end!");
+ return;
+
+ case 'd':
+ lprcat("go down");
+ if (dir > 0)
+ lprcat("\nThe stairs don't go down!");
+ else if (level != 0 && level != 10 && level != 13) {
+ k = level;
+ newcavelevel(level + 1);
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+ } else
+ lprcat("\nThe stairs lead to a dead end!");
+ return;
+ };
+}
+
+
+
+/*
+ subroutine to handle a teleport trap +/- 1 level maximum
+ */
+void
+oteleport(int err)
+{
+ int tmp;
+ if (err)
+ if (rnd(151) < 3)
+ died(264); /* stuck in a rock */
+ c[TELEFLAG] = 1; /* show ?? on bottomline if been teleported */
+ if (level == 0)
+ tmp = 0;
+ else if (level < MAXLEVEL) {
+ tmp = rnd(5) + level - 3;
+ if (tmp >= MAXLEVEL)
+ tmp = MAXLEVEL - 1;
+ if (tmp < 1)
+ tmp = 1;
+ } else {
+ tmp = rnd(3) + level - 2;
+ if (tmp >= MAXLEVEL + MAXVLEVEL)
+ tmp = MAXLEVEL + MAXVLEVEL - 1;
+ if (tmp < MAXLEVEL)
+ tmp = MAXLEVEL;
+ }
+ playerx = rnd(MAXX - 2);
+ playery = rnd(MAXY - 2);
+ if (level != tmp)
+ newcavelevel(tmp);
+ positionplayer();
+ draws(0, MAXX, 0, MAXY);
+ bot_linex();
+}
+
+
+/*
+ function to process a potion
+ */
+static void
+opotion(int pot)
+{
+ lprcat("\nDo you (d) drink it, (t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'd':
+ lprcat("drink\n");
+ forget(); /* destroy potion */
+ quaffpotion(pot);
+ return;
+
+ case 't':
+ lprcat("take\n");
+ if (take(OPOTION, pot) == 0)
+ forget();
+ return;
+ };
+}
+
+/*
+ function to drink a potion
+ */
+void
+quaffpotion(int pot)
+{
+ int i, j, k;
+ if (pot < 0 || pot >= MAXPOTION)
+ return; /* check for within bounds */
+ potionname[pot] = potionhide[pot];
+ switch (pot) {
+ case 9:
+ lprcat("\nYou feel greedy . . .");
+ nap(2000);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ if ((item[j][i] == OGOLDPILE) || (item[j][i] == OMAXGOLD)) {
+ know[j][i] = 1;
+ show1cell(j, i);
+ }
+ showplayer();
+ return;
+
+ case 19:
+ lprcat("\nYou feel greedy . . .");
+ nap(2000);
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++) {
+ k = item[j][i];
+ if ((k == ODIAMOND) || (k == ORUBY) || (k == OEMERALD) || (k == OMAXGOLD)
+ || (k == OSAPPHIRE) || (k == OLARNEYE) || (k == OGOLDPILE)) {
+ know[j][i] = 1;
+ show1cell(j, i);
+ }
+ }
+ showplayer();
+ return;
+
+ case 20:
+ c[HP] = c[HPMAX];
+ break; /* instant healing */
+
+ case 1:
+ lprcat("\nYou feel better");
+ if (c[HP] == c[HPMAX])
+ raisemhp(1);
+ else if ((c[HP] += rnd(20) + 20 + c[LEVEL]) > c[HPMAX])
+ c[HP] = c[HPMAX];
+ break;
+
+ case 2:
+ lprcat("\nSuddenly, you feel much more skillful!");
+ raiselevel();
+ raisemhp(1);
+ return;
+
+ case 3:
+ lprcat("\nYou feel strange for a moment");
+ c[rund(6)]++;
+ break;
+
+ case 4:
+ lprcat("\nYou feel more self confident!");
+ c[WISDOM] += rnd(2);
+ break;
+
+ case 5:
+ lprcat("\nWow! You feel great!");
+ if (c[STRENGTH] < 12)
+ c[STRENGTH] = 12;
+ else
+ c[STRENGTH]++;
+ break;
+
+ case 6:
+ lprcat("\nYour charm went up by one!");
+ c[CHARISMA]++;
+ break;
+
+ case 8:
+ lprcat("\nYour intelligence went up by one!");
+ c[INTELLIGENCE]++;
+ break;
+
+ case 10:
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ if (mitem[j][i]) {
+ know[j][i] = 1;
+ show1cell(j, i);
+ }
+ /* monster detection */ return;
+
+ case 12:
+ lprcat("\nThis potion has no taste to it");
+ return;
+
+ case 15:
+ lprcat("\nWOW!!! You feel Super-fantastic!!!");
+ if (c[HERO] == 0)
+ for (i = 0; i < 6; i++)
+ c[i] += 11;
+ c[HERO] += 250;
+ break;
+
+ case 16:
+ lprcat("\nYou have a greater intestinal constitude!");
+ c[CONSTITUTION]++;
+ break;
+
+ case 17:
+ lprcat("\nYou now have incredibly bulging muscles!!!");
+ if (c[GIANTSTR] == 0)
+ c[STREXTRA] += 21;
+ c[GIANTSTR] += 700;
+ break;
+
+ case 18:
+ lprcat("\nYou feel a chill run up your spine!");
+ c[FIRERESISTANCE] += 1000;
+ break;
+
+ case 0:
+ lprcat("\nYou fall asleep. . .");
+ i = rnd(11) - (c[CONSTITUTION] >> 2) + 2;
+ while (--i > 0) {
+ parse2();
+ nap(1000);
+ }
+ cursors();
+ lprcat("\nYou woke up!");
+ return;
+
+ case 7:
+ lprcat("\nYou become dizzy!");
+ if (--c[STRENGTH] < 3)
+ c[STRENGTH] = 3;
+ break;
+
+ case 11:
+ lprcat("\nYou stagger for a moment . .");
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = 0;
+ nap(2000);
+ draws(0, MAXX, 0, MAXY); /* potion of forgetfulness */
+ return;
+
+ case 13:
+ lprcat("\nYou can't see anything!"); /* blindness */
+ c[BLINDCOUNT] += 500;
+ return;
+
+ case 14:
+ lprcat("\nYou feel confused");
+ c[CONFUSE] += 20 + rnd(9);
+ return;
+
+ case 21:
+ lprcat("\nYou don't seem to be affected");
+ return; /* cure dianthroritis */
+
+ case 22:
+ lprcat("\nYou feel a sickness engulf you"); /* poison */
+ c[HALFDAM] += 200 + rnd(200);
+ return;
+
+ case 23:
+ lprcat("\nYou feel your vision sharpen"); /* see invisible */
+ c[SEEINVISIBLE] += rnd(1000) + 400;
+ monstnamelist[INVISIBLESTALKER] = 'I';
+ return;
+ };
+ bottomline(); /* show new stats */
+ return;
+}
+
+
+/*
+ function to process a magic scroll
+ */
+static void
+oscroll(int typ)
+{
+ lprcat("\nDo you ");
+ if (c[BLINDCOUNT] == 0)
+ lprcat("(r) read it, ");
+ lprcat("(t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'r':
+ if (c[BLINDCOUNT])
+ break;
+ lprcat("read");
+ forget();
+ if (typ == 2 || typ == 15) {
+ show1cell(playerx, playery);
+ cursors();
+ }
+ /* destroy it */ read_scroll(typ);
+ return;
+
+ case 't':
+ lprcat("take");
+ if (take(OSCROLL, typ) == 0)
+ forget(); /* destroy it */
+ return;
+ };
+}
+
+/*
+ data for the function to read a scroll
+ */
+static int xh, yh, yl, xl;
+static u_char curse[] = {
+ BLINDCOUNT, CONFUSE, AGGRAVATE, HASTEMONST, ITCHING,
+ LAUGHING, DRAINSTRENGTH, CLUMSINESS, INFEEBLEMENT, HALFDAM
+};
+
+static u_char exten[] = {
+ PROTECTIONTIME, DEXCOUNT, STRCOUNT, CHARMCOUNT, INVISIBILITY,
+ CANCELLATION, HASTESELF, GLOBE, SCAREMONST, HOLDMONST, TIMESTOP
+};
+
+static u_char time_change[] = {
+ HASTESELF, HERO, ALTPRO, PROTECTIONTIME, DEXCOUNT, STRCOUNT,
+ GIANTSTR, CHARMCOUNT, INVISIBILITY, CANCELLATION, HASTESELF,
+ AGGRAVATE, SCAREMONST, STEALTH, AWARENESS, HOLDMONST,
+ HASTEMONST, FIRERESISTANCE, GLOBE, SPIRITPRO, UNDEADPRO,
+ HALFDAM, SEEINVISIBLE, ITCHING, CLUMSINESS, WTW
+};
+
+/*
+ * function to adjust time when time warping and taking courses in school
+ */
+void
+adjusttime(long tim)
+{
+ int j;
+ for (j = 0; j < 26; j++)/* adjust time related parameters */
+ if (c[time_change[j]])
+ if ((c[time_change[j]] -= tim) < 1)
+ c[time_change[j]] = 1;
+ regen();
+}
+
+/*
+ function to read a scroll
+ */
+void
+read_scroll(int typ)
+{
+ int i, j;
+ if (typ < 0 || typ >= MAXSCROLL)
+ return; /* be sure we are within bounds */
+ scrollname[typ] = scrollhide[typ];
+ switch (typ) {
+ case 0:
+ lprcat("\nYour armor glows for a moment");
+ enchantarmor();
+ return;
+
+ case 1:
+ lprcat("\nYour weapon glows for a moment");
+ enchweapon();
+ return; /* enchant weapon */
+
+ case 2:
+ lprcat("\nYou have been granted enlightenment!");
+ yh = min(playery + 7, MAXY);
+ xh = min(playerx + 25, MAXX);
+ yl = max(playery - 7, 0);
+ xl = max(playerx - 25, 0);
+ for (i = yl; i < yh; i++)
+ for (j = xl; j < xh; j++)
+ know[j][i] = 1;
+ nap(2000);
+ draws(xl, xh, yl, yh);
+ return;
+
+ case 3:
+ lprcat("\nThis scroll seems to be blank");
+ return;
+
+ case 4:
+ createmonster(makemonst(level + 1));
+ return; /* this one creates a monster */
+
+ case 5:
+ something(level); /* create artifact */
+ return;
+
+ case 6:
+ c[AGGRAVATE] += 800;
+ return; /* aggravate monsters */
+
+ case 7:
+ gltime += (i = rnd(1000) - 850); /* time warp */
+ if (i >= 0)
+ lprintf("\nYou went forward in time by %ld mobuls", (long) ((i + 99) / 100));
+ else
+ lprintf("\nYou went backward in time by %ld mobuls", (long) (-(i + 99) / 100));
+ adjusttime((long) i); /* adjust time for time warping */
+ return;
+
+ case 8:
+ oteleport(0);
+ return; /* teleportation */
+
+ case 9:
+ c[AWARENESS] += 1800;
+ return; /* expanded awareness */
+
+ case 10:
+ c[HASTEMONST] += rnd(55) + 12;
+ return; /* haste monster */
+
+ case 11:
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ if (mitem[j][i])
+ hitp[j][i] = monster[mitem[j][i]].hitpoints;
+ return; /* monster healing */
+ case 12:
+ c[SPIRITPRO] += 300 + rnd(200);
+ bottomline();
+ return; /* spirit protection */
+
+ case 13:
+ c[UNDEADPRO] += 300 + rnd(200);
+ bottomline();
+ return; /* undead protection */
+
+ case 14:
+ c[STEALTH] += 250 + rnd(250);
+ bottomline();
+ return; /* stealth */
+
+ case 15:
+ lprcat("\nYou have been granted enlightenment!"); /* magic mapping */
+ for (i = 0; i < MAXY; i++)
+ for (j = 0; j < MAXX; j++)
+ know[j][i] = 1;
+ nap(2000);
+ draws(0, MAXX, 0, MAXY);
+ return;
+
+ case 16:
+ c[HOLDMONST] += 30;
+ bottomline();
+ return; /* hold monster */
+
+ case 17:
+ for (i = 0; i < 26; i++) /* gem perfection */
+ switch (iven[i]) {
+ case ODIAMOND:
+ case ORUBY:
+ case OEMERALD:
+ case OSAPPHIRE:
+ j = ivenarg[i];
+ j &= 255;
+ j <<= 1;
+ if (j > 255)
+ j = 255; /* double value */
+ ivenarg[i] = j;
+ break;
+ }
+ break;
+
+ case 18:
+ for (i = 0; i < 11; i++)
+ c[exten[i]] <<= 1; /* spell extension */
+ break;
+
+ case 19:
+ for (i = 0; i < 26; i++) { /* identify */
+ if (iven[i] == OPOTION)
+ potionname[ivenarg[i]] = potionhide[ivenarg[i]];
+ if (iven[i] == OSCROLL)
+ scrollname[ivenarg[i]] = scrollhide[ivenarg[i]];
+ }
+ break;
+
+ case 20:
+ for (i = 0; i < 10; i++) /* remove curse */
+ if (c[curse[i]])
+ c[curse[i]] = 1;
+ break;
+
+ case 21:
+ annihilate();
+ break; /* scroll of annihilation */
+
+ case 22:
+ godirect(22, 150, "The ray hits the %s", 0, ' '); /* pulverization */
+ break;
+ case 23:
+ c[LIFEPROT]++;
+ break; /* life protection */
+ };
+}
+
+
+
+static void
+oorb(void)
+{
+}
+
+static void
+opit(void)
+{
+ int i;
+ if (rnd(101) < 81) {
+ if (rnd(70) > 9 * c[DEXTERITY] - packweight() || rnd(101) < 5) {
+ if (level == MAXLEVEL - 1)
+ obottomless();
+ else if (level == MAXLEVEL + MAXVLEVEL - 1)
+ obottomless();
+ else {
+ if (rnd(101) < 20) {
+ i = 0;
+ lprcat("\nYou fell into a pit! Your fall is cushioned by an unknown force\n");
+ } else {
+ i = rnd(level * 3 + 3);
+ lprintf("\nYou fell into a pit! You suffer %ld hit points damage", (long) i);
+ lastnum = 261; /* if he dies scoreboard
+ * will say so */
+ }
+ losehp(i);
+ nap(2000);
+ newcavelevel(level + 1);
+ draws(0, MAXX, 0, MAXY);
+ }
+ }
+ }
+}
+
+static void
+obottomless(void)
+{
+ lprcat("\nYou fell into a bottomless pit!");
+ beep();
+ nap(3000);
+ died(262);
+}
+
+static void
+oelevator(int dir)
+{
+#ifdef lint
+ int x;
+ x = dir;
+ dir = x;
+#endif /* lint */
+}
+
+static void
+ostatue(void)
+{
+}
+
+static void
+omirror(void)
+{
+}
+
+static void
+obook(void)
+{
+ lprcat("\nDo you ");
+ if (c[BLINDCOUNT] == 0)
+ lprcat("(r) read it, ");
+ lprcat("(t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'r':
+ if (c[BLINDCOUNT])
+ break;
+ lprcat("read");
+ /* no more book */ readbook(iarg[playerx][playery]);
+ forget();
+ return;
+
+ case 't':
+ lprcat("take");
+ if (take(OBOOK, iarg[playerx][playery]) == 0)
+ forget(); /* no more book */
+ return;
+ };
+}
+
+/*
+ function to read a book
+ */
+void
+readbook(int lev)
+{
+ int i, tmp;
+ if (lev <= 3)
+ i = rund((tmp = splev[lev]) ? tmp : 1);
+ else
+ i = rnd((tmp = splev[lev] - 9) ? tmp : 1) + 9;
+ spelknow[i] = 1;
+ lprintf("\nSpell \"%s\": %s\n%s", spelcode[i], spelname[i], speldescript[i]);
+ if (rnd(10) == 4) {
+ lprcat("\nYour int went up by one!");
+ c[INTELLIGENCE]++;
+ bottomline();
+ }
+}
+
+static void
+ocookie(void)
+{
+ const char *p;
+
+ lprcat("\nDo you (e) eat it, (t) take it");
+ iopts();
+ while (1)
+ switch (ttgetch()) {
+ case '\33':
+ case 'i':
+ ignore();
+ return;
+
+ case 'e':
+ lprcat("eat\nThe cookie tasted good.");
+ forget(); /* no more cookie */
+ if (c[BLINDCOUNT])
+ return;
+ if (!(p = fortune()))
+ return;
+ lprcat(" A message inside the cookie reads:\n");
+ lprcat(p);
+ return;
+
+ case 't':
+ lprcat("take");
+ if (take(OCOOKIE, 0) == 0)
+ forget(); /* no more book */
+ return;
+ };
+}
+
+
+/*
+ * routine to pick up some gold -- if arg==OMAXGOLD then the pile is worth
+ * 100* the argument
+ */
+static void
+ogold(int arg)
+{
+ long i;
+ i = iarg[playerx][playery];
+ if (arg == OMAXGOLD)
+ i *= 100;
+ else if (arg == OKGOLD)
+ i *= 1000;
+ else if (arg == ODGOLD)
+ i *= 10;
+ lprintf("\nIt is worth %ld!", (long) i);
+ c[GOLD] += i;
+ bottomgold();
+ item[playerx][playery] = know[playerx][playery] = 0; /* destroy gold */
+}
+
+static void
+ohome(void)
+{
+ int i;
+ nosignal = 1; /* disable signals */
+ for (i = 0; i < 26; i++)
+ if (iven[i] == OPOTION)
+ if (ivenarg[i] == 21) {
+ iven[i] = 0; /* remove the potion of cure
+ * dianthroritis from
+ * inventory */
+ clear();
+ lprcat("Congratulations. You found a potion of cure dianthroritis.\n");
+ lprcat("\nFrankly, No one thought you could do it. Boy! Did you surprise them!\n");
+ if (gltime > TIMELIMIT) {
+ lprcat("\nThe doctor has the sad duty to inform you that your daughter died!\n");
+ lprcat("You didn't make it in time. In your agony, you kill the doctor,\nyour wife, and yourself! Too bad!\n");
+ nap(5000);
+ died(269);
+ } else {
+ lprcat("\nThe doctor is now administering the potion, and in a few moments\n");
+ lprcat("Your daughter should be well on her way to recovery.\n");
+ nap(6000);
+ lprcat("\nThe potion is");
+ nap(3000);
+ lprcat(" working! The doctor thinks that\n");
+ lprcat("your daughter will recover in a few days. Congratulations!\n");
+ beep();
+ nap(5000);
+ died(263);
+ }
+ }
+ while (1) {
+ clear();
+ lprintf("Welcome home %s. Latest word from the doctor is not good.\n", logname);
+
+ if (gltime > TIMELIMIT) {
+ lprcat("\nThe doctor has the sad duty to inform you that your daughter died!\n");
+ lprcat("You didn't make it in time. In your agony, you kill the doctor,\nyour wife, and yourself! Too bad!\n");
+ nap(5000);
+ died(269);
+ }
+ lprcat("\nThe diagnosis is confirmed as dianthroritis. He guesses that\n");
+ lprintf("your daughter has only %ld mobuls left in this world. It's up to you,\n", (long) ((TIMELIMIT - gltime + 99) / 100));
+ lprintf("%s, to find the only hope for your daughter, the very rare\n", logname);
+ lprcat("potion of cure dianthroritis. It is rumored that only deep in the\n");
+ lprcat("depths of the caves can this potion be found.\n\n\n");
+ lprcat("\n ----- press ");
+ standout("return");
+ lprcat(" to continue, ");
+ standout("escape");
+ lprcat(" to leave ----- ");
+ i = ttgetch();
+ while (i != '\33' && i != '\n')
+ i = ttgetch();
+ if (i == '\33') {
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ }
+}
+
+/* routine to save program space */
+void
+iopts(void)
+{
+ lprcat(", or (i) ignore it? ");
+}
+
+void
+ignore(void)
+{
+ lprcat("ignore\n");
+}
diff --git a/larn/pathnames.h b/larn/pathnames.h
new file mode 100644
index 0000000..0790dfc
--- /dev/null
+++ b/larn/pathnames.h
@@ -0,0 +1,38 @@
+/* $NetBSD: pathnames.h,v 1.8 2003/08/07 09:37:23 agc Exp $ */
+
+/*-
+ * Copyright (c) 1990 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 5.2 (Berkeley) 4/27/95
+ */
+
+#define _PATH_LOG "/var/games/larn/llog12.0"
+#define _PATH_SCORE "/var/games/larn/lscore12.0"
+#define _PATH_HELP "/usr/share/games/larn/larn.help"
+#define _PATH_LEVELS "/usr/share/games/larn/larnmaze"
+#define _PATH_PLAYERIDS "/var/games/larn/playerids"
diff --git a/larn/regen.c b/larn/regen.c
new file mode 100644
index 0000000..2c8537f
--- /dev/null
+++ b/larn/regen.c
@@ -0,0 +1,187 @@
+/* $NetBSD: regen.c,v 1.6 2012/06/19 05:30:44 dholland Exp $ */
+
+/* regen.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: regen.c,v 1.6 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+
+#include "header.h"
+#include "extern.h"
+/*
+ *******
+ REGEN()
+ *******
+ regen()
+
+ subroutine to regenerate player hp and spells
+ */
+void
+regen(void)
+{
+ int i, flag;
+ long *d;
+ d = c;
+#ifdef EXTRA
+ d[MOVESMADE]++;
+#endif
+ if (d[TIMESTOP]) {
+ if (--d[TIMESTOP] <= 0)
+ bottomline();
+ return;
+ } /* for stop time spell */
+ flag = 0;
+
+ if (d[STRENGTH] < 3) {
+ d[STRENGTH] = 3;
+ flag = 1;
+ }
+ if ((d[HASTESELF] == 0) || ((d[HASTESELF] & 1) == 0))
+ gltime++;
+
+ if (d[HP] != d[HPMAX])
+ if (d[REGENCOUNTER]-- <= 0) { /* regenerate hit points */
+ d[REGENCOUNTER] = 22 + (d[HARDGAME] << 1) - d[LEVEL];
+ if ((d[HP] += d[REGEN]) > d[HPMAX])
+ d[HP] = d[HPMAX];
+ bottomhp();
+ }
+ if (d[SPELLS] < d[SPELLMAX]) /* regenerate spells */
+ if (d[ECOUNTER]-- <= 0) {
+ d[ECOUNTER] = 100 + 4 * (d[HARDGAME] - d[LEVEL] - d[ENERGY]);
+ d[SPELLS]++;
+ bottomspell();
+ }
+ if (d[HERO])
+ if (--d[HERO] <= 0) {
+ for (i = 0; i < 6; i++)
+ d[i] -= 10;
+ flag = 1;
+ }
+ if (d[ALTPRO])
+ if (--d[ALTPRO] <= 0) {
+ d[MOREDEFENSES] -= 3;
+ flag = 1;
+ }
+ if (d[PROTECTIONTIME])
+ if (--d[PROTECTIONTIME] <= 0) {
+ d[MOREDEFENSES] -= 2;
+ flag = 1;
+ }
+ if (d[DEXCOUNT])
+ if (--d[DEXCOUNT] <= 0) {
+ d[DEXTERITY] -= 3;
+ flag = 1;
+ }
+ if (d[STRCOUNT])
+ if (--d[STRCOUNT] <= 0) {
+ d[STREXTRA] -= 3;
+ flag = 1;
+ }
+ if (d[BLINDCOUNT])
+ if (--d[BLINDCOUNT] <= 0) {
+ cursors();
+ lprcat("\nThe blindness lifts ");
+ beep();
+ }
+ if (d[CONFUSE])
+ if (--d[CONFUSE] <= 0) {
+ cursors();
+ lprcat("\nYou regain your senses");
+ beep();
+ }
+ if (d[GIANTSTR])
+ if (--d[GIANTSTR] <= 0) {
+ d[STREXTRA] -= 20;
+ flag = 1;
+ }
+ if (d[CHARMCOUNT])
+ if ((--d[CHARMCOUNT]) <= 0)
+ flag = 1;
+ if (d[INVISIBILITY])
+ if ((--d[INVISIBILITY]) <= 0)
+ flag = 1;
+ if (d[CANCELLATION])
+ if ((--d[CANCELLATION]) <= 0)
+ flag = 1;
+ if (d[WTW])
+ if ((--d[WTW]) <= 0)
+ flag = 1;
+ if (d[HASTESELF])
+ if ((--d[HASTESELF]) <= 0)
+ flag = 1;
+ if (d[AGGRAVATE])
+ --d[AGGRAVATE];
+ if (d[SCAREMONST])
+ if ((--d[SCAREMONST]) <= 0)
+ flag = 1;
+ if (d[STEALTH])
+ if ((--d[STEALTH]) <= 0)
+ flag = 1;
+ if (d[AWARENESS])
+ --d[AWARENESS];
+ if (d[HOLDMONST])
+ if ((--d[HOLDMONST]) <= 0)
+ flag = 1;
+ if (d[HASTEMONST])
+ --d[HASTEMONST];
+ if (d[FIRERESISTANCE])
+ if ((--d[FIRERESISTANCE]) <= 0)
+ flag = 1;
+ if (d[GLOBE])
+ if (--d[GLOBE] <= 0) {
+ d[MOREDEFENSES] -= 10;
+ flag = 1;
+ }
+ if (d[SPIRITPRO])
+ if (--d[SPIRITPRO] <= 0)
+ flag = 1;
+ if (d[UNDEADPRO])
+ if (--d[UNDEADPRO] <= 0)
+ flag = 1;
+ if (d[HALFDAM])
+ if (--d[HALFDAM] <= 0) {
+ cursors();
+ lprcat("\nYou now feel better ");
+ beep();
+ }
+ if (d[SEEINVISIBLE])
+ if (--d[SEEINVISIBLE] <= 0) {
+ monstnamelist[INVISIBLESTALKER] = ' ';
+ cursors();
+ lprcat("\nYou feel your vision return to normal");
+ beep();
+ }
+ if (d[ITCHING]) {
+ if (d[ITCHING] > 1)
+ if ((d[WEAR] != -1) || (d[SHIELD] != -1))
+ if (rnd(100) < 50) {
+ d[WEAR] = d[SHIELD] = -1;
+ cursors();
+ lprcat("\nThe hysteria of itching forces you to remove your armor!");
+ beep();
+ recalc();
+ bottomline();
+ }
+ if (--d[ITCHING] <= 0) {
+ cursors();
+ lprcat("\nYou now feel the irritation subside!");
+ beep();
+ }
+ }
+ if (d[CLUMSINESS]) {
+ if (d[WIELD] != -1)
+ if (d[CLUMSINESS] > 1)
+ if (item[playerx][playery] == 0) /* only if nothing there */
+ if (rnd(100) < 33) /* drop your weapon due
+ * to clumsiness */
+ drop_object((int) d[WIELD]);
+ if (--d[CLUMSINESS] <= 0) {
+ cursors();
+ lprcat("\nYou now feel less awkward!");
+ beep();
+ }
+ }
+ if (flag)
+ bottomline();
+}
diff --git a/larn/savelev.c b/larn/savelev.c
new file mode 100644
index 0000000..9954ff0
--- /dev/null
+++ b/larn/savelev.c
@@ -0,0 +1,67 @@
+/* $NetBSD: savelev.c,v 1.7 2012/06/19 05:30:44 dholland Exp $ */
+
+/* savelev.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: savelev.c,v 1.7 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+#include "header.h"
+#include "extern.h"
+
+/*
+ * routine to save the present level into storage
+ */
+void
+savelevel(void)
+{
+ struct cel *pcel;
+ u_char *pitem, *pknow, *pmitem;
+ short *phitp, *piarg;
+ struct cel *pecel;
+ pcel = &cell[level * MAXX * MAXY]; /* pointer to this level's
+ * cells */
+ pecel = pcel + MAXX * MAXY; /* pointer to past end of this
+ * level's cells */
+ pitem = item[0];
+ piarg = iarg[0];
+ pknow = know[0];
+ pmitem = mitem[0];
+ phitp = hitp[0];
+ while (pcel < pecel) {
+ pcel->mitem = *pmitem++;
+ pcel->hitp = *phitp++;
+ pcel->item = *pitem++;
+ pcel->know = *pknow++;
+ pcel->iarg = *piarg++;
+ pcel++;
+ }
+}
+
+/*
+ * routine to restore a level from storage
+ */
+void
+getlevel(void)
+{
+ struct cel *pcel;
+ u_char *pitem, *pknow, *pmitem;
+ short *phitp, *piarg;
+ struct cel *pecel;
+ pcel = &cell[level * MAXX * MAXY]; /* pointer to this level's
+ * cells */
+ pecel = pcel + MAXX * MAXY; /* pointer to past end of this
+ * level's cells */
+ pitem = item[0];
+ piarg = iarg[0];
+ pknow = know[0];
+ pmitem = mitem[0];
+ phitp = hitp[0];
+ while (pcel < pecel) {
+ *pmitem++ = pcel->mitem;
+ *phitp++ = pcel->hitp;
+ *pitem++ = pcel->item;
+ *pknow++ = pcel->know;
+ *piarg++ = pcel->iarg;
+ pcel++;
+ }
+}
diff --git a/larn/scores.c b/larn/scores.c
new file mode 100644
index 0000000..49a6f87
--- /dev/null
+++ b/larn/scores.c
@@ -0,0 +1,855 @@
+/* $NetBSD: scores.c,v 1.21 2012/06/19 05:30:44 dholland Exp $ */
+
+/*
+ * scores.c Larn is copyrighted 1986 by Noah Morgan.
+ *
+ * Functions in this file are:
+ *
+ * readboard() Function to read in the scoreboard into a static buffer
+ * writeboard() Function to write the scoreboard from readboard()'s buffer
+ * makeboard() Function to create a new scoreboard (wipe out old one)
+ * hashewon() Function to return 1 if player has won a game before, else 0
+ * long paytaxes(x) Function to pay taxes if any are due winshou()
+ * ubroutine to print out the winning scoreboard shou(x)
+ * ubroutine to print out the non-winners scoreboard showscores()
+ * unction to show the scoreboard on the terminal showallscores()
+ * Function to show scores and the iven lists that go with them sortboard()
+ * unction to sort the scoreboard newscore(score, whoo, whyded, winner)
+ * Function to add entry to scoreboard new1sub(score,i,whoo,taxes)
+ * Subroutine to put player into a new2sub(score,i,whoo,whyded)
+ * Subroutine to put player into a died(x) Subroutine to record who
+ * played larn, and what the score was diedsub(x) Subroutine to print out a
+ * line showing player when he is killed diedlog() Subroutine to read a
+ * log file and print it out in ascii format getplid(name)
+ * on to get players id # from id file
+ *
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: scores.c,v 1.21 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "header.h"
+#include "extern.h"
+
+struct scofmt { /* This is the structure for the scoreboard */
+ long score; /* the score of the player */
+ long suid; /* the user id number of the player */
+ short what; /* the number of the monster that killed
+ * player */
+ short level; /* the level player was on when he died */
+ short hardlev;/* the level of difficulty player played at */
+ short order; /* the relative ordering place of this entry */
+ char who[40];/* the name of the character */
+ char sciv[26][2]; /* this is the inventory list of the
+ * character */
+};
+struct wscofmt { /* This is the structure for the winning
+ * scoreboard */
+ long score; /* the score of the player */
+ long timeused; /* the time used in mobuls to win the
+ * game */
+ long taxes; /* taxes he owes to LRS */
+ long suid; /* the user id number of the player */
+ short hardlev;/* the level of difficulty player played at */
+ short order; /* the relative ordering place of this entry */
+ char who[40];/* the name of the character */
+};
+
+struct log_fmt { /* 102 bytes struct for the log file */
+ long score; /* the players score */
+ int32_t diedtime; /* time when game was over */
+ short cavelev;/* level in caves */
+ short diff; /* difficulty player played at */
+#ifdef EXTRA
+ long elapsedtime; /* real time of game in seconds */
+ long bytout; /* bytes input and output */
+ long bytin;
+ long moves; /* number of moves made by player */
+ short ac; /* armor class of player */
+ short hp, hpmax; /* players hitpoints */
+ short cputime;/* CPU time needed in seconds */
+ short killed, spused; /* monsters killed and spells cast */
+ short usage; /* usage of the CPU in % */
+ short lev; /* player level */
+#endif
+ char who[12];/* player name */
+ char what[46]; /* what happened to player */
+};
+
+static struct scofmt sco[SCORESIZE]; /* the structure for the scoreboard */
+static struct wscofmt winr[SCORESIZE]; /* struct for the winning scoreboard */
+static struct log_fmt logg; /* structure for the log file */
+static const char *whydead[] = {
+ "quit", "suspended", "self - annihilated", "shot by an arrow",
+ "hit by a dart", "fell into a pit", "fell into a bottomless pit",
+ "a winner", "trapped in solid rock", "killed by a missing save file",
+ "killed by an old save file", "caught by the greedy cheater checker trap",
+ "killed by a protected save file", "killed his family and committed suicide",
+ "erased by a wayward finger", "fell through a bottomless trap door",
+ "fell through a trap door", "drank some poisonous water",
+ "fried by an electric shock", "slipped on a volcano shaft",
+ "killed by a stupid act of frustration", "attacked by a revolting demon",
+ "hit by his own magic", "demolished by an unseen attacker",
+ "fell into the dreadful sleep", "killed by an exploding chest",
+ /* 26 */ "killed by a missing maze data file", "annihilated in a sphere",
+ "died a post mortem death", "wasted by a malloc() failure"
+};
+
+static int readboard(void);
+static int writeboard(void);
+static int winshou(void);
+static int shou(int);
+static int sortboard(void);
+static void newscore(long, char *, int, int);
+static void new1sub(long, int, char *, long);
+static void new2sub(long, int, char *, int);
+static void diedsub(int);
+
+/*
+ * readboard() Function to read in the scoreboard into a static buffer
+ *
+ * returns -1 if unable to read in the scoreboard, returns 0 if all is OK
+ */
+static int
+readboard(void)
+{
+ int i;
+
+ if (gid != egid)
+ setegid(egid);
+ i = lopen(scorefile);
+ if (gid != egid)
+ setegid(gid);
+ if (i < 0) {
+ lprcat("Can't read scoreboard\n");
+ lflush();
+ return (-1);
+ }
+ lrfill((char *) sco, sizeof(sco));
+ lrfill((char *) winr, sizeof(winr));
+ lrclose();
+ lcreat((char *) 0);
+ return (0);
+}
+
+/*
+ * writeboard() Function to write the scoreboard from readboard()'s buffer
+ *
+ * returns -1 if unable to write the scoreboard, returns 0 if all is OK
+ */
+static int
+writeboard(void)
+{
+ int i;
+
+ set_score_output();
+ if (gid != egid)
+ setegid(egid);
+ i = lcreat(scorefile);
+ if (gid != egid)
+ setegid(gid);
+ if (i < 0) {
+ lprcat("Can't write scoreboard\n");
+ lflush();
+ return (-1);
+ }
+ lwrite((char *) sco, sizeof(sco));
+ lwrite((char *) winr, sizeof(winr));
+ lwclose();
+ lcreat((char *) 0);
+ return (0);
+}
+
+/*
+ * makeboard() Function to create a new scoreboard (wipe out old one)
+ *
+ * returns -1 if unable to write the scoreboard, returns 0 if all is OK
+ */
+int
+makeboard(void)
+{
+ int i;
+ set_score_output();
+ for (i = 0; i < SCORESIZE; i++) {
+ winr[i].taxes = winr[i].score = sco[i].score = 0;
+ winr[i].order = sco[i].order = i;
+ }
+ if (writeboard())
+ return (-1);
+ if (gid != egid)
+ setegid(egid);
+ chmod(scorefile, 0660);
+ if (gid != egid)
+ setegid(gid);
+ return (0);
+}
+
+/*
+ * hashewon() Function to return 1 if player has won a game before, else 0
+ *
+ * This function also sets c[HARDGAME] to appropriate value -- 0 if not a
+ * winner, otherwise the next level of difficulty listed in the winners
+ * scoreboard. This function also sets outstanding_taxes to the value in
+ * the winners scoreboard.
+ */
+int
+hashewon(void)
+{
+ int i;
+ c[HARDGAME] = 0;
+ if (readboard() < 0)
+ return (0); /* can't find scoreboard */
+ for (i = 0; i < SCORESIZE; i++) /* search through winners scoreboard */
+ if (winr[i].suid == userid)
+ if (winr[i].score > 0) {
+ c[HARDGAME] = winr[i].hardlev + 1;
+ outstanding_taxes = winr[i].taxes;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * long paytaxes(x) Function to pay taxes if any are due
+ *
+ * Enter with the amount (in gp) to pay on the taxes.
+ * Returns amount actually paid.
+ */
+long
+paytaxes(long x)
+{
+ int i;
+ long amt;
+ if (x < 0)
+ return (0L);
+ if (readboard() < 0)
+ return (0L);
+ for (i = 0; i < SCORESIZE; i++)
+ if (winr[i].suid == userid) /* look for players winning
+ * entry */
+ if (winr[i].score > 0) { /* search for a winning
+ * entry for the player */
+ amt = winr[i].taxes;
+ if (x < amt)
+ amt = x; /* don't overpay taxes
+ * (Ughhhhh) */
+ winr[i].taxes -= amt;
+ outstanding_taxes -= amt;
+ set_score_output();
+ if (writeboard() < 0)
+ return (0);
+ return (amt);
+ }
+ return (0L); /* couldn't find user on winning scoreboard */
+}
+
+/*
+ * winshou() Subroutine to print out the winning scoreboard
+ *
+ * Returns the number of players on scoreboard that were shown
+ */
+static int
+winshou(void)
+{
+ struct wscofmt *p;
+ int i, j, count;
+ for (count = j = i = 0; i < SCORESIZE; i++) /* is there anyone on
+ * the scoreboard? */
+ if (winr[i].score != 0) {
+ j++;
+ break;
+ }
+ if (j) {
+ lprcat("\n Score Difficulty Time Needed Larn Winners List\n");
+
+ for (i = 0; i < SCORESIZE; i++) /* this loop is needed to
+ * print out the */
+ for (j = 0; j < SCORESIZE; j++) { /* winners in order */
+ p = &winr[j]; /* pointer to the scoreboard
+ * entry */
+ if (p->order == i) {
+ if (p->score) {
+ count++;
+ lprintf("%10ld %2ld %5ld Mobuls %s \n",
+ (long) p->score, (long) p->hardlev, (long) p->timeused, p->who);
+ }
+ break;
+ }
+ }
+ }
+ return (count); /* return number of people on scoreboard */
+}
+
+/*
+ * shou(x) Subroutine to print out the non-winners scoreboard
+ * int x;
+ *
+ * Enter with 0 to list the scores, enter with 1 to list inventories too
+ * Returns the number of players on scoreboard that were shown
+ */
+static int
+shou(int x)
+{
+ int i, j, n, k;
+ int count;
+ for (count = j = i = 0; i < SCORESIZE; i++) /* is the scoreboard
+ * empty? */
+ if (sco[i].score != 0) {
+ j++;
+ break;
+ }
+ if (j) {
+ lprcat("\n Score Difficulty Larn Visitor Log\n");
+ for (i = 0; i < SCORESIZE; i++) /* be sure to print them out
+ * in order */
+ for (j = 0; j < SCORESIZE; j++)
+ if (sco[j].order == i) {
+ if (sco[j].score) {
+ count++;
+ lprintf("%10ld %2ld %s ",
+ (long) sco[j].score, (long) sco[j].hardlev, sco[j].who);
+ if (sco[j].what < 256)
+ lprintf("killed by a %s", monster[sco[j].what].name);
+ else
+ lprintf("%s", whydead[sco[j].what - 256]);
+ if (x != 263)
+ lprintf(" on %s", levelname[sco[j].level]);
+ if (x) {
+ for (n = 0; n < 26; n++) {
+ iven[n] = sco[j].sciv[n][0];
+ ivenarg[n] = sco[j].sciv[n][1];
+ }
+ for (k = 1; k < 99; k++)
+ for (n = 0; n < 26; n++)
+ if (k == iven[n]) {
+ srcount = 0;
+ show3(n);
+ }
+ lprcat("\n\n");
+ } else
+ lprc('\n');
+ }
+ j = SCORESIZE;
+ }
+ }
+ return (count); /* return the number of players just shown */
+}
+
+/*
+ * showscores() Function to show the scoreboard on the terminal
+ *
+ * Returns nothing of value
+ */
+static char esb[] = "The scoreboard is empty.\n";
+void
+showscores(void)
+{
+ int i, j;
+ lflush();
+ lcreat((char *) 0);
+ if (readboard() < 0)
+ return;
+ i = winshou();
+ j = shou(0);
+ if (i + j == 0)
+ lprcat(esb);
+ else
+ lprc('\n');
+ lflush();
+}
+
+/*
+ * showallscores() Function to show scores and the iven lists that go with them
+ *
+ * Returns nothing of value
+ */
+void
+showallscores(void)
+{
+ int i, j;
+ lflush();
+ lcreat((char *) 0);
+ if (readboard() < 0)
+ return;
+ c[WEAR] = c[WIELD] = c[SHIELD] = -1; /* not wielding or wearing
+ * anything */
+ for (i = 0; i < MAXPOTION; i++)
+ potionname[i] = potionhide[i];
+ for (i = 0; i < MAXSCROLL; i++)
+ scrollname[i] = scrollhide[i];
+ i = winshou();
+ j = shou(1);
+ if (i + j == 0)
+ lprcat(esb);
+ else
+ lprc('\n');
+ lflush();
+}
+
+/*
+ * sortboard() Function to sort the scoreboard
+ *
+ * Returns 0 if no sorting done, else returns 1
+ */
+static int
+sortboard(void)
+{
+ int i, j = 0, pos;
+ long jdat;
+ for (i = 0; i < SCORESIZE; i++)
+ sco[i].order = winr[i].order = -1;
+ pos = 0;
+ while (pos < SCORESIZE) {
+ jdat = 0;
+ for (i = 0; i < SCORESIZE; i++)
+ if ((sco[i].order < 0) && (sco[i].score >= jdat)) {
+ j = i;
+ jdat = sco[i].score;
+ }
+ sco[j].order = pos++;
+ }
+ pos = 0;
+ while (pos < SCORESIZE) {
+ jdat = 0;
+ for (i = 0; i < SCORESIZE; i++)
+ if ((winr[i].order < 0) && (winr[i].score >= jdat)) {
+ j = i;
+ jdat = winr[i].score;
+ }
+ winr[j].order = pos++;
+ }
+ return (1);
+}
+
+/*
+ * newscore(score, whoo, whyded, winner) Function to add entry to scoreboard
+ * int score, winner, whyded;
+ * char *whoo;
+ *
+ * Enter with the total score in gp in score, players name in whoo,
+ * died() reason # in whyded, and TRUE/FALSE in winner if a winner
+ * ex. newscore(1000, "player 1", 32, 0);
+ */
+static void
+newscore(long score, char *whoo, int whyded, int winner)
+{
+ int i;
+ long taxes;
+ if (readboard() < 0)
+ return; /* do the scoreboard */
+ /* if a winner then delete all non-winning scores */
+ if (cheat)
+ winner = 0; /* if he cheated, don't let him win */
+ if (winner) {
+ for (i = 0; i < SCORESIZE; i++)
+ if (sco[i].suid == userid)
+ sco[i].score = 0;
+ taxes = score * TAXRATE;
+ score += 100000 * c[HARDGAME]; /* bonus for winning */
+ /*
+ * if he has a slot on the winning scoreboard update it if
+ * greater score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (winr[i].suid == userid) {
+ new1sub(score, i, whoo, taxes);
+ return;
+ }
+ /*
+ * he had no entry. look for last entry and see if he has a
+ * greater score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (winr[i].order == SCORESIZE - 1) {
+ new1sub(score, i, whoo, taxes);
+ return;
+ }
+ } else if (!cheat) { /* for not winning scoreboard */
+ /*
+ * if he has a slot on the scoreboard update it if greater
+ * score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (sco[i].suid == userid) {
+ new2sub(score, i, whoo, whyded);
+ return;
+ }
+ /*
+ * he had no entry. look for last entry and see if he has a
+ * greater score
+ */
+ for (i = 0; i < SCORESIZE; i++)
+ if (sco[i].order == SCORESIZE - 1) {
+ new2sub(score, i, whoo, whyded);
+ return;
+ }
+ }
+}
+
+/*
+ * new1sub(score,i,whoo,taxes) Subroutine to put player into a
+ * int score,i,whyded,taxes; winning scoreboard entry if his score
+ * char *whoo; is high enough
+ *
+ * Enter with the total score in gp in score, players name in whoo,
+ * died() reason # in whyded, and TRUE/FALSE in winner if a winner
+ * slot in scoreboard in i, and the tax bill in taxes.
+ * Returns nothing of value
+ */
+static void
+new1sub(long score, int i, char *whoo, long taxes)
+{
+ struct wscofmt *p;
+ p = &winr[i];
+ p->taxes += taxes;
+ if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
+ strcpy(p->who, whoo);
+ p->score = score;
+ p->hardlev = c[HARDGAME];
+ p->suid = userid;
+ p->timeused = gltime / 100;
+ }
+}
+
+/*
+ * new2sub(score,i,whoo,whyded) Subroutine to put player into a
+ * int score,i,whyded,taxes; non-winning scoreboard entry if his
+ * char *whoo; score is high enough
+ *
+ * Enter with the total score in gp in score, players name in whoo,
+ * died() reason # in whyded, and slot in scoreboard in i.
+ * Returns nothing of value
+ */
+static void
+new2sub(long score, int i, char *whoo, int whyded)
+{
+ int j;
+ struct scofmt *p;
+ p = &sco[i];
+ if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
+ strcpy(p->who, whoo);
+ p->score = score;
+ p->what = whyded;
+ p->hardlev = c[HARDGAME];
+ p->suid = userid;
+ p->level = level;
+ for (j = 0; j < 26; j++) {
+ p->sciv[j][0] = iven[j];
+ p->sciv[j][1] = ivenarg[j];
+ }
+ }
+}
+
+/*
+ * died(x) Subroutine to record who played larn, and what the score was
+ * int x;
+ *
+ * if x < 0 then don't show scores
+ * died() never returns! (unless c[LIFEPROT] and a reincarnatable death!)
+ *
+ * < 256 killed by the monster number
+ * 256 quit
+ * 257 suspended
+ * 258 self - annihilated
+ * 259 shot by an arrow
+ * 260 hit by a dart
+ * 261 fell into a pit
+ * 262 fell into a bottomless pit
+ * 263 a winner
+ * 264 trapped in solid rock
+ * 265 killed by a missing save file
+ * 266 killed by an old save file
+ * 267 caught by the greedy cheater checker trap
+ * 268 killed by a protected save file
+ * 269 killed his family and killed himself
+ * 270 erased by a wayward finger
+ * 271 fell through a bottomless trap door
+ * 272 fell through a trap door
+ * 273 drank some poisonous water
+ * 274 fried by an electric shock
+ * 275 slipped on a volcano shaft
+ * 276 killed by a stupid act of frustration
+ * 277 attacked by a revolting demon
+ * 278 hit by his own magic
+ * 279 demolished by an unseen attacker
+ * 280 fell into the dreadful sleep
+ * 281 killed by an exploding chest
+ * 282 killed by a missing maze data file
+ * 283 killed by a sphere of annihilation
+ * 284 died a post mortem death
+ * 285 malloc() failure
+ * 300 quick quit -- don't put on scoreboard
+ */
+
+static int scorerror;
+void
+died(int x)
+{
+ int f, win;
+ char ch;
+ const char *mod;
+ time_t zzz;
+ if (c[LIFEPROT] > 0) { /* if life protection */
+ switch ((x > 0) ? x : -x) {
+ case 256:
+ case 257:
+ case 262:
+ case 263:
+ case 265:
+ case 266:
+ case 267:
+ case 268:
+ case 269:
+ case 271:
+ case 282:
+ case 284:
+ case 285:
+ case 300:
+ goto invalid; /* can't be saved */
+ };
+ --c[LIFEPROT];
+ c[HP] = 1;
+ --c[CONSTITUTION];
+ cursors();
+ lprcat("\nYou feel wiiieeeeerrrrrd all over! ");
+ beep();
+ lflush();
+ sleep(4);
+ return; /* only case where died() returns */
+ }
+invalid:
+ clearvt100();
+ lflush();
+ f = 0;
+ if (ckpflag)
+ unlink(ckpfile);/* remove checkpoint file if used */
+ if (x < 0) {
+ f++;
+ x = -x;
+ } /* if we are not to display the scores */
+ if ((x == 300) || (x == 257))
+ exit(0); /* for quick exit or saved game */
+ if (x == 263)
+ win = 1;
+ else
+ win = 0;
+ c[GOLD] += c[BANKACCOUNT];
+ c[BANKACCOUNT] = 0;
+ /* now enter the player at the end of the scoreboard */
+ newscore(c[GOLD], logname, x, win);
+ diedsub(x); /* print out the score line */
+ lflush();
+
+ set_score_output();
+ if ((wizard == 0) && (c[GOLD] > 0)) { /* wizards can't score */
+#ifndef NOLOG
+ if (gid != egid)
+ setegid(egid);
+ if (lappend(logfile) < 0) { /* append to file */
+ if (lcreat(logfile) < 0) { /* and can't create new
+ * log file */
+ lcreat((char *) 0);
+ lprcat("\nCan't open record file: I can't post your score.\n");
+ sncbr();
+ resetscroll();
+ lflush();
+ exit(0);
+ }
+ if (gid != egid)
+ setegid(egid);
+ chmod(logfile, 0660);
+ if (gid != egid)
+ setegid(gid);
+ }
+ if (gid != egid)
+ setegid(gid);
+ strcpy(logg.who, loginname);
+ logg.score = c[GOLD];
+ logg.diff = c[HARDGAME];
+ if (x < 256) {
+ ch = *monster[x].name;
+ if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
+ mod = "an";
+ else
+ mod = "a";
+ snprintf(logg.what, sizeof(logg.what),
+ "killed by %s %s", mod, monster[x].name);
+ } else
+ snprintf(logg.what, sizeof(logg.what),
+ "%s", whydead[x - 256]);
+ logg.cavelev = level;
+ time(&zzz); /* get CPU time -- write out score info */
+ logg.diedtime = zzz;
+#ifdef EXTRA
+ times(&cputime);/* get CPU time -- write out score info */
+ logg.cputime = i = (cputime.tms_utime + cputime.tms_stime) / 60 + c[CPUTIME];
+ logg.lev = c[LEVEL];
+ logg.ac = c[AC];
+ logg.hpmax = c[HPMAX];
+ logg.hp = c[HP];
+ logg.elapsedtime = (zzz - initialtime + 59) / 60;
+ logg.usage = (10000 * i) / (zzz - initialtime);
+ logg.bytin = c[BYTESIN];
+ logg.bytout = c[BYTESOUT];
+ logg.moves = c[MOVESMADE];
+ logg.spused = c[SPELLSCAST];
+ logg.killed = c[MONSTKILLED];
+#endif
+ lwrite((char *) &logg, sizeof(struct log_fmt));
+ lwclose();
+#endif /* NOLOG */
+
+ /*
+ * now for the scoreboard maintenance -- not for a suspended
+ * game
+ */
+ if (x != 257) {
+ if (sortboard()) {
+ set_score_output();
+ scorerror = writeboard();
+ }
+ }
+ }
+ if ((x == 256) || (x == 257) || (f != 0))
+ exit(0);
+ if (scorerror == 0)
+ showscores(); /* if we updated the scoreboard */
+ if (x == 263)
+ mailbill();
+ exit(0);
+}
+
+/*
+ * diedsub(x) Subroutine to print out the line showing the player when he is killed
+ * int x;
+ */
+static void
+diedsub(int x)
+{
+ char ch;
+ const char *mod;
+
+ lprintf("Score: %ld, Diff: %ld, %s ", (long) c[GOLD], (long) c[HARDGAME], logname);
+ if (x < 256) {
+ ch = *monster[x].name;
+ if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
+ mod = "an";
+ else
+ mod = "a";
+ lprintf("killed by %s %s", mod, monster[x].name);
+ } else
+ lprintf("%s", whydead[x - 256]);
+ if (x != 263)
+ lprintf(" on %s\n", levelname[level]);
+ else
+ lprc('\n');
+}
+
+/*
+ * diedlog() Subroutine to read a log file and print it out in ascii format
+ */
+void
+diedlog(void)
+{
+ int n;
+ char *p;
+ static char q[] = "?";
+ struct stat stbuf;
+ time_t t;
+
+ lcreat((char *) 0);
+ if (lopen(logfile) < 0) {
+ lprintf("Can't locate log file <%s>\n", logfile);
+ return;
+ }
+ if (fstat(io_infd, &stbuf) < 0) {
+ lprintf("Can't stat log file <%s>\n", logfile);
+ return;
+ }
+ for (n = stbuf.st_size / sizeof(struct log_fmt); n > 0; --n) {
+ lrfill((char *) &logg, sizeof(struct log_fmt));
+ t = logg.diedtime;
+ if ((p = ctime(&t)) == NULL)
+ p = q;
+ else {
+ p[16] = '\n';
+ p[17] = 0;
+ }
+ lprintf("Score: %ld, Diff: %ld, %s %s on %ld at %s", (long) (logg.score), (long) (logg.diff), logg.who, logg.what, (long) (logg.cavelev), p + 4);
+#ifdef EXTRA
+ if (logg.moves <= 0)
+ logg.moves = 1;
+ lprintf(" Experience Level: %ld, AC: %ld, HP: %ld/%ld, Elapsed Time: %ld minutes\n", (long) (logg.lev), (long) (logg.ac), (long) (logg.hp), (long) (logg.hpmax), (long) (logg.elapsedtime));
+ lprintf(" CPU time used: %ld seconds, Machine usage: %ld.%02ld%%\n", (long) (logg.cputime), (long) (logg.usage / 100), (long) (logg.usage % 100));
+ lprintf(" BYTES in: %ld, out: %ld, moves: %ld, deaths: %ld, spells cast: %ld\n", (long) (logg.bytin), (long) (logg.bytout), (long) (logg.moves), (long) (logg.killed), (long) (logg.spused));
+ lprintf(" out bytes per move: %ld, time per move: %ld ms\n", (long) (logg.bytout / logg.moves), (long) ((logg.cputime * 1000) / logg.moves));
+#endif
+ }
+ lflush();
+ lrclose();
+ return;
+}
+
+#ifndef UIDSCORE
+/*
+ * getplid(name) Function to get players id # from id file
+ *
+ * Enter with the name of the players character in name.
+ * Returns the id # of the players character, or -1 if failure.
+ * This routine will try to find the name in the id file, if its not there,
+ * it will try to make a new entry in the file. Only returns -1 if can't
+ * find him in the file, and can't make a new entry in the file.
+ * Format of playerids file:
+ * Id # in ascii \n character name \n
+ */
+static int havepid = -1; /* playerid # if previously done */
+int
+getplid(nam)
+ char *nam;
+{
+ int fd7, high = 999, no;
+ char *p, *p2;
+ char name[80];
+ if (havepid != -1)
+ return (havepid); /* already did it */
+ lflush(); /* flush any pending I/O */
+ snprintf(name, sizeof(name), "%s\n", nam);/* append a \n to name */
+ if (lopen(playerids) < 0) { /* no file, make it */
+ if ((fd7 = creat(playerids, 0664)) < 0)
+ return (-1); /* can't make it */
+ close(fd7);
+ goto addone; /* now append new playerid record to file */
+ }
+ for (;;) { /* now search for the name in the player id
+ * file */
+ p = lgetl();
+ if (p == NULL)
+ break; /* EOF? */
+ no = atoi(p); /* the id # */
+ p2 = lgetl();
+ if (p2 == NULL)
+ break; /* EOF? */
+ if (no > high)
+ high = no; /* accumulate highest id # */
+ if (strcmp(p2, name) == 0) { /* we found him */
+ return (no); /* his id number */
+ }
+ }
+ lrclose();
+ /* if we get here, we didn't find him in the file -- put him there */
+addone:
+ if (lappend(playerids) < 0)
+ return (-1); /* can't open file for append */
+ lprintf("%ld\n%s", (long) ++high, name); /* new id # and name */
+ lwclose();
+ lcreat((char *) 0); /* re-open terminal channel */
+ return (high);
+}
+#endif /* UIDSCORE */
diff --git a/larn/signal.c b/larn/signal.c
new file mode 100644
index 0000000..807579a
--- /dev/null
+++ b/larn/signal.c
@@ -0,0 +1,138 @@
+/* $NetBSD: signal.c,v 1.9 2012/06/19 05:30:44 dholland Exp $ */
+
+/* "Larn is copyrighted 1986 by Noah Morgan.\n" */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: signal.c,v 1.9 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "header.h"
+#include "extern.h"
+
+static void s2choose(void);
+static void cntlc(int);
+static void sgam(int);
+static void tstop(int);
+static void sigpanic(int);
+
+#define BIT(a) (1<<((a)-1))
+
+static void
+s2choose(void)
+{ /* text to be displayed if ^C during intro
+ * screen */
+ cursor(1, 24);
+ lprcat("Press ");
+ setbold();
+ lprcat("return");
+ resetbold();
+ lprcat(" to continue: ");
+ lflush();
+}
+
+static void
+cntlc(int n)
+{ /* what to do for a ^C */
+ if (nosignal)
+ return; /* don't do anything if inhibited */
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ quit();
+ if (predostuff == 1)
+ s2choose();
+ else
+ showplayer();
+ lflush();
+ signal(SIGQUIT, cntlc);
+ signal(SIGINT, cntlc);
+}
+
+/*
+ * subroutine to save the game if a hangup signal
+ */
+static void
+sgam(int n)
+{
+ savegame(savefilename);
+ wizard = 1;
+ died(-257); /* hangup signal */
+}
+
+#ifdef SIGTSTP
+static void
+tstop(int n)
+{ /* control Y */
+ if (nosignal)
+ return; /* nothing if inhibited */
+ lcreat((char *) 0);
+ clearvt100();
+ lflush();
+ signal(SIGTSTP, SIG_DFL);
+#ifdef SIGVTALRM
+ /*
+ * looks like BSD4.2 or higher - must clr mask for signal to take
+ * effect
+ */
+ sigsetmask(sigblock(0) & ~BIT(SIGTSTP));
+#endif
+ kill(getpid(), SIGTSTP);
+
+ setupvt100();
+ signal(SIGTSTP, tstop);
+ if (predostuff == 1)
+ s2choose();
+ else
+ drawscreen();
+ showplayer();
+ lflush();
+}
+#endif /* SIGTSTP */
+
+/*
+ * subroutine to issue the needed signal traps called from main()
+ */
+void
+sigsetup(void)
+{
+ signal(SIGQUIT, cntlc);
+ signal(SIGINT, cntlc);
+ signal(SIGKILL, SIG_IGN);
+ signal(SIGHUP, sgam);
+ signal(SIGILL, sigpanic);
+ signal(SIGTRAP, sigpanic);
+ signal(SIGIOT, sigpanic);
+ /* signal(SIGEMT, sigpanic); 20150209 bkw: not on linux */
+ signal(SIGFPE, sigpanic);
+ signal(SIGBUS, sigpanic);
+ signal(SIGSEGV, sigpanic);
+ signal(SIGSYS, sigpanic);
+ signal(SIGPIPE, sigpanic);
+ signal(SIGTERM, sigpanic);
+#ifdef SIGTSTP
+ signal(SIGTSTP, tstop);
+ signal(SIGSTOP, tstop);
+#endif /* SIGTSTP */
+}
+
+/*
+ * routine to process a fatal error signal
+ */
+static void
+sigpanic(int sig)
+{
+ char buf[128];
+ signal(sig, SIG_DFL);
+ snprintf(buf, sizeof(buf),
+ "\nLarn - Panic! Signal %d received [%s]", sig,
+ sys_siglist[sig]); /* 20150209 bkw: no sys_signame on linux */
+ write(2, buf, strlen(buf));
+ sleep(2);
+ sncbr();
+ savegame(savefilename);
+ kill(getpid(), sig); /* this will terminate us */
+}
diff --git a/larn/store.c b/larn/store.c
new file mode 100644
index 0000000..2d33538
--- /dev/null
+++ b/larn/store.c
@@ -0,0 +1,910 @@
+/* $NetBSD: store.c,v 1.16 2012/06/19 05:30:44 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)store.c 5.4 (Berkeley) 5/13/91";
+#else
+__RCSID("$NetBSD: store.c,v 1.16 2012/06/19 05:30:44 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/* store.c Larn is copyrighted 1986 by Noah Morgan. */
+#include "header.h"
+#include "extern.h"
+
+static void handsfull(void);
+static void outofstock(void);
+static void nogold(void);
+static void dnditem(int);
+static void banktitle(const char *);
+static void obanksub(void);
+static void otradhead(void);
+static void cnsitm(void);
+
+static int dndcount = 0, dnditm = 0;
+
+/* number of items in the dnd inventory table */
+#define MAXITM 83
+
+/* this is the data for the stuff in the dnd store */
+struct _itm itm[90] = {
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {2, OLEATHER, 0, 3},
+ {10, OSTUDLEATHER, 0, 2},
+ {40, ORING, 0, 2},
+ {85, OCHAIN, 0, 2},
+ {220, OSPLINT, 0, 1},
+ {400, OPLATE, 0, 1},
+ {900, OPLATEARMOR, 0, 1},
+ {2600, OSSPLATE, 0, 1},
+ {150, OSHIELD, 0, 1},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {2, ODAGGER, 0, 3},
+ {20, OSPEAR, 0, 3},
+ {80, OFLAIL, 0, 2},
+ {150, OBATTLEAXE, 0, 2},
+ {450, OLONGSWORD, 0, 2},
+ {1000, O2SWORD, 0, 2},
+ {5000, OSWORD, 0, 1},
+ {16500, OLANCE, 0, 1},
+ {6000, OSWORDofSLASHING, 0, 0},
+ {10000, OHAMMER, 0, 0},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {150, OPROTRING, 1, 1},
+ {85, OSTRRING, 1, 1},
+ {120, ODEXRING, 1, 1},
+ {120, OCLEVERRING, 1, 1},
+ {180, OENERGYRING, 0, 1},
+ {125, ODAMRING, 0, 1},
+ {220, OREGENRING, 0, 1},
+ {1000, ORINGOFEXTRA, 0, 1},
+
+ {280, OBELT, 0, 1},
+
+ {400, OAMULET, 0, 1},
+
+ {6500, OORBOFDRAGON, 0, 0},
+ {5500, OSPIRITSCARAB, 0, 0},
+ {5000, OCUBEofUNDEAD, 0, 0},
+ {6000, ONOTHEFT, 0, 0},
+
+ {590, OCHEST, 6, 1},
+ {200, OBOOK, 8, 1},
+ {10, OCOOKIE, 0, 3},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {20, OPOTION, 0, 6},
+ {90, OPOTION, 1, 5},
+ {520, OPOTION, 2, 1},
+ {100, OPOTION, 3, 2},
+ {50, OPOTION, 4, 2},
+ {150, OPOTION, 5, 2},
+ {70, OPOTION, 6, 1},
+ {30, OPOTION, 7, 7},
+ {200, OPOTION, 8, 1},
+ {50, OPOTION, 9, 1},
+ {80, OPOTION, 10, 1},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {30, OPOTION, 11, 3},
+ {20, OPOTION, 12, 5},
+ {40, OPOTION, 13, 3},
+ {35, OPOTION, 14, 2},
+ {520, OPOTION, 15, 1},
+ {90, OPOTION, 16, 2},
+ {200, OPOTION, 17, 2},
+ {220, OPOTION, 18, 4},
+ {80, OPOTION, 19, 6},
+ {370, OPOTION, 20, 3},
+ {50, OPOTION, 22, 1},
+ {150, OPOTION, 23, 3},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {100, OSCROLL, 0, 2},
+ {125, OSCROLL, 1, 2},
+ {60, OSCROLL, 2, 4},
+ {10, OSCROLL, 3, 4},
+ {100, OSCROLL, 4, 3},
+ {200, OSCROLL, 5, 2},
+ {110, OSCROLL, 6, 1},
+ {500, OSCROLL, 7, 2},
+ {200, OSCROLL, 8, 2},
+ {250, OSCROLL, 9, 4},
+ {20, OSCROLL, 10, 5},
+ {30, OSCROLL, 11, 3},
+
+ /*
+ * cost iven name iven arg how gp
+ * iven[] ivenarg[] many
+ */
+
+ {340, OSCROLL, 12, 1},
+ {340, OSCROLL, 13, 1},
+ {300, OSCROLL, 14, 2},
+ {400, OSCROLL, 15, 2},
+ {500, OSCROLL, 16, 2},
+ {1000, OSCROLL, 17, 1},
+ {500, OSCROLL, 18, 1},
+ {340, OSCROLL, 19, 2},
+ {220, OSCROLL, 20, 3},
+ {3900, OSCROLL, 21, 0},
+ {610, OSCROLL, 22, 1},
+ {3000, OSCROLL, 23, 0}
+};
+
+/*
+ function for the dnd store
+ */
+static void
+dnd_2hed(void)
+{
+ lprcat("Welcome to the Larn Thrift Shoppe. We stock many items explorers find useful\n");
+ lprcat(" in their adventures. Feel free to browse to your hearts content.\n");
+ lprcat("Also be advised, if you break 'em, you pay for 'em.");
+}
+
+static void
+dnd_hed(void)
+{
+ int i;
+ for (i = dnditm; i < 26 + dnditm; i++)
+ dnditem(i);
+ cursor(50, 18);
+ lprcat("You have ");
+}
+
+static void
+handsfull(void)
+{
+ lprcat("\nYou can't carry anything more!");
+ lflush();
+ nap(2200);
+}
+
+static void
+outofstock(void)
+{
+ lprcat("\nSorry, but we are out of that item.");
+ lflush();
+ nap(2200);
+}
+
+static void
+nogold(void)
+{
+ lprcat("\nYou don't have enough gold to pay for that!");
+ lflush();
+ nap(2200);
+}
+
+void
+dndstore(void)
+{
+ int i;
+ dnditm = 0;
+ nosignal = 1; /* disable signals */
+ clear();
+ dnd_2hed();
+ if (outstanding_taxes > 0) {
+ lprcat("\n\nThe Larn Revenue Service has ordered us to not do business with tax evaders.\n");
+ beep();
+ lprintf("They have also told us that you owe %ld gp in back taxes, and as we must\n", (long) outstanding_taxes);
+ lprcat("comply with the law, we cannot serve you at this time. Soo Sorry.\n");
+ cursors();
+ lprcat("\nPress ");
+ standout("escape");
+ lprcat(" to leave: ");
+ lflush();
+ i = 0;
+ while (i != '\33')
+ i = ttgetch();
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ dnd_hed();
+ while (1) {
+ cursor(59, 18);
+ lprintf("%ld gold pieces", (long) c[GOLD]);
+ cltoeoln();
+ cl_dn(1, 20); /* erase to eod */
+ lprcat("\nEnter your transaction [");
+ standout("space");
+ lprcat(" for more, ");
+ standout("escape");
+ lprcat(" to leave]? ");
+ i = 0;
+ while ((i < 'a' || i > 'z') && (i != ' ') && (i != '\33') && (i != 12))
+ i = ttgetch();
+ if (i == 12) {
+ clear();
+ dnd_2hed();
+ dnd_hed();
+ } else if (i == '\33') {
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ } else if (i == ' ') {
+ cl_dn(1, 4);
+ if ((dnditm += 26) >= MAXITM)
+ dnditm = 0;
+ dnd_hed();
+ } else { /* buy something */
+ lprc(i);/* echo the byte */
+ i += dnditm - 'a';
+ if (i >= MAXITM)
+ outofstock();
+ else if (itm[i].qty <= 0)
+ outofstock();
+ else if (pocketfull())
+ handsfull();
+ else if (c[GOLD] < itm[i].price * 10)
+ nogold();
+ else {
+ if (itm[i].obj == OPOTION) {
+ potionname[itm[i].arg] = potionhide[itm[i].arg];
+ } else if (itm[i].obj == OSCROLL) {
+ scrollname[itm[i].arg] = scrollhide[itm[i].arg];
+ }
+ c[GOLD] -= itm[i].price * 10;
+ itm[i].qty--;
+ take(itm[i].obj, itm[i].arg);
+ if (itm[i].qty == 0)
+ dnditem(i);
+ nap(1001);
+ }
+ }
+
+ }
+}
+
+/*
+ dnditem(index)
+
+ to print the item list; used in dndstore() enter with the index into itm
+ */
+static void
+dnditem(int i)
+{
+ int j, k;
+ if (i >= MAXITM)
+ return;
+ cursor((j = (i & 1) * 40 + 1), (k = ((i % 26) >> 1) + 5));
+ if (itm[i].qty == 0) {
+ lprintf("%39s", "");
+ return;
+ }
+ lprintf("%c) ", (i % 26) + 'a');
+ if (itm[i].obj == OPOTION) {
+ lprintf("potion of%s", potionhide[itm[i].arg]);
+ } else if (itm[i].obj == OSCROLL) {
+ lprintf("scroll of%s", scrollhide[itm[i].arg]);
+ } else
+ lprintf("%s", objectname[itm[i].obj]);
+ cursor(j + 31, k);
+ lprintf("%6ld", (long) (itm[i].price * 10));
+}
+
+
+
+/*
+ for the college of larn
+ */
+u_char course[26] = {0}; /* the list of courses taken */
+static char coursetime[] = {10, 15, 10, 20, 10, 10, 10, 5};
+/*
+ function to display the header info for the school
+ */
+static void
+sch_hed(void)
+{
+ clear();
+ lprcat("The College of Larn offers the exciting opportunity of higher education to\n");
+ lprcat("all inhabitants of the caves. Here is a list of the class schedule:\n\n\n");
+ lprcat("\t\t Course Name \t Time Needed\n\n");
+
+ if (course[0] == 0)
+ lprcat("\t\ta) Fighters Training I 10 mobuls"); /* line 7 of crt */
+ lprc('\n');
+ if (course[1] == 0)
+ lprcat("\t\tb) Fighters Training II 15 mobuls");
+ lprc('\n');
+ if (course[2] == 0)
+ lprcat("\t\tc) Introduction to Wizardry 10 mobuls");
+ lprc('\n');
+ if (course[3] == 0)
+ lprcat("\t\td) Applied Wizardry 20 mobuls");
+ lprc('\n');
+ if (course[4] == 0)
+ lprcat("\t\te) Behavioral Psychology 10 mobuls");
+ lprc('\n');
+ if (course[5] == 0)
+ lprcat("\t\tf) Faith for Today 10 mobuls");
+ lprc('\n');
+ if (course[6] == 0)
+ lprcat("\t\tg) Contemporary Dance 10 mobuls");
+ lprc('\n');
+ if (course[7] == 0)
+ lprcat("\t\th) History of Larn 5 mobuls");
+
+ lprcat("\n\n\t\tAll courses cost 250 gold pieces.");
+ cursor(30, 18);
+ lprcat("You are presently carrying ");
+}
+
+void
+oschool(void)
+{
+ int i;
+ long time_used;
+ nosignal = 1; /* disable signals */
+ sch_hed();
+ while (1) {
+ cursor(57, 18);
+ lprintf("%ld gold pieces. ", (long) c[GOLD]);
+ cursors();
+ lprcat("\nWhat is your choice [");
+ standout("escape");
+ lprcat(" to leave] ? ");
+ yrepcount = 0;
+ i = 0;
+ while ((i < 'a' || i > 'h') && (i != '\33') && (i != 12))
+ i = ttgetch();
+ if (i == 12) {
+ sch_hed();
+ continue;
+ } else if (i == '\33') {
+ nosignal = 0;
+ drawscreen(); /* enable signals */
+ return;
+ }
+ lprc(i);
+ if (c[GOLD] < 250)
+ nogold();
+ else if (course[i - 'a']) {
+ lprcat("\nSorry, but that class is filled.");
+ nap(1000);
+ } else if (i <= 'h') {
+ c[GOLD] -= 250;
+ time_used = 0;
+ switch (i) {
+ case 'a':
+ c[STRENGTH] += 2;
+ c[CONSTITUTION]++;
+ lprcat("\nYou feel stronger!");
+ cl_line(16, 7);
+ break;
+
+ case 'b':
+ if (course[0] == 0) {
+ lprcat("\nSorry, but this class has a prerequisite of Fighters Training I");
+ c[GOLD] += 250;
+ time_used = -10000;
+ break;
+ }
+ lprcat("\nYou feel much stronger!");
+ cl_line(16, 8);
+ c[STRENGTH] += 2;
+ c[CONSTITUTION] += 2;
+ break;
+
+ case 'c':
+ c[INTELLIGENCE] += 2;
+ lprcat("\nThe task before you now seems more attainable!");
+ cl_line(16, 9);
+ break;
+
+ case 'd':
+ if (course[2] == 0) {
+ lprcat("\nSorry, but this class has a prerequisite of Introduction to Wizardry");
+ c[GOLD] += 250;
+ time_used = -10000;
+ break;
+ }
+ lprcat("\nThe task before you now seems very attainable!");
+ cl_line(16, 10);
+ c[INTELLIGENCE] += 2;
+ break;
+
+ case 'e':
+ c[CHARISMA] += 3;
+ lprcat("\nYou now feel like a born leader!");
+ cl_line(16, 11);
+ break;
+
+ case 'f':
+ c[WISDOM] += 2;
+ lprcat("\nYou now feel more confident that you can find the potion in time!");
+ cl_line(16, 12);
+ break;
+
+ case 'g':
+ c[DEXTERITY] += 3;
+ lprcat("\nYou feel like dancing!");
+ cl_line(16, 13);
+ break;
+
+ case 'h':
+ c[INTELLIGENCE]++;
+ lprcat("\nYour instructor told you that the Eye of Larn is rumored to be guarded\n");
+ lprcat("by a platinum dragon who possesses psionic abilities. ");
+ cl_line(16, 14);
+ break;
+ }
+ time_used += coursetime[i - 'a'] * 100;
+ if (time_used > 0) {
+ gltime += time_used;
+ course[i - 'a']++; /* remember that he has
+ * taken that course */
+ c[HP] = c[HPMAX];
+ c[SPELLS] = c[SPELLMAX]; /* he regenerated */
+
+ if (c[BLINDCOUNT])
+ c[BLINDCOUNT] = 1; /* cure blindness too! */
+ if (c[CONFUSE])
+ c[CONFUSE] = 1; /* end confusion */
+ adjusttime((long) time_used); /* adjust parameters for
+ * time change */
+ }
+ nap(1000);
+ }
+ }
+}
+
+
+/*
+ * for the first national bank of Larn
+ */
+int lasttime = 0; /* last time he was in bank */
+
+void
+obank(void)
+{
+ banktitle(" Welcome to the First National Bank of Larn.");
+}
+void
+obank2(void)
+{
+ banktitle("Welcome to the 5th level branch office of the First National Bank of Larn.");
+}
+
+static void
+banktitle(const char *str)
+{
+ nosignal = 1; /* disable signals */
+ clear();
+ lprcat(str);
+ if (outstanding_taxes > 0) {
+ int i;
+ lprcat("\n\nThe Larn Revenue Service has ordered that your account be frozen until all\n");
+ beep();
+ lprintf("levied taxes have been paid. They have also told us that you owe %ld gp in\n", (long) outstanding_taxes);
+ lprcat("taxes, and we must comply with them. We cannot serve you at this time. Sorry.\n");
+ lprcat("We suggest you go to the LRS office and pay your taxes.\n");
+ cursors();
+ lprcat("\nPress ");
+ standout("escape");
+ lprcat(" to leave: ");
+ lflush();
+ i = 0;
+ while (i != '\33')
+ i = ttgetch();
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ lprcat("\n\n\tGemstone\t Appraisal\t\tGemstone\t Appraisal");
+ obanksub();
+ nosignal = 0; /* enable signals */
+ drawscreen();
+}
+
+/*
+ * function to put interest on your bank account
+ */
+void
+ointerest(void)
+{
+ int i;
+ if (c[BANKACCOUNT] < 0)
+ c[BANKACCOUNT] = 0;
+ else if ((c[BANKACCOUNT] > 0) && (c[BANKACCOUNT] < 500000)) {
+ i = (gltime - lasttime) / 100; /* # mobuls elapsed */
+ while ((i-- > 0) && (c[BANKACCOUNT] < 500000))
+ c[BANKACCOUNT] += c[BANKACCOUNT] / 250;
+ if (c[BANKACCOUNT] > 500000)
+ c[BANKACCOUNT] = 500000; /* interest limit */
+ }
+ lasttime = (gltime / 100) * 100;
+}
+
+static short gemorder[26] = {0}; /* the reference to screen location
+ * for each */
+static long gemvalue[26] = {0}; /* the appraisal of the gems */
+void
+obanksub(void)
+{
+ long amt;
+ int i, k;
+ ointerest(); /* credit any needed interest */
+
+ for (k = i = 0; i < 26; i++)
+ switch (iven[i]) {
+ case OLARNEYE:
+ case ODIAMOND:
+ case OEMERALD:
+ case ORUBY:
+ case OSAPPHIRE:
+
+ if (iven[i] == OLARNEYE) {
+ gemvalue[i] = 250000 - ((gltime * 7) / 100) * 100;
+ if (gemvalue[i] < 50000)
+ gemvalue[i] = 50000;
+ } else
+ gemvalue[i] = (255 & ivenarg[i]) * 100;
+ gemorder[i] = k;
+ cursor((k % 2) * 40 + 1, (k >> 1) + 4);
+ lprintf("%c) %s", i + 'a', objectname[iven[i]]);
+ cursor((k % 2) * 40 + 33, (k >> 1) + 4);
+ lprintf("%5ld", (long) gemvalue[i]);
+ k++;
+ };
+ cursor(31, 17);
+ lprintf("You have %8ld gold pieces in the bank.", (long) c[BANKACCOUNT]);
+ cursor(40, 18);
+ lprintf("You have %8ld gold pieces", (long) c[GOLD]);
+ if (c[BANKACCOUNT] + c[GOLD] >= 500000)
+ lprcat("\nNote: Larndom law states that only deposits under 500,000gp can earn interest.");
+ while (1) {
+ cl_dn(1, 20);
+ lprcat("\nYour wish? [(");
+ standout("d");
+ lprcat(") deposit, (");
+ standout("w");
+ lprcat(") withdraw, (");
+ standout("s");
+ lprcat(") sell a stone, or ");
+ standout("escape");
+ lprcat("] ");
+ yrepcount = 0;
+ i = 0;
+ while (i != 'd' && i != 'w' && i != 's' && i != '\33')
+ i = ttgetch();
+ switch (i) {
+ case 'd':
+ lprcat("deposit\nHow much? ");
+ amt = readnum((long) c[GOLD]);
+ if (amt < 0) {
+ lprcat("\nSorry, but we can't take negative gold!");
+ nap(2000);
+ amt = 0;
+ } else if (amt > c[GOLD]) {
+ lprcat(" You don't have that much.");
+ nap(2000);
+ } else {
+ c[GOLD] -= amt;
+ c[BANKACCOUNT] += amt;
+ }
+ break;
+
+ case 'w':
+ lprcat("withdraw\nHow much? ");
+ amt = readnum((long) c[BANKACCOUNT]);
+ if (amt < 0) {
+ lprcat("\nSorry, but we don't have any negative gold!");
+ nap(2000);
+ amt = 0;
+ } else if (amt > c[BANKACCOUNT]) {
+ lprcat("\nYou don't have that much in the bank!");
+ nap(2000);
+ } else {
+ c[GOLD] += amt;
+ c[BANKACCOUNT] -= amt;
+ }
+ break;
+
+ case 's':
+ lprcat("\nWhich stone would you like to sell? ");
+ i = 0;
+ while ((i < 'a' || i > 'z') && i != '*')
+ i = ttgetch();
+ if (i == '*')
+ for (i = 0; i < 26; i++) {
+ if (gemvalue[i]) {
+ c[GOLD] += gemvalue[i];
+ iven[i] = 0;
+ gemvalue[i] = 0;
+ k = gemorder[i];
+ cursor((k % 2) * 40 + 1, (k >> 1) + 4);
+ lprintf("%39s", "");
+ }
+ }
+ else {
+ if (gemvalue[i = i - 'a'] == 0) {
+ lprintf("\nItem %c is not a gemstone!", i + 'a');
+ nap(2000);
+ break;
+ }
+ c[GOLD] += gemvalue[i];
+ iven[i] = 0;
+ gemvalue[i] = 0;
+ k = gemorder[i];
+ cursor((k % 2) * 40 + 1, (k >> 1) + 4);
+ lprintf("%39s", "");
+ }
+ break;
+
+ case '\33':
+ return;
+ };
+ cursor(40, 17);
+ lprintf("%8ld", (long) c[BANKACCOUNT]);
+ cursor(49, 18);
+ lprintf("%8ld", (long) c[GOLD]);
+ }
+}
+
+#if 0 /* XXX: apparently unused */
+/*
+ subroutine to appraise any stone for the bank
+ */
+static void
+appraise(int gemstone)
+{
+ int j, amt;
+ for (j = 0; j < 26; j++)
+ if (iven[j] == gemstone) {
+ lprintf("\nI see you have %s", objectname[gemstone]);
+ if (gemstone == OLARNEYE)
+ lprcat(" I must commend you. I didn't think\nyou could get it.");
+ lprcat(" Shall I appraise it for you? ");
+ yrepcount = 0;
+ if (getyn() == 'y') {
+ lprcat("yes.\n Just one moment please \n");
+ nap(1000);
+ if (gemstone == OLARNEYE) {
+ amt = 250000 - ((gltime * 7) / 100) * 100;
+ if (amt < 50000)
+ amt = 50000;
+ } else
+ amt = (255 & ivenarg[j]) * 100;
+ lprintf("\nI can see this is an excellent stone, It is worth %ld", (long) amt);
+ lprcat("\nWould you like to sell it to us? ");
+ yrepcount = 0;
+ if (getyn() == 'y') {
+ lprcat("yes\n");
+ c[GOLD] += amt;
+ iven[j] = 0;
+ } else
+ lprcat("no thank you.\n");
+ if (gemstone == OLARNEYE)
+ lprcat("It is, of course, your privilege to keep the stone\n");
+ } else
+ lprcat("no\nO. K.\n");
+ }
+}
+#endif /* 0 - unused */
+
+/*
+ function for the trading post
+ */
+static void
+otradhead(void)
+{
+ clear();
+ lprcat("Welcome to the Larn Trading Post. We buy items that explorers no longer find\n");
+ lprcat("useful. Since the condition of the items you bring in is not certain,\n");
+ lprcat("and we incur great expense in reconditioning the items, we usually pay\n");
+ lprcat("only 20% of their value were they to be new. If the items are badly\n");
+ lprcat("damaged, we will pay only 10% of their new value.\n\n");
+}
+
+void
+otradepost(void)
+{
+ int i, j, value, isub, izarg;
+ dnditm = dndcount = 0;
+ nosignal = 1; /* disable signals */
+ resetscroll();
+ otradhead();
+ while (1) {
+ lprcat("\nWhat item do you want to sell to us [");
+ standout("*");
+ lprcat(" for list, or ");
+ standout("escape");
+ lprcat("] ? ");
+ i = 0;
+ while (i > 'z' || (i < 'a' && i != '*' && i != '\33' && i != '.'))
+ i = ttgetch();
+ if (i == '\33') {
+ setscroll();
+ recalc();
+ drawscreen();
+ nosignal = 0; /* enable signals */
+ return;
+ }
+ isub = i - 'a';
+ j = 0;
+ if (iven[isub] == OSCROLL)
+ if (scrollname[ivenarg[isub]][0] == 0) {
+ j = 1;
+ cnsitm();
+ } /* can't sell unidentified item */
+ if (iven[isub] == OPOTION)
+ if (potionname[ivenarg[isub]][0] == 0) {
+ j = 1;
+ cnsitm();
+ } /* can't sell unidentified item */
+ if (!j) {
+ if (i == '*') {
+ clear();
+ qshowstr();
+ otradhead();
+ } else if (iven[isub] == 0)
+ lprintf("\nYou don't have item %c!", isub + 'a');
+ else {
+ for (j = 0; j < MAXITM; j++)
+ if ((itm[j].obj == iven[isub]) || (iven[isub] == ODIAMOND) || (iven[isub] == ORUBY) || (iven[isub] == OEMERALD) || (iven[isub] == OSAPPHIRE)) {
+ srcount = 0;
+ show3(isub); /* show what the item
+ * was */
+ if ((iven[isub] == ODIAMOND) || (iven[isub] == ORUBY)
+ || (iven[isub] == OEMERALD) || (iven[isub] == OSAPPHIRE))
+ value = 20 * ivenarg[isub];
+ else if ((itm[j].obj == OSCROLL) || (itm[j].obj == OPOTION))
+ value = 2 * itm[j + ivenarg[isub]].price;
+ else {
+ izarg = ivenarg[isub];
+ value = itm[j].price; /* appreciate if a +n
+ * object */
+ if (izarg >= 0)
+ value *= 2;
+ while ((izarg-- > 0) && ((value = 14 * (67 + value) / 10) < 500000));
+ }
+ lprintf("\nItem (%c) is worth %ld gold pieces to us. Do you want to sell it? ", i, (long) value);
+ yrepcount = 0;
+ if (getyn() == 'y') {
+ lprcat("yes\n");
+ c[GOLD] += value;
+ if (c[WEAR] == isub)
+ c[WEAR] = -1;
+ if (c[WIELD] == isub)
+ c[WIELD] = -1;
+ if (c[SHIELD] == isub)
+ c[SHIELD] = -1;
+ adjustcvalues(iven[isub], ivenarg[isub]);
+ iven[isub] = 0;
+ } else
+ lprcat("no thanks.\n");
+ j = MAXITM + 100; /* get out of the inner
+ * loop */
+ }
+ if (j <= MAXITM + 2)
+ lprcat("\nSo sorry, but we are not authorized to accept that item.");
+ }
+ }
+ }
+}
+
+static void
+cnsitm(void)
+{
+ lprcat("\nSorry, we can't accept unidentified objects.");
+}
+
+/*
+ * for the Larn Revenue Service
+ */
+void
+olrs(void)
+{
+ int i, first;
+ long amt;
+ first = nosignal = 1; /* disable signals */
+ clear();
+ resetscroll();
+ cursor(1, 4);
+ lprcat("Welcome to the Larn Revenue Service district office. How can we help you?");
+ while (1) {
+ if (first) {
+ first = 0;
+ goto nxt;
+ }
+ cursors();
+ lprcat("\n\nYour wish? [(");
+ standout("p");
+ lprcat(") pay taxes, or ");
+ standout("escape");
+ lprcat("] ");
+ yrepcount = 0;
+ i = 0;
+ while (i != 'p' && i != '\33')
+ i = ttgetch();
+ switch (i) {
+ case 'p':
+ lprcat("pay taxes\nHow much? ");
+ amt = readnum((long) c[GOLD]);
+ if (amt < 0) {
+ lprcat("\nSorry, but we can't take negative gold\n");
+ amt = 0;
+ } else if (amt > c[GOLD])
+ lprcat(" You don't have that much.\n");
+ else
+ c[GOLD] -= paytaxes(amt);
+ break;
+
+ case '\33':
+ nosignal = 0; /* enable signals */
+ setscroll();
+ drawscreen();
+ return;
+ };
+
+nxt: cursor(1, 6);
+ if (outstanding_taxes > 0)
+ lprintf("You presently owe %ld gp in taxes. ", (long) outstanding_taxes);
+ else
+ lprcat("You do not owe us any taxes. ");
+ cursor(1, 8);
+ if (c[GOLD] > 0)
+ lprintf("You have %6ld gp. ", (long) c[GOLD]);
+ else
+ lprcat("You have no gold pieces. ");
+ }
+}
diff --git a/larn/tok.c b/larn/tok.c
new file mode 100644
index 0000000..6bab595
--- /dev/null
+++ b/larn/tok.c
@@ -0,0 +1,235 @@
+/* $NetBSD: tok.c,v 1.11 2012/06/19 05:30:44 dholland Exp $ */
+
+/* tok.c Larn is copyrighted 1986 by Noah Morgan. */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: tok.c,v 1.11 2012/06/19 05:30:44 dholland Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include "header.h"
+#include "extern.h"
+
+/* Keystrokes (roughly) between checkpoints */
+#define CHECKPOINT_INTERVAL 400
+
+static char lastok = 0;
+int yrepcount = 0;
+#ifndef FLUSHNO
+#define FLUSHNO 5
+#endif /* FLUSHNO */
+static int flushno = FLUSHNO; /* input queue flushing threshold */
+#define MAXUM 52 /* maximum number of user re-named monsters */
+#define MAXMNAME 40 /* max length of a monster re-name */
+static char usermonster[MAXUM][MAXMNAME]; /* the user named monster
+ * name goes here */
+static u_char usermpoint = 0; /* the user monster pointer */
+
+/*
+ lexical analyzer for larn
+ */
+int
+yylex(void)
+{
+ char cc;
+ int ic;
+ if (hit2flag) {
+ hit2flag = 0;
+ yrepcount = 0;
+ return (' ');
+ }
+ if (yrepcount > 0) {
+ --yrepcount;
+ return (lastok);
+ } else
+ yrepcount = 0;
+ if (yrepcount == 0) {
+ bottomdo();
+ showplayer();
+ } /* show where the player is */
+ lflush();
+ while (1) {
+ c[BYTESIN]++;
+ /* check for periodic checkpointing */
+ if (ckpflag)
+ if ((c[BYTESIN] % CHECKPOINT_INTERVAL) == 0) {
+#ifndef DOCHECKPOINTS
+ savegame(ckpfile);
+#else
+ wait(0); /* wait for other forks to
+ * finish */
+ if (fork() == 0) {
+ savegame(ckpfile);
+ exit();
+ }
+#endif
+ }
+ do { /* if keyboard input buffer is too big, flush
+ * some of it */
+ ioctl(0, FIONREAD, &ic);
+ if (ic > flushno)
+ read(0, &cc, 1);
+ }
+ while (ic > flushno);
+
+ if (read(0, &cc, 1) != 1)
+ return (lastok = -1);
+
+ if (cc == 'Y' - 64) { /* control Y -- shell escape */
+ resetscroll();
+ clear();/* scrolling region, home, clear, no
+ * attributes */
+ if ((ic = fork()) == 0) { /* child */
+ execl("/bin/csh", "/bin/csh", NULL);
+ exit(1);
+ }
+ wait(0);
+ if (ic < 0) { /* error */
+ write(2, "Can't fork off a shell!\n", 25);
+ sleep(2);
+ }
+ setscroll();
+ return (lastok = 'L' - 64); /* redisplay screen */
+ }
+ if ((cc <= '9') && (cc >= '0')) {
+ yrepcount = yrepcount * 10 + cc - '0';
+ } else {
+ if (yrepcount > 0)
+ --yrepcount;
+ return (lastok = cc);
+ }
+ }
+}
+
+/*
+ * flushall() Function to flush all type-ahead in the input buffer
+ */
+void
+flushall(void)
+{
+ char cc;
+ int ic;
+ for (;;) { /* if keyboard input buffer is too big, flush
+ * some of it */
+ ioctl(0, FIONREAD, &ic);
+ if (ic <= 0)
+ return;
+ while (ic > 0) {
+ read(0, &cc, 1);
+ --ic;
+ } /* gobble up the byte */
+ }
+}
+
+/*
+ function to set the desired hardness
+ enter with hard= -1 for default hardness, else any desired hardness
+ */
+void
+sethard(int hard)
+{
+ int j, k, i;
+ struct monst *mp;
+
+ j = c[HARDGAME];
+ hashewon();
+ if (restorflag == 0) { /* don't set c[HARDGAME] if restoring game */
+ if (hard >= 0)
+ c[HARDGAME] = hard;
+ } else
+ c[HARDGAME] = j;/* set c[HARDGAME] to proper value if
+ * restoring game */
+
+ if ((k = c[HARDGAME]) != 0)
+ for (j = 0; j <= MAXMONST + 8; j++) {
+ mp = &monster[j];
+ i = ((6 + k) * mp->hitpoints + 1) / 6;
+ mp->hitpoints = (i < 0) ? 32767 : i;
+ i = ((6 + k) * mp->damage + 1) / 5;
+ mp->damage = (i > 127) ? 127 : i;
+ i = (10 * mp->gold) / (10 + k);
+ mp->gold = (i > 32767) ? 32767 : i;
+ i = mp->armorclass - k;
+ mp->armorclass = (i < -127) ? -127 : i;
+ i = (7 * mp->experience) / (7 + k) + 1;
+ mp->experience = (i <= 0) ? 1 : i;
+ }
+}
+
+/*
+ function to read and process the larn options file
+ */
+void
+readopts(void)
+{
+ const char *i;
+ int j, k;
+ int flag;
+
+ flag = 1; /* set to 0 if a name is specified */
+
+ if (lopen(optsfile) < 0) {
+ strcpy(logname, loginname);
+ return; /* user name if no character name */
+ }
+ i = " ";
+ while (*i) {
+ if ((i = lgetw()) == NULL)
+ break; /* check for EOF */
+ while ((*i == ' ') || (*i == '\t'))
+ i++; /* eat leading whitespace */
+
+ if (strcmp(i, "bold-objects") == 0)
+ boldon = 1;
+ else if (strcmp(i, "enable-checkpointing") == 0)
+ ckpflag = 1;
+ else if (strcmp(i, "inverse-objects") == 0)
+ boldon = 0;
+ else if (strcmp(i, "female") == 0)
+ sex = 0; /* male or female */
+ else if (strcmp(i, "monster:") == 0) { /* name favorite monster */
+ if ((i = lgetw()) == 0)
+ break;
+ strlcpy(usermonster[usermpoint], i, MAXMNAME);
+ if (usermpoint >= MAXUM)
+ continue; /* defined all of em */
+ if (isalpha(j = usermonster[usermpoint][0])) {
+ for (k = 1; k < MAXMONST + 8; k++) /* find monster */
+ if (monstnamelist[k] == j) {
+ monster[k].name = &usermonster[usermpoint++][0];
+ break;
+ }
+ }
+ } else if (strcmp(i, "male") == 0)
+ sex = 1;
+ else if (strcmp(i, "name:") == 0) { /* defining players name */
+ if ((i = lgetw()) == 0)
+ break;
+ strlcpy(logname, i, LOGNAMESIZE);
+ flag = 0;
+ } else if (strcmp(i, "no-introduction") == 0)
+ nowelcome = 1;
+ else if (strcmp(i, "no-beep") == 0)
+ nobeep = 1;
+ else if (strcmp(i, "process-name:") == 0) {
+ if ((i = lgetw()) == 0)
+ break;
+ strlcpy(psname, i, PSNAMESIZE);
+ } else if (strcmp(i, "play-day-play") == 0) {
+ /* bypass time restrictions: ignored */
+ } else if (strcmp(i, "savefile:") == 0) { /* defining savefilename */
+ if ((i = lgetw()) == 0)
+ break;
+ strcpy(savefilename, i);
+ flag = 0;
+ }
+ }
+ if (flag)
+ strcpy(logname, loginname);
+}
diff --git a/out b/out
new file mode 100644
index 0000000..6e0acd2
--- /dev/null
+++ b/out
@@ -0,0 +1,404 @@
+for dir in boggle bs cgram ching colorbars dab dm grdc hack hals_end larn paranoia rogue tetris; do ( cd $dir && pmake CFLAGS=" -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99" ); done
+pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 8: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkdict; "
+pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 10: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkindex; "
+all ===> boggle
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c bog.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c help.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c mach.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c prtable.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c timer.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -c word.c
+cc -o boggle bog.o help.o mach.o prtable.o timer.o word.o -lcurses -lbsd
+all ===> mkdict
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkdict/../boggle -c mkdict.c
+cc -o mkdict mkdict.o
+all ===> mkindex
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkindex/../boggle -c mkindex.c
+cc -o mkindex mkindex.o
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c bs.c
+cc -o bs bs.o -lncurses
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c cgram.c
+cc -o cgram cgram.o -lcurses
+all ===> ching
+all ===> castching
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/ching/castching/../include -c castching.c
+cc -o castching castching.o
+all ===> printching
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -Werror -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/ching/printching/../include -c printching.c
+cc -o printching printching.o
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c colorbars.c
+cc -o colorbars colorbars.o -lcurses
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c algor.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c board.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c main.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c human.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c box.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c player.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c gamescreen.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c ttyscrn.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c random.cc
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+cc1plus: warning: command line option ‘-std=gnu99’ is valid for C/ObjC but not for C++ [enabled by default]
+c++ -o dab algor.o board.o main.o human.o box.o player.o gamescreen.o ttyscrn.o random.o -lcurses -lm
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -I/usr.bin/who -DSUPPORT_UTMPX -DSUPPORT_UTMP -c dm.c
+cc -o dm dm.o
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c grdc.c
+cc -o grdc grdc.o -lncurses
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -o makedefs makedefs.c
+./makedefs /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/hack/def.objects.h > hack.onames.h
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c alloc.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.Decl.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.apply.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.bones.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.cmd.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.do.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.do_name.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.do_wear.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.dog.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.eat.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.end.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.engrave.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.fight.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.invent.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.ioctl.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.lev.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.main.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.makemon.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mhitu.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mklev.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mkmaze.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mkobj.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mkshop.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.mon.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.monst.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.o_init.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.objnam.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.options.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.pager.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.potion.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.pri.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.read.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.rip.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.rumors.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.save.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.search.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.shk.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.shknam.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.steal.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.termcap.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.timeout.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.topl.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.track.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.trap.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.tty.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.u_init.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.unix.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.vault.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.version.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.wield.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.wizard.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.worm.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.worn.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hack.zap.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c rnd.c
+cc -o hack alloc.o 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.u_init.o hack.unix.o hack.vault.o hack.version.o hack.wield.o hack.wizard.o hack.worm.o hack.worn.o hack.zap.o rnd.o -ltermcap -lbsd
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c hals_end.c
+cc -o hals_end hals_end.o
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c main.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c object.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c create.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c tok.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c display.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c global.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c data.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c io.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c monster.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c store.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c diag.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c help.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c config.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c nap.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c bill.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c scores.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c signal.c
+signal.c: In function ‘tstop’:
+signal.c:81:2: warning: ‘sigsetmask’ is deprecated (declared at /usr/include/signal.h:200) [-Wdeprecated-declarations]
+ sigsetmask(sigblock(0) & ~BIT(SIGTSTP));
+ ^
+signal.c:81:2: warning: ‘sigblock’ is deprecated (declared at /usr/include/signal.h:197) [-Wdeprecated-declarations]
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c action.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c moreobj.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c movem.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c regen.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c fortune.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DSYSV -DVER=12 -DSUBVER=0 -DNONAP -DUIDSCORE -DTERMIOS -DVT100 -c savelev.c
+cc -o larn main.o object.o create.o tok.o display.o global.o data.o io.o monster.o store.o diag.o help.o config.o nap.o bill.o scores.o signal.o action.o moreobj.o movem.o regen.o fortune.o savelev.o -lcurses -lbsd
+gcc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -o paranoia paranoia.c
+paranoia.c:33:1: warning: return type defaults to ‘int’ [enabled by default]
+ more()
+ ^
+paranoia.c: In function ‘more’:
+paranoia.c:41:3: warning: implicit declaration of function ‘character’ [-Wimplicit-function-declaration]
+ character();
+ ^
+paranoia.c: At top level:
+paranoia.c:47:1: warning: return type defaults to ‘int’ [enabled by default]
+ new_clone(resume)
+ ^
+paranoia.c:67:1: warning: return type defaults to ‘int’ [enabled by default]
+ dice_roll(number,faces)
+ ^
+paranoia.c:75:1: warning: return type defaults to ‘int’ [enabled by default]
+ instructions()
+ ^
+paranoia.c:96:1: warning: return type defaults to ‘int’ [enabled by default]
+ character()
+ ^
+paranoia.c:122:1: warning: return type defaults to ‘int’ [enabled by default]
+ choose(a,aptr,b,bptr)
+ ^
+paranoia.c:132:1: warning: return type defaults to ‘int’ [enabled by default]
+ page1()
+ ^
+paranoia.c:140:1: warning: return type defaults to ‘int’ [enabled by default]
+ page2()
+ ^
+paranoia.c:158:1: warning: return type defaults to ‘int’ [enabled by default]
+ page3()
+ ^
+paranoia.c:166:1: warning: return type defaults to ‘int’ [enabled by default]
+ page4()
+ ^
+paranoia.c:174:1: warning: return type defaults to ‘int’ [enabled by default]
+ page5()
+ ^
+paranoia.c:185:1: warning: return type defaults to ‘int’ [enabled by default]
+ page6()
+ ^
+paranoia.c:197:1: warning: return type defaults to ‘int’ [enabled by default]
+ page7()
+ ^
+paranoia.c:224:1: warning: return type defaults to ‘int’ [enabled by default]
+ page8()
+ ^
+paranoia.c:241:1: warning: return type defaults to ‘int’ [enabled by default]
+ page9()
+ ^
+paranoia.c:254:1: warning: return type defaults to ‘int’ [enabled by default]
+ page10()
+ ^
+paranoia.c:282:1: warning: return type defaults to ‘int’ [enabled by default]
+ page11()
+ ^
+paranoia.c:313:1: warning: return type defaults to ‘int’ [enabled by default]
+ page12()
+ ^
+paranoia.c:325:1: warning: return type defaults to ‘int’ [enabled by default]
+ page13()
+ ^
+paranoia.c:338:1: warning: return type defaults to ‘int’ [enabled by default]
+ page14()
+ ^
+paranoia.c:352:1: warning: return type defaults to ‘int’ [enabled by default]
+ page15()
+ ^
+paranoia.c:376:1: warning: return type defaults to ‘int’ [enabled by default]
+ page16()
+ ^
+paranoia.c:387:1: warning: return type defaults to ‘int’ [enabled by default]
+ page17()
+ ^
+paranoia.c:424:1: warning: return type defaults to ‘int’ [enabled by default]
+ page18()
+ ^
+paranoia.c:440:1: warning: return type defaults to ‘int’ [enabled by default]
+ page19()
+ ^
+paranoia.c:453:1: warning: return type defaults to ‘int’ [enabled by default]
+ page20()
+ ^
+paranoia.c:465:1: warning: return type defaults to ‘int’ [enabled by default]
+ page21()
+ ^
+paranoia.c:474:1: warning: return type defaults to ‘int’ [enabled by default]
+ page22()
+ ^
+paranoia.c:486:1: warning: return type defaults to ‘int’ [enabled by default]
+ page23()
+ ^
+paranoia.c:495:1: warning: return type defaults to ‘int’ [enabled by default]
+ page24()
+ ^
+paranoia.c:503:1: warning: return type defaults to ‘int’ [enabled by default]
+ page25()
+ ^
+paranoia.c:511:1: warning: return type defaults to ‘int’ [enabled by default]
+ page26()
+ ^
+paranoia.c:521:1: warning: return type defaults to ‘int’ [enabled by default]
+ page27()
+ ^
+paranoia.c:527:1: warning: return type defaults to ‘int’ [enabled by default]
+ page28()
+ ^
+paranoia.c:533:1: warning: return type defaults to ‘int’ [enabled by default]
+ page29()
+ ^
+paranoia.c:551:1: warning: return type defaults to ‘int’ [enabled by default]
+ page30()
+ ^
+paranoia.c:578:1: warning: return type defaults to ‘int’ [enabled by default]
+ page31()
+ ^
+paranoia.c:591:1: warning: return type defaults to ‘int’ [enabled by default]
+ page32()
+ ^
+paranoia.c:601:1: warning: return type defaults to ‘int’ [enabled by default]
+ page33()
+ ^
+paranoia.c:614:1: warning: return type defaults to ‘int’ [enabled by default]
+ page34()
+ ^
+paranoia.c:642:1: warning: return type defaults to ‘int’ [enabled by default]
+ page35()
+ ^
+paranoia.c:661:1: warning: return type defaults to ‘int’ [enabled by default]
+ page36()
+ ^
+paranoia.c:684:1: warning: return type defaults to ‘int’ [enabled by default]
+ page37()
+ ^
+paranoia.c:700:1: warning: return type defaults to ‘int’ [enabled by default]
+ page38()
+ ^
+paranoia.c:717:1: warning: return type defaults to ‘int’ [enabled by default]
+ page39()
+ ^
+paranoia.c:735:1: warning: return type defaults to ‘int’ [enabled by default]
+ page40()
+ ^
+paranoia.c: In function ‘page40’:
+paranoia.c:740:9: warning: unknown escape sequence: '\`' [enabled by default]
+ printf("The Troubleshooter looks around with a \`who me?\' expression on his face, but\n");
+ ^
+paranoia.c: At top level:
+paranoia.c:767:1: warning: return type defaults to ‘int’ [enabled by default]
+ page41()
+ ^
+paranoia.c:781:1: warning: return type defaults to ‘int’ [enabled by default]
+ page42()
+ ^
+paranoia.c:791:1: warning: return type defaults to ‘int’ [enabled by default]
+ page43()
+ ^
+paranoia.c:807:1: warning: return type defaults to ‘int’ [enabled by default]
+ page44()
+ ^
+paranoia.c:825:1: warning: return type defaults to ‘int’ [enabled by default]
+ page45()
+ ^
+paranoia.c:839:1: warning: return type defaults to ‘int’ [enabled by default]
+ page46()
+ ^
+paranoia.c:847:1: warning: return type defaults to ‘int’ [enabled by default]
+ page47()
+ ^
+paranoia.c:859:1: warning: return type defaults to ‘int’ [enabled by default]
+ page48()
+ ^
+paranoia.c:869:1: warning: return type defaults to ‘int’ [enabled by default]
+ page49()
+ ^
+paranoia.c:876:1: warning: return type defaults to ‘int’ [enabled by default]
+ page50()
+ ^
+paranoia.c:885:1: warning: return type defaults to ‘int’ [enabled by default]
+ page51()
+ ^
+paranoia.c:892:1: warning: return type defaults to ‘int’ [enabled by default]
+ page52()
+ ^
+paranoia.c:899:1: warning: return type defaults to ‘int’ [enabled by default]
+ page53()
+ ^
+paranoia.c:905:1: warning: return type defaults to ‘int’ [enabled by default]
+ page54()
+ ^
+paranoia.c:919:1: warning: return type defaults to ‘int’ [enabled by default]
+ page55()
+ ^
+paranoia.c:947:1: warning: return type defaults to ‘int’ [enabled by default]
+ page56()
+ ^
+paranoia.c:955:1: warning: return type defaults to ‘int’ [enabled by default]
+ page57()
+ ^
+paranoia.c:962:1: warning: return type defaults to ‘int’ [enabled by default]
+ next_page(this_page)
+ ^
+paranoia.c:1030:1: warning: return type defaults to ‘int’ [enabled by default]
+ main()
+ ^
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c hit.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c init.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c inventory.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c level.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c machdep.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c main.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c message.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c monster.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c move.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c object.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c pack.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c play.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c random.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c ring.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c room.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c save.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c score.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c spec_hit.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c throw.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c trap.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c use.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -DUNIX -c zap.c
+cc -o rogue hit.o init.o inventory.o level.o machdep.o main.o message.o monster.o move.o object.o pack.o play.o random.o ring.o room.o save.o score.o spec_hit.o throw.o trap.o use.o zap.o -lcurses -lbsd
+all ===> USD.doc
+groff -me rogue.me > paper.ps
+rogue.me:158: warning [p 2, 2.8i, div `|k', 0.6i]: cannot adjust line
+rogue.me:159: warning [p 2, 2.8i, div `|k', 0.7i]: cannot adjust line
+rogue.me:160: warning [p 2, 2.8i, div `|k', 0.9i]: cannot adjust line
+rogue.me:161: warning [p 2, 2.8i, div `|k', 1.1i]: cannot adjust line
+rogue.me:162: warning [p 2, 2.8i, div `|k', 1.2i]: cannot adjust line
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c input.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c screen.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c shapes.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c scores.c
+cc -I/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/include -include bsdcompat.h -std=gnu99 -c tetris.c
+cc -o tetris input.o screen.o shapes.o scores.o tetris.o -lcurses -lbsd
+[ -x boggle/mkdict/mkdict ] && cd boggle && pmake realall
+pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 8: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkdict; "
+pmake: "/home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/Makefile" line 10: warning: Couldn't read shell's output for "cd /home/urchlay/bsd-games-extra/bsd-games-extra-20150209/boggle/mkindex; "
+pmake: ${_MKTARGET_CREATE} expands to empty string
+rm -f dictionary
+./mkdict//mkdict < /usr/share/dict/words > dictionary
+38620 words
+pmake: ${_MKTARGET_CREATE} expands to empty string
+rm -f dictindex
+./mkindex//mkindex < dictionary > dictindex
diff --git a/paranoia/Makefile b/paranoia/Makefile
new file mode 100644
index 0000000..725d461
--- /dev/null
+++ b/paranoia/Makefile
@@ -0,0 +1,16 @@
+PROG= paranoia
+
+BINDIR=/usr/games
+LIBDIR= $(DESTDIR)/lib
+MANDIR= /usr/man/man6
+CC= gcc
+CFLAGS= -O2
+
+$(PROG):
+ $(CC) $(CFLAGS) -o $(PROG) $(PROG).c
+
+install: $(PROG)
+ install -c -o root -g root -m 755 $(PROG) $(DESTDIR)/$(BINDIR)
+
+clean:
+ rm -f $(PROG) core *.o
diff --git a/paranoia/README b/paranoia/README
new file mode 100644
index 0000000..fe31c76
--- /dev/null
+++ b/paranoia/README
@@ -0,0 +1,8 @@
+ This is a very simple, but amusing, adventure game. It is based on the
+role-playing game "Paranoia". It requires no instructions, as it is self-
+prompting, and has been written in the simplest C that I could imagine, so
+as to be implementable on almost any system. This also means that it could
+be translateable to any other language.
+
+ Note that since it uses random functions to simulate dice rolls, it will
+choose its own pathways in some circumstances, unlike many adventure games.
diff --git a/paranoia/README.linux b/paranoia/README.linux
new file mode 100644
index 0000000..81b87b5
--- /dev/null
+++ b/paranoia/README.linux
@@ -0,0 +1,10 @@
+8/25/93
+
+As usual the Makefiles were done completely from scratch. Verify that
+DESTDIR is properly set in the Makefile.
+
+Note: we currently do not have a man page for this program. The program
+is designed to not need a man page. Run it and see!
+
+Curt Olson
+curt@sledge.mn.org
diff --git a/paranoia/paranoia.c b/paranoia/paranoia.c
new file mode 100644
index 0000000..251e530
--- /dev/null
+++ b/paranoia/paranoia.c
@@ -0,0 +1,1036 @@
+/* This is a solo paranoia game taken from the Jan/Feb issue (No 77) of
+ "SpaceGamer/FantasyGamer" magazine.
+
+ Article by Sam Shirley.
+
+ Implemented in C on Vax 11/780 under UNIX by Tim Lister
+
+ This is a public domain adventure and may not be sold for profit */
+
+#define MOXIE 13
+#define AGILITY 15
+#define MAXKILL 7 /* The maximum number of UV's you can kill */
+
+int clone=1;
+int page=1;
+int computer_request=0;
+int ultra_violet=0;
+int action_doll=0;
+int hit_points=10;
+int read_letter=0;
+int plato_clone=3;
+int blast_door=0;
+int killer_count=0;
+
+char get_char()
+{
+ char c;
+ c=getchar();
+ if (c!='\n') while(getchar()!='\n');
+ return c;
+}
+
+more()
+{
+ printf("---------- More ----------");
+#ifdef DEBUG
+ printf("(page %d)",page);
+#endif
+ if (get_char()=='p')
+ {
+ character();
+ printf("---------- More ----------");
+ (void)get_char();
+ };
+}
+
+new_clone(resume)
+int resume;
+{
+ printf("\nClone %d just died.\n",clone);
+ if (++clone>6)
+ {
+ printf("\n*** You Lose ***\n\nAll your clones are dead. Your name has been stricken from the records.\n\n THE END\n");
+ return 0;
+ }
+ else
+ {
+ printf("Clone %d now activated.\n",clone);
+ ultra_violet=0;
+ action_doll=0;
+ hit_points=10;
+ killer_count=0;
+ return resume;
+ }
+}
+
+dice_roll(number,faces)
+int number, faces;
+{
+ int i,total=0;
+ for(i=number;i>0;i--) total+= rand()%faces+1;
+ return total;
+}
+
+instructions()
+{
+ printf("\n\n\n\nWelcome to Paranoia!\n\n");
+ printf("HOW TO PLAY:\n\n");
+ printf(" Just press <RETURN> until you are asked to make a choice.\n");
+ printf(" Select 'a' or 'b' or whatever for your choice, then press <RETURN>.\n");
+ printf(" You may select 'p' at any time to get a display of your statistics.\n");
+ printf(" Always choose the least dangerous option. Continue doing this until you win.\n");
+ printf(" At times you will use a skill or engage in combat and and will be informed of\n");
+ printf(" the outcome. These sections will be self explanatory.\n\n");
+ printf("HOW TO DIE:\n\n");
+ printf(" As Philo-R-DMD you will die at times during the adventure.\n");
+ printf(" When this happens you will be given an new clone at a particular location.\n");
+ printf(" The new Philo-R will usually have to retrace some of the old Philo-R\'s path;\n");
+ printf(" hopefully he won\'t make the same mistake as his predecessor.\n\n");
+ printf("HOW TO WIN:\n\n");
+ printf(" Simply complete the mission before you expend all six clones.\n");
+ printf(" If you make it, congratulations.\n");
+ printf(" If not, you can try again later.\n");
+}
+
+character()
+{
+ printf("===============================================================================\n");
+ printf("The Character : Philo-R-DMD %d\n", clone);
+ printf("Primary Attributes Secondary Attributes\n");
+ printf("===============================================================================\n");
+ printf("Strength ..................... 13 Carrying Capacity ................. 30\n");
+ printf("Endurance .................... 13 Damage Bonus ....................... 0\n");
+ printf("Agility ...................... 15 Macho Bonus ....................... -1\n");
+ printf("Manual Dexterity ............. 15 Melee Bonus ...................... +5%%\n");
+ printf("Moxie ........................ 13 Aimed Weapon Bonus .............. +10%%\n");
+ printf("Chutzpah ...................... 8 Comprehension Bonus .............. +4%%\n");
+ printf("Mechanical Aptitude .......... 14 Believability Bonus .............. +5%%\n");
+ printf("Power Index .................. 10 Repair Bonus ..................... +5%%\n");
+ printf("===============================================================================\n");
+ printf("Credits: 160 Secret Society: Illuminati Secret Society Rank: 1\n");
+ printf("Service Group: Power Services Mutant Power: Precognition\n");
+ printf("Weapon: laser pistol; to hit, 40%%; type, L; Range, 50m; Reload, 6r; Malfnt, 00\n");
+ printf("Skills: Basics 1(20%%), Aimed Weapon Combat 2(35%%), Laser 3(40%%),\n Personal Development 1(20%%), Communications 2(29%%), Intimidation 3(34%%)\n");
+ printf("Equipment: Red Reflec Armour, Laser Pistol, Laser Barrel (red),\n");
+ printf(" Notebook & Stylus, Knife, Com Unit 1, Jump suit,\n");
+ printf(" Secret Illuminati Eye-In-The-Pyramid(tm) Decoder ring,\n");
+ printf(" Utility Belt & Pouches\n");
+ printf("===============================================================================\n");
+}
+
+choose(a,aptr,b,bptr)
+int a,b;
+char *aptr, *bptr;
+{
+ printf("\nSelect \'a\' or \'b\' :\n");
+ printf(" a - %s.\n b - %s.\n", aptr, bptr);
+ if (get_char()=='a') return a;
+ else return b;
+}
+
+page1()
+{
+ printf(" You wake up face down on the red and pink checked E-Z-Kleen linoleum floor.\n");
+ printf(" You recognise the pattern, it\'s the type preferred in the internal security\nbriefing cells. When you finally look around you, you see that you are alone\n");
+ printf("in a large mission briefing room.\n");
+ return 57;
+}
+
+page2()
+{
+ printf("\"Greetings,\" says the kindly Internal Security self incrimination expert who\n");
+ printf("meets you at the door, \"How are we doing today?\" He offers you a doughnut\n");
+ printf("and coffee and asks what brings you here. This doesn\'t seem so bad, so you\n");
+ printf("tell him that you have come to confess some possible security lapses. He\n");
+ printf("smiles knowingly, deftly catching your coffee as you slump to the floor.\n");
+ printf("\"Nothing to be alarmed about; it\'s just the truth serum,\" he says,\n");
+ printf("dragging you back into a discussion room.\n");
+ printf("The next five hours are a dim haze, but you can recall snatches of conversation\n");
+ printf("about your secret society, your mutant power, and your somewhat paranoid\n");
+ printf("distrust of The Computer. This should explain why you are hogtied and moving\n");
+ printf("slowly down the conveyer belt towards the meat processing unit in Food\n");
+ printf("Services.\n");
+ if (computer_request==1) return new_clone(45);
+ else return new_clone(32);
+}
+
+page3()
+{
+ printf("You walk to the nearest Computer terminal and request more information about\n");
+ printf("Christmas. The Computer says, \"That is an A-1 ULTRAVIOLET ONLY IMMEDIATE\n");
+ printf("TERMINATION classified topic. What is your clearance please, Troubleshooter?\"\n");
+ return choose(4,"You give your correct clearance",5,"You lie and claim Ultraviolet clearance");
+}
+
+page4()
+{
+ printf("\"That is classified information, Troubleshooter, thank you for your inquiry.\n");
+ printf(" Please report to an Internal Security self incrimination station as soon as\n");
+ printf(" possible.\"\n");
+ return 9;
+}
+
+page5()
+{
+ printf("The computer says, \"Troubleshooter, you are not wearing the correct colour\n");
+ printf("uniform. You must put on an Ultraviolet uniform immediately. I have seen to\n");
+ printf("your needs and ordered one already; it will be here shortly. Please wait with\n");
+ printf("your back to the wall until it arrives.\" In less than a minute an infrared\n");
+ printf("arrives carrying a white bundle. He asks you to sign for it, then hands it to\n");
+ printf("you and stands back, well outside of a fragmentation grenade\'s blast radius.\n");
+ return choose(6, "You open the package and put on the uniform", 7, "You finally come to your senses and run for it");
+}
+
+page6()
+{
+ printf("The uniform definitely makes you look snappy and pert. It really looks\n");
+ printf("impressive, and even has the new lopsided lapel fashion that you admire so\n");
+ printf("much. What\'s more, citizens of all ranks come to obsequious attention as you\n");
+ printf("walk past. This isn\'t so bad being an Ultraviolet. You could probably come\n");
+ printf("to like it, given time.\n");
+ printf("The beeping computer terminal interrupts your musings.\n");
+ ultra_violet=1;
+ return 8;
+}
+
+page7()
+{
+ printf("The corridor lights dim and are replaced by red battle lamps as the Security\n");
+ printf("Breach alarms howl all around you. You run headlong down the corridor and\n");
+ printf("desperately windmill around a corner, only to collide with a squad of 12 Blue\n");
+ printf("clearance Vulture squadron soldiers. \"Stop, Slime Face,\" shouts the\n");
+ printf("commander, \"or there won\'t be enough of you left for a tissue sample.\"\n");
+ printf("\"All right, soldiers, stuff the greasy traitor into the uniform,\" he orders,\n");
+ printf("waving the business end of his blue laser scant inches from your nose.\n");
+ printf("With his other hand he shakes open a white bundle to reveal a pristine new\n");
+ printf("Ultraviolet citizen's uniform.\n");
+ printf("One of the Vulture squadron Troubleshooters grabs you by the neck in the\n");
+ printf("exotic and very painful Vulture Clamp(tm) death grip (you saw a special about\n");
+ printf("it on the Teela O\'Malley show), while the rest tear off your clothes and\n");
+ printf("force you into the Ultraviolet uniform. The moment you are dressed they step\n");
+ printf("clear and stand at attention.\n");
+ printf("\"Thank you for your cooperation, sir,\" says the steely eyed leader of the\n");
+ printf("Vulture Squad. \"We will be going about our business now.\" With perfect\n");
+ printf("timing the Vultures wheel smartly and goosestep down the corridor.\n");
+ printf("Special Note: don\'t make the mistake of assuming that your skills have\n");
+ printf("improved any because of the uniform; you\'re only a Red Troubleshooter\n");
+ printf("traitorously posing as an Ultraviolet, and don\'t you forget it!\n");
+ printf("Suddenly, a computer terminal comes to life beside you.\n");
+ ultra_violet=1;
+ return 8;
+}
+
+page8()
+{
+ printf("\"Now, about your question, citizen. Christmas was an old world marketing ploy\n");
+ printf("to induce lower clearance citizens to purchase vast quantities of goods, thus\n");
+ printf("accumulation a large amount of credit under the control of a single class of\n");
+ printf("citizen known as Retailers. The strategy used is to imply that all good\n");
+ printf("citizens give gifts during Christmas, thus if one wishes to be a valuable\n");
+ printf("member of society one must also give gifts during Christmas. More valuable\n");
+ printf("gifts make one a more valuable member, and thus did the Retailers come to\n");
+ printf("control a disproportionate amount of the currency. In this way Christmas\n");
+ printf("eventually caused the collapse of the old world. Understandably, Christmas\n");
+ printf("has been declared a treasonable practice in Alpha Complex.\n");
+ printf("Thank you for your inquiry.\"\n");
+ printf("You continue on your way to GDH7-beta.\n");
+ return 10;
+}
+
+page9()
+{
+ int choice;
+ printf("As you walk toward the tubecar that will take you to GDH7-beta, you pass one\n");
+ printf("of the bright blue and orange Internal Security self incrimination stations.\n");
+ printf("Inside, you can see an IS agent cheerfully greet an infrared citizen and then\n");
+ printf("lead him at gunpoint into one of the rubber lined discussion rooms.\n");
+ choice=choose(2,"You decide to stop here and chat, as ordered by The Computer",10,"You just continue blithely on past");
+ if (choice==2) computer_request = 1;
+ else computer_request = 0;
+ return choice;
+}
+
+page10()
+{
+ int choice;
+ printf("You stroll briskly down the corridor, up a ladder, across an unrailed catwalk,\n");
+ printf("under a perilously swinging blast door in urgent need of repair, and into\n");
+ printf("tubecar grand central. This is the bustling hub of Alpha Complex tubecar\n");
+ printf("transportation. Before you spreads a spaghetti maze of magnalift tube tracks\n");
+ printf("and linear accelerators. You bravely study the specially enhanced 3-D tube\n");
+ printf("route map; you wouldn\'t be the first Troubleshooter to take a fast tube ride\n");
+ printf("to nowhere.\n");
+ if (ultra_violet==0)
+ {
+ choice=choose(3,"You decide to ask The Computer about Christmas using a nearby terminal",10,"You think you have the route worked out, so you\'ll board a tube train");
+ if (choice==3) return choice;
+ };
+ printf("You nervously select a tubecar and step aboard.\n");
+ if (dice_roll(2,10)<MOXIE)
+ {
+ printf("You just caught a purple line tubecar.\n");
+ return 13;
+ }
+ else
+ {
+ printf("You just caught a brown line tubecar.\n");
+ return 48;
+ }
+}
+
+page11()
+{
+ printf("The printing on the folder says \"Experimental Self Briefing.\"\n");
+ printf("You open it and begin to read the following:\n");
+ printf("Step 1: Compel the briefing subject to attend the briefing.\n");
+ printf(" Note: See Experimental Briefing Sub Form Indigo-WY-2,\n");
+ printf(" \'Experimental Self Briefing Subject Acquisition Through The Use Of\n");
+ printf(" Neurotoxin Room Foggers.\'\n");
+ printf("Step 2: Inform the briefing subject that the briefing has begun.\n");
+ printf(" ATTENTION: THE BRIEFING HAS BEGUN.\n");
+ printf("Step 3: Present the briefing material to the briefing subject.\n");
+ printf(" GREETINGS TROUBLESHOOTER.\n");
+ printf(" YOU HAVE BEEN SPECIALLY SELECTED TO SINGLEHANDEDLY\n");
+ printf(" WIPE OUT A DEN OF TRAITOROUS CHRISTMAS ACTIVITY. YOUR MISSION IS TO\n");
+ printf(" GO TO GOODS DISTRIBUTION HALL 7-BETA AND ASSESS ANY CHRISTMAS ACTIVITY\n");
+ printf(" YOU FIND THERE. YOU ARE TO INFILTRATE THESE CHRISTMAS CELEBRANTS,\n");
+ printf(" LOCATE THEIR RINGLEADER, AN UNKNOWN MASTER RETAILER, AND BRING HIM\n");
+ printf(" BACK FOR EXECUTION AND TRIAL. THANK YOU. THE COMPUTER IS YOUR FRIEND.\n");
+ printf("Step 4: Sign the briefing subject\'s briefing release form to indicate that\n");
+ printf(" the briefing subject has completed the briefing.\n");
+ printf(" ATTENTION: PLEASE SIGN YOUR BRIEFING RELEASE FORM.\n");
+ printf("Step 5: Terminate the briefing\n");
+ printf(" ATTENTION: THE BRIEFING IS TERMINATED.\n");
+ more();
+ printf("You walk to the door and hold your signed briefing release form up to the\n");
+ printf("plexiglass window. A guard scrutinises it for a moment and then slides back\n");
+ printf("the megabolts holding the door shut. You are now free to continue the\n");
+ printf("mission.\n");
+ return choose(3,"You wish to ask The Computer for more information about Christmas",10,"You have decided to go directly to Goods Distribution Hall 7-beta");
+}
+
+page12()
+{
+ printf("You walk up to the door and push the button labelled \"push to exit.\"\n");
+ printf("Within seconds a surly looking guard shoves his face into the small plexiglass\n");
+ printf("window. You can see his mouth forming words but you can\'t hear any of them.\n");
+ printf("You just stare at him blankly for a few moments until he points down to a\n");
+ printf("speaker on your side of the door. When you put your ear to it you can barely\n");
+ printf("hear him say, \"Let\'s see your briefing release form, bud. You aren\'t\n");
+ printf("getting out of here without it.\"\n");
+ return choose(11,"You sit down at the table and read the Orange packet",57,"You stare around the room some more");
+}
+
+page13()
+{
+ printf("You step into the shiny plasteel tubecar, wondering why the shape has always\n");
+ printf("reminded you of bullets. The car shoots forward the instant your feet touch\n");
+ printf("the slippery gray floor, pinning you immobile against the back wall as the\n");
+ printf("tubecar careens toward GDH7-beta. Your only solace is the knowledge that it\n");
+ printf("could be worse, much worse.\n");
+ printf("Before too long the car comes to a stop. You can see signs for GDH7-beta\n");
+ printf("through the window. With a little practice you discover that you can crawl\n");
+ printf("to the door and pull open the latch.\n");
+ return 14;
+}
+
+page14()
+{
+ printf("You manage to pull yourself out of the tubecar and look around. Before you is\n");
+ printf("one of the most confusing things you have ever seen, a hallway that is\n");
+ printf("simultaneously both red and green clearance. If this is the result of\n");
+ printf("Christmas then it\'s easy to see the evils inherent in its practice.\n");
+ printf("You are in the heart of a large goods distribution centre. You can see all\n");
+ printf("about you evidence of traitorous secret society Christmas celebration; rubber\n");
+ printf("faced robots whiz back and forth selling toys to holiday shoppers, simul-plast\n");
+ printf("wreaths hang from every light fixture, while ahead in the shadows is a citizen\n");
+ printf("wearing a huge red synthetic flower.\n");
+ return 22;
+}
+
+page15()
+{
+ printf("You are set upon by a runty robot with a queer looking face and two pointy\n");
+ printf("rubber ears poking from beneath a tattered cap. \"Hey mister,\" it says,\n");
+ printf("\"you done all your last minute Christmas shopping? I got some real neat junk\n");
+ printf("here. You don\'t wanna miss the big day tommorrow, if you know what I mean.\"\n");
+ printf("The robot opens its bag to show you a pile of shoddy Troubleshooter dolls. It\n");
+ printf("reaches in and pulls out one of them. \"Look, these Action Troubleshooter(tm)\n");
+ printf("dolls are the neatest thing. This one\'s got moveable arms and when you\n");
+ printf("squeeze him, his little rifle squirts realistic looking napalm. It\'s only\n");
+ printf("50 credits. Oh yeah, Merry Christmas.\"\n");
+ printf("\nSelect \'a\', \'b\' or \'c\' :\n");
+ printf(" a - You decide to buy the doll.\n");
+ printf(" b - You shoot the robot.\n");
+ printf(" c - You ignore the robot and keep searching the hall.\n");
+ switch(get_char())
+ {
+ case 'a' : return 16;
+ case 'b' : return 17;
+ case 'c' :
+ default : return 22;
+ }
+}
+
+page16()
+{
+ printf("The doll is a good buy for fifty credits; it will make a fine Christmas present\n");
+ printf("for one of your friends. After the sale the robot rolls away. You can use\n");
+ printf("the doll later in combat. It works just like a cone rifle firing napalm,\n");
+ printf("except that occasionally it will explode and blow the user to smithereens.\n");
+ printf("But don\'t let that stop you.\n");
+ action_doll=1;
+ return 22;
+}
+
+page17()
+{
+ int i, robot_hp=15;
+ printf("You whip out your laser and shoot the robot, but not before it squeezes the\n");
+ printf("toy at you. The squeeze toy has the same effect as a cone rifle firing napalm,\n");
+ printf("and the elfbot\'s armour has no effect against your laser.\n");
+ for(i=0;i<2;i++)
+ {
+ if(dice_roll(1,100)<=25)
+ {
+ printf("You have been hit!\n");
+ hit_points-= dice_roll(1,10);
+ if (hit_points<=0) return new_clone(45);
+ }
+ else printf("It missed you, but not by much!\n");
+ if(dice_roll(1,100)<=40)
+ {
+ printf("You zapped the little bastard!\n");
+ robot_hp-= dice_roll(2,10);
+ if (robot_hp<=0)
+ {
+ printf("You wasted it! Good shooting!\n");
+ printf("You will need more evidence, so you search GDH7-beta further\n");
+ if (hit_points<10) printf("after the GDH medbot has patched you up.\n");
+ hit_points=10;
+ return 22;
+ }
+ }
+ else printf("Damn! You missed!\n");
+ };
+ printf("It tried to fire again, but the toy exploded and demolished it.\n");
+ printf("You will need more evidence, so you search GDH7-beta further\n");
+ if (hit_points<10) printf("after the GDH medbot has patched you up.\n");
+ hit_points=10;
+ return 22;
+}
+
+page18()
+{
+ printf("You walk to the centre of the hall, ogling like an infrared fresh from the\n");
+ printf("clone vats. Towering before you is the most unearthly thing you have ever\n");
+ printf("seen, a green multi armed mutant horror hulking 15 feet above your head.\n");
+ printf("Its skeletal body is draped with hundreds of metallic strips (probably to\n");
+ printf("negate the effects of some insidious mutant power), and the entire hideous\n");
+ printf("creature is wrapped in a thousand blinking hazard lights. It\'s times like\n");
+ printf("this when you wish you\'d had some training for this job. Luckily the\n");
+ printf("creature doesn\'t take notice of you but stands unmoving, as though waiting for\n");
+ printf("a summons from its dark lord, the Master Retailer.\n");
+ printf("WHAM, suddenly you are struck from behind.\n");
+ if (dice_roll(2,10)<AGILITY) return 19;
+ else return 20;
+}
+
+page19()
+{
+ printf("Quickly you regain your balance, whirl and fire your laser into the Ultraviolet\n");
+ printf("citizen behind you. For a moment your heart leaps to your throat, then you\n");
+ printf("realise that he is indeed dead and you will be the only one filing a report on\n");
+ printf("this incident. Besides, he was participating in this traitorous Christmas\n");
+ printf("shopping, as is evident from the rain of shoddy toys falling all around you.\n");
+ printf("Another valorous deed done in the service of The Computer!\n");
+ if (++killer_count>(MAXKILL-clone)) return 21;
+ if (read_letter==1) return 22;
+ return choose(34,"You search the body, keeping an eye open for Internal Security",22,"You run away like the cowardly dog you are");
+}
+
+page20()
+{
+ printf("Oh no! you can\'t keep your balance. You\'re falling, falling head first into\n");
+ printf("the Christmas beast\'s gaping maw. It\'s a valiant struggle; you think you are\n");
+ printf("gone when its poisonous needles dig into your flesh, but with a heroic effort\n");
+ printf("you jerk a string of lights free and jam the live wires into the creature\'s\n");
+ printf("spine. The Christmas beast topples to the ground and begins to burn, filling\n");
+ printf("the area with a thick acrid smoke. It takes only a moment to compose yourself,\n");
+ printf("and then you are ready to continue your search for the Master Retailer.\n");
+ return 22;
+}
+
+page21()
+{
+ printf("You have been wasting the leading citizens of Alpha Complex at a prodigious\n");
+ printf("rate. This has not gone unnoticed by the Internal Security squad at GDH7-beta.\n");
+ printf("Suddenly, a net of laser beams spear out of the gloomy corners of the hall,\n");
+ printf("chopping you into teeny, weeny bite size pieces.\n");
+ return new_clone(45);
+}
+
+page22()
+{
+ printf("You are searching Goods Distribution Hall 7-beta.\n");
+ switch(dice_roll(1,4))
+ {
+ case 1: return 18;
+ case 2: return 15;
+ case 3: return 18;
+ case 4: return 29;
+ }
+}
+
+page23()
+{
+ printf("You go to the nearest computer terminal and declare yourself a mutant.\n");
+ printf("\"A mutant, he\'s a mutant,\" yells a previously unnoticed infrared who had\n");
+ printf("been looking over your shoulder. You easily gun him down, but not before a\n");
+ printf("dozen more citizens take notice and aim their weapons at you.\n");
+ return choose(28,"You tell them that it was really only a bad joke",24,"You want to fight it out, one against twelve");
+}
+
+page24()
+{
+ printf("Golly, I never expected someone to pick this. I haven\'t even designed\n");
+ printf("the 12 citizens who are going to make a sponge out of you. Tell you what,\n");
+ printf("I\'ll give you a second chance.\n");
+ return choose(28,"You change your mind and say it was only a bad joke",25,"You REALLY want to shoot it out");
+}
+
+page25()
+{
+ printf("Boy, you really can\'t take a hint!\n");
+ printf("They\'re closing in. Their trigger fingers are twitching, they\'re about to\n");
+ printf("shoot. This is your last chance.\n");
+ return choose(28,"You tell them it was all just a bad joke",26,"You are going to shoot");
+}
+
+page26()
+{
+ printf("You can read the cold, sober hatred in their eyes (They really didn\'t think\n");
+ printf("it was funny), as they tighten the circle around you. One of them shoves a\n");
+ printf("blaster up your nose, but that doesn\'t hurt as much as the multi-gigawatt\n");
+ printf("carbonium tipped food drill in the small of your back.\n");
+ printf("You spend the remaining micro-seconds of your life wondering what you did wrong\n");
+ return new_clone(32);
+}
+
+page27()
+{
+ /* doesn't exist. Can't happen with computer version.
+ designed to catch dice cheats */
+}
+
+page28()
+{
+ printf("They don\'t think it\'s funny.\n");
+ return 26;
+}
+
+page29()
+{
+ printf("\"Psst, hey citizen, come here. Pssfft,\" you hear. When you peer around\n");
+ printf("you can see someone\'s dim outline in the shadows. \"I got some information\n");
+ printf("on the Master Retailer. It\'ll only cost you 30 psst credits.\"\n");
+ printf("\nSelect \'a\', \'b\' or \'c\' :\n");
+ printf(" a - You pay the 30 credits for the info.\n");
+ printf(" b - You would rather threaten him for the information.\n");
+ printf(" c - You ignore him and walk away.\n");
+ switch(get_char())
+ {
+ case 'a' : return 30;
+ case 'b' : return 31;
+ case 'c' :
+ default : return 22;
+ }
+}
+
+page30()
+{
+ printf("You step into the shadows and offer the man a thirty credit bill. \"Just drop\n");
+ printf("it on the floor,\" he says. \"So you\'re looking for the Master Retailer, pssfft?\n");
+ printf("I\'ve seen him, he\'s a fat man in a fuzzy red and white jump suit. They say\n");
+ printf("he\'s a high programmer with no respect for proper security. If you want to\n");
+ printf("find him then pssfft step behind me and go through the door.\"\n");
+ printf("Behind the man is a reinforced plasteel blast door. The centre of it has been\n");
+ printf("buckled toward you in a manner you only saw once before when you were field\n");
+ printf("testing the rocket assist plasma slingshot (you found it easily portable but\n");
+ printf("prone to misfire). Luckily it isn\'t buckled too far for you to make out the\n");
+ printf("warning sign. WARNING!! Don\'t open this door or the same thing will happen to\n");
+ printf("you. Opening this door is a capital offense. Do not do it. Not at all. This\n");
+ printf("is not a joke.\n");
+ printf("\nSelect \'a\', \'b\' or \'c\' :\n");
+ printf(" a - You use your Precognition mutant power on opening the door.\n");
+ printf(" b - You just go through the door anyway.\n");
+ printf(" c - You decide it\'s too dangerous and walk away.\n");
+ switch(get_char())
+ {
+ case 'a' : return 56;
+ case 'b' : return 33;
+ case 'c' :
+ default : return 22;
+ }
+}
+
+page31()
+{
+ printf("Like any good troubleshooter you make the least expensive decision and threaten\n");
+ printf("him for information. With lightning like reflexes you whip out your laser and\n");
+ printf("stick it up his nose. \"Talk, you traitorous Christmas celebrator, or who nose\n");
+ printf("what will happen to you, yuk yuk,\" you pun menacingly, and then you notice\n");
+ printf("something is very wrong. He doesn\'t have a nose. As a matter of fact he\'s\n");
+ printf("made of one eighth inch cardboard and your laser is sticking through the other\n");
+ printf("side of his head. \"Are you going to pay?\" says his mouth speaker,\n");
+ printf("\"or are you going to pssfft go away stupid?\"\n");
+ return choose(30,"You pay the 30 credits",22,"You pssfft go away stupid");
+}
+
+page32()
+{
+ printf("Finally it\'s your big chance to prove that you\'re as good a troubleshooter\n");
+ printf("as your previous clone. You walk briskly to mission briefing and pick up your\n");
+ printf("previous clone\'s personal effects and notepad. After reviewing the notes you\n");
+ printf("know what has to be done. You catch the purple line to Goods Distribution Hall\n");
+ printf("7-beta and begin to search for the blast door.\n");
+ return 22;
+}
+
+page33()
+{
+ blast_door=1;
+ printf("You release the megabolts on the blast door, then strain against it with your\n");
+ printf("awesome strength. Slowly the door creaks open. You bravely leap through the\n");
+ printf("opening and smack your head into the barrel of a 300 mm \'ultra shock\' class\n");
+ printf("plasma cannon. It\'s dark in the barrel now, but just before your head got\n");
+ printf("stuck you can remember seeing a group of technicians anxiously watch you leap\n");
+ printf("into the room.\n");
+ if (ultra_violet==1) return 35;
+ else return 36;
+}
+
+page34()
+{
+ printf("You have found a sealed envelope on the body. You open it and read:\n");
+ printf("\"WARNING: Ultraviolet Clearance ONLY. DO NOT READ.\n");
+ printf("Memo from Chico-U-MRX4 to Harpo-U-MRX5.\n");
+ printf("The planned takeover of the Troubleshooter Training Course goes well, Comrade.\n");
+ printf("Once we have trained the unwitting bourgeois troubleshooters to work as\n");
+ printf("communist dupes, the overthrow of Alpha Complex will be unstoppable. My survey\n");
+ printf("of the complex has convinced me that no one suspects a thing; soon it will be\n");
+ printf("too late for them to oppose the revolution. The only thing that could possibly\n");
+ printf("impede the people\'s revolution would be someone alerting The Computer to our\n");
+ printf("plans (for instance, some enterprising Troubleshooter could tell The Computer\n");
+ printf("that the communists have liberated the Troubleshooter Training Course and plan\n");
+ printf("to use it as a jumping off point from which to undermine the stability of all\n");
+ printf("Alpha Complex), but as we both know, the capitalistic Troubleshooters would\n");
+ printf("never serve the interests of the proletariat above their own bourgeois desires.\n");
+ printf("P.S. I\'m doing some Christmas shopping later today. Would you like me to pick\n");
+ printf("you up something?\"\n");
+ more();
+ printf("When you put down the memo you are overcome by that strange deja\'vu again.\n");
+ printf("You see yourself talking privately with The Computer. You are telling it all\n");
+ printf("about the communists\' plan, and then the scene shifts and you see yourself\n");
+ printf("showered with awards for foiling the insidious communist plot to take over the\n");
+ printf("complex.\n");
+ read_letter=1;
+ return choose(46,"You rush off to the nearest computer terminal to expose the commies",22,"You wander off to look for more evidence");
+}
+
+page35()
+{
+ printf("\"Oh master,\" you hear through the gun barrel, \"where have you been? It is\n");
+ printf("time for the great Christmas gifting ceremony. You had better hurry and get\n");
+ printf("the costume on or the trainee may begin to suspect.\" For the second time\n");
+ printf("today you are forced to wear attire not of your own choosing. They zip the\n");
+ printf("suit to your chin just as you hear gunfire erupt behind you.\n");
+ printf("\"Oh no! Who left the door open? The commies will get in. Quick, fire the\n");
+ printf("laser cannon or we\'re all doomed.\"\n");
+ printf("\"Too late you capitalist swine, the people\'s revolutionary strike force claims\n");
+ printf("this cannon for the proletariat\'s valiant struggle against oppression. Take\n");
+ printf("that, you running dog imperialist lackey. ZAP, KAPOW\"\n");
+ printf("Just when you think that things couldn\'t get worse, \"Aha, look what we have\n");
+ printf("here, the Master Retailer himself with his head caught in his own cannon. His\n");
+ printf("death will serve as a symbol of freedom for all Alpha Complex.\n");
+ printf("Fire the cannon.\"\n");
+ return new_clone(32);
+}
+
+page36()
+{
+ printf("\"Congratulations, troubleshooter, you have successfully found the lair of the\n");
+ printf("Master Retailer and completed the Troubleshooter Training Course test mission,\"\n");
+ printf("a muffled voice tells you through the barrel. \"Once we dislodge your head\n");
+ printf("from the barrel of the \'Ultra Shock\' plasma cannon you can begin with the\n");
+ printf("training seminars, the first of which will concern the 100%% accurate\n");
+ printf("identification and elimination of unregistered mutants. If you have any\n");
+ printf("objections please voice them now.\"\n");
+ printf("\nSelect \'a\', \'b\' or \'c\' :\n");
+ printf(" a - You appreciate his courtesy and voice an objection.\n");
+ printf(" b - After your head is removed from the cannon, you register as a mutant.\n");
+ printf(" c - After your head is removed from the cannon, you go to the unregistered\n");
+ printf(" mutant identification and elimination seminar.\n");
+ switch(get_char())
+ {
+ case 'a' : return new_clone(32);
+ case 'b' : return 23;
+ case 'c' :
+ default : return 37;
+ }
+}
+
+page37()
+{
+ printf("\"Come with me please, Troubleshooter,\" says the Green clearance technician\n");
+ printf("after he has dislodged your head from the cannon. \"You have been participating\n");
+ printf("in the Troubleshooter Training Course since you got off the tube car in\n");
+ printf("GDH7-beta,\" he explains as he leads you down a corridor. \"The entire\n");
+ printf("Christmas assignment was a test mission to assess your current level of\n");
+ printf("training. You didn\'t do so well. We\'re going to start at the beginning with\n");
+ printf("the other student. Ah, here we are, the mutant identification and elimination\n");
+ printf("lecture.\" He shows you into a vast lecture hall filled with empty seats.\n");
+ printf("There is only one other student here, a Troubleshooter near the front row\n");
+ printf("playing with his Action Troubleshooter(tm) figure. \"Find a seat and I will\n");
+ printf("begin,\" says the instructor.\n");
+ return 38;
+}
+
+page38()
+{
+ printf("\"I am Plato-B-PHI%d, head of mutant propaganda here at the training course.\n",plato_clone);
+ printf("If you have any questions about mutants please come to me. Today I will be\n");
+ printf("talking about mutant detection. Detecting mutants is very easy. One simply\n");
+ printf("watches for certain tell tale signs, such as the green scaly skin, the third\n");
+ printf("arm growing from the forehead, or other similar disfigurements so common with\n");
+ printf("their kind. There are, however, a few rare specimens that show no outward sign\n");
+ printf("of their treason. This has been a significant problem, so our researchers have\n");
+ printf("been working on a solution. I would like a volunteer to test this device,\"\n");
+ printf("he says, holding up a ray gun looking thing. \"It is a mutant detection ray.\n");
+ printf("This little button detects for mutants, and this big button stuns them once\n");
+ printf("they are discovered. Who would like to volunteer for a test?\"\n");
+ printf("The Troubleshooter down the front squirms deeper into his chair.\n");
+ return choose(39,"You volunteer for the test",40,"You duck behind a chair and hope the instructor doesn\'t notice you");
+}
+
+page39()
+{
+ printf("You bravely volunteer to test the mutant detection gun. You stand up and walk\n");
+ printf("down the steps to the podium, passing a very relieved Troubleshooter along the\n");
+ printf("way. When you reach the podium Plato-B-PHI hands you the mutant detection gun\n");
+ printf("and says, \"Here, aim the gun at that Troubleshooter and push the small button.\n");
+ printf("If you see a purple light, stun him.\" Grasping the opportunity to prove your\n");
+ printf("worth to The Computer, you fire the mutant detection ray at the Troubleshooter.\n");
+ printf("A brilliant purple nimbus instantly surrounds his body. You slip your finger\n");
+ printf("to the large stun button and he falls writhing to the floor.\n");
+ printf("\"Good shot,\" says the instructor as you hand him the mutant detection gun,\n");
+ printf("\"I\'ll see that you get a commendation for this. It seems you have the hang\n");
+ printf("of mutant detection and elimination. You can go on to the secret society\n");
+ printf("infiltration class. I\'ll see that the little mutie gets packaged for\n");
+ printf("tomorrow\'s mutant dissection class.\"\n");
+ return 41;
+}
+
+page40()
+{
+ printf("You breathe a sigh of relief as Plato-B-PHI picks on the other Troubleshooter.\n");
+ printf("\"You down here in the front,\" says the instructor pointing at the other\n");
+ printf("Troubleshooter, \"you\'ll make a good volunteer. Please step forward.\"\n");
+ printf("The Troubleshooter looks around with a \`who me?\' expression on his face, but\n");
+ printf("since he is the only one visible in the audience he figures his number is up.\n");
+ printf("He walks down to the podium clutching his Action Troubleshooter(tm) doll before\n");
+ printf("him like a weapon. \"Here,\" says Plato-B-PHI, \"take the mutant detection ray\n");
+ printf("and point it at the audience. If there are any mutants out there we\'ll know\n");
+ printf("soon enough.\" Suddenly your skin prickles with static electricity as a bright\n");
+ printf("purple nimbus surrounds your body. \"Ha Ha, got one,\" says the instructor.\n");
+ printf("\"Stun him before he gets away.\"\n");
+ more();
+ while(1)
+ {
+ if (dice_roll(1,100)<=30)
+ {
+ printf("His shot hits you. You feel numb all over.\n");
+ return 49;
+ }
+ else printf("His shot just missed.\n");
+
+ if (dice_roll(1,100)<=40)
+ {
+ printf("You just blew his head off. His lifeless hand drops the mutant detector ray.\n");
+ return 50;
+ }
+ else printf("You burnt a hole in the podium. He sights the mutant detector ray on you.\n");
+ }
+}
+
+page41()
+{
+ printf("You stumble down the hallway of the Troubleshooter Training Course looking for\n");
+ printf("your next class. Up ahead you see one of the instructors waving to you. When\n");
+ printf("you get there he shakes your hand and says, \"I am Jung-I-PSY. Welcome to the\n");
+ printf("secret society infiltration seminar. I hope you ...\" You don\'t catch the\n");
+ printf("rest of his greeting because you\'re paying too much attention to his handshake;\n");
+ printf("it is the strangest thing that has ever been done to your hand, sort of how it\n");
+ printf("would feel if you put a neuro whip in a high energy palm massage unit.\n");
+ printf("It doesn\'t take you long to learn what he is up to; you feel him briefly shake\n");
+ printf("your hand with the secret Illuminati handshake.\n");
+ return choose(42,"You respond with the proper Illuminati code phrase, \"Ewige Blumenkraft\"",43,"You ignore this secret society contact");
+}
+
+page42()
+{
+ printf("\"Aha, so you are a member of the elitist Illuminati secret society,\" he says\n");
+ printf("loudly, \"that is most interesting.\" He turns to the large class already\n");
+ printf("seated in the auditorium and says, \"You see, class, by simply using the correct\n");
+ printf("hand shake you can identify the member of any secret society. Please keep your\n");
+ printf("weapons trained on him while I call a guard.\n");
+ return choose(51,"You run for it",52,"You wait for the guard");
+}
+
+page43()
+{
+ printf("You sit through a long lecture on how to recognise and infiltrate secret\n");
+ printf("societies, with an emphasis on mimicking secret handshakes. The basic theory,\n");
+ printf("which you realise to be sound from your Iluminati training, is that with the\n");
+ printf("proper handshake you can pass unnoticed in any secret society gathering.\n");
+ printf("What\'s more, the proper handshake will open doors faster than an \'ultra shock\'\n");
+ printf("plasma cannon. You are certain that with the information you learn here you\n");
+ printf("will easily be promoted to the next level of your Illuminati secret society.\n");
+ printf("The lecture continues for three hours, during which you have the opportunity\n");
+ printf("to practice many different handshakes. Afterwards everyone is directed to\n");
+ printf("attend the graduation ceremony. Before you must go you have a little time to\n");
+ printf("talk to The Computer about, you know, certain topics.\n");
+ return choose(44,"You go looking for a computer terminal",55,"You go to the graduation ceremony immediately");
+}
+
+page44()
+{
+ printf("You walk down to a semi-secluded part of the training course complex and\n");
+ printf("activate a computer terminal. \"AT YOUR SERVICE\" reads the computer screen.\n");
+ if (read_letter==0) return choose(23,"You register yourself as a mutant",55,"You change your mind and go to the graduation ceremony");
+ printf("\nSelect \'a\', \'b\' or \'c\' :\n");
+ printf(" a - You register yourself as a mutant.\n");
+ printf(" b - You want to chat about the commies.\n");
+ printf(" c - You change your mind and go to the graduation ceremony.\n");
+ switch(get_char())
+ {
+ case 'a' : return 23;
+ case 'b' : return 46;
+ case 'c' :
+ default : return 55;
+ }
+}
+
+page45()
+{
+ printf("\"Hrank Hrank,\" snorts the alarm in your living quarters. Something is up.\n");
+ printf("You look at the monitor above the bathroom mirror and see the message you have\n");
+ printf("been waiting for all these years. \"ATTENTION TROUBLESHOOTER, YOU ARE BEING\n");
+ printf("ACTIVATED. PLEASE REPORT IMMEDIATELY TO MISSION ASSIGNMENT ROOM A17/GAMMA/LB22.\n");
+ printf("THANK YOU. THE COMPUTER IS YOUR FRIEND.\" When you arrive at mission\n");
+ printf("assignment room A17-gamma/LB22 you are given your previous clone\'s\n");
+ printf("remaining possessions and notebook. You puzzle through your predecessor\'s\n");
+ printf("cryptic notes, managing to decipher enough to lead you to the tube station and\n");
+ printf("the tube car to GDH7-beta.\n");
+ return 10;
+}
+
+page46()
+{
+ printf("\"Why do you ask about the communists, Troubleshooter? It is not in the\n");
+ printf("interest of your continued survival to be asking about such topics,\" says\n");
+ printf("The Computer.\n");
+ return choose(53,"You insist on talking about the communists",54,"You change the subject");
+}
+
+page47()
+{
+ printf("The Computer orders the entire Vulture squadron to terminate the Troubleshooter\n");
+ printf("Training Course. Unfortunately you too are terminated for possessing\n");
+ printf("classified information.\n\n");
+ printf("Don\'t act so innocent, we both know that you are an Illuminatus which is in\n");
+ printf("itself an act of treason.\n\n");
+ printf("Don\'t look to me for sympathy.\n\n");
+ printf(" THE END\n");
+ return 0;
+}
+
+page48()
+{
+ printf("The tubecar shoots forward as you enter, slamming you back into a pile of\n");
+ printf("garbage. The front end rotates upward and you, the garbage and the garbage\n");
+ printf("disposal car shoot straight up out of Alpha Complex. One of the last things\n");
+ printf("you see is a small blue sphere slowly dwindling behind you. After you fail to\n");
+ printf("report in, you will be assumed dead.\n");
+ return new_clone(45);
+}
+
+page49()
+{
+ printf("The instructor drags your inert body into a specimen detainment cage.\n");
+ printf("\"He\'ll make a good subject for tomorrow\'s mutant dissection class,\" you hear.\n");
+ return new_clone(32);
+}
+
+page50()
+{
+ printf("You put down the other Troubleshooter, and then wisely decide to drill a few\n");
+ printf("holes in the instructor as well; the only good witness is a dead witness.\n");
+ printf("You continue with the training course.\n");
+ plato_clone++;
+ return 41;
+}
+
+page51()
+{
+ printf("You run for it, but you don\'t run far. Three hundred strange and exotic\n");
+ printf("weapons turn you into a freeze dried cloud of soot.\n");
+ return new_clone(32);
+}
+
+page52()
+{
+ printf("You wisely wait until the instructor returns with a Blue Internal Security\n");
+ printf("guard. The guard leads you to an Internal Security self incrimination station.\n");
+ return 2;
+}
+
+page53()
+{
+ printf("You tell The Computer about:\n");
+ return choose(47,"The commies who have infiltrated the Troubleshooter Training Course\n and the impending People\'s Revolution",54,"Something less dangerous");
+}
+
+page54()
+{
+ printf("\"Do not try to change the subject, Troubleshooter,\" says The Computer.\n");
+ printf("\"It is a serious crime to ask about the communists. You will be terminated\n");
+ printf("immediately. Thank you for your inquiry. The Computer is your friend.\"\n");
+ printf("Steel bars drop to your left and right, trapping you here in the hallway.\n");
+ printf("A spotlight beams from the computer console to brilliantly iiluminate you while\n");
+ printf("the speaker above your head rapidly repeats \"Traitor, Traitor, Traitor.\"\n");
+ printf("It doesn\'t take long for a few guards to notice your predicament and come to\n");
+ printf("finish you off.\n");
+ if (blast_door==0) return new_clone(45);
+ else return new_clone(32);
+}
+
+page55()
+{
+ printf("You and 300 other excited graduates are marched from the lecture hall and into\n");
+ printf("a large auditorium for the graduation exercise. The auditorium is\n");
+ printf("extravagantly decorated in the colours of the graduating class. Great red and\n");
+ printf("green plasti-paper ribbons drape from the walls, while a huge sign reading\n");
+ printf("\"Congratulations class of GDH7-beta-203.44/A\" hangs from the raised stage down\n");
+ printf("front. Once everyone finds a seat the ceremony begins. Jung-I-PSY is the\n");
+ printf("first to speak, \"Congratulations students, you have successfully survived the\n");
+ printf("Troubleshooter Training Course. It always brings me great pride to address\n");
+ printf("the graduating class, for I know, as I am sure you do too, that you are now\n");
+ printf("qualified for the most perilous missions The Computer may select for you. The\n");
+ printf("thanks is not owed to us of the teaching staff, but to all of you, who have\n");
+ printf("persevered and graduated. Good luck and die trying.\" Then the instructor\n");
+ printf("begins reading the names of the students who one by one walk to the front of\n");
+ printf("the auditorium and receive their diplomas. Soon it is your turn,\n");
+ printf("\"Philo-R-DMD, graduating a master of mutant identification and secret society\n");
+ printf("infiltration.\" You walk up and receive your diploma from Plato-B-PHI%d, then\n",plato_clone);
+ printf("return to your seat. There is another speech after the diplomas are handed\n");
+ printf("out, but it is cut short by by rapid fire laser bursts from the high spirited\n");
+ printf("graduating class. You are free to return to your barracks to wait, trained\n");
+ printf("and fully qualified, for your next mission. You also get that cherished\n");
+ printf("promotion from the Illuminati secret society. In a week you receive a\n");
+ printf("detailed Training Course bill totalling 1,523 credits.\n");
+ printf(" THE END\n");
+ return 0;
+}
+
+page56()
+{
+ printf("That familiar strange feeling of deja\'vu envelops you again. It is hard to\n");
+ printf("say, but whatever is on the other side of the door does not seem to be intended\n");
+ printf("for you.\n");
+ return choose(33,"You open the door and step through",22,"You go looking for more information");
+}
+
+page57()
+{
+ printf("In the centre of the room is a table and a single chair. There is an Orange\n");
+ printf("folder on the table top, but you can\'t make out the lettering on it.\n");
+ return choose(11,"You sit down and read the folder",12,"You leave the room");
+}
+
+next_page(this_page)
+int this_page;
+{
+ printf("\n");
+ switch (this_page)
+ {
+ case 0 : return 0;
+ case 1 : return page1();
+ case 2 : return page2();
+ case 3 : return page3();
+ case 4 : return page4();
+ case 5 : return page5();
+ case 6 : return page6();
+ case 7 : return page7();
+ case 8 : return page8();
+ case 9 : return page9();
+ case 10 : return page10();
+ case 11 : return page11();
+ case 12 : return page12();
+ case 13 : return page13();
+ case 14 : return page14();
+ case 15 : return page15();
+ case 16 : return page16();
+ case 17 : return page17();
+ case 18 : return page18();
+ case 19 : return page19();
+ case 20 : return page20();
+ case 21 : return page21();
+ case 22 : return page22();
+ case 23 : return page23();
+ case 24 : return page24();
+ case 25 : return page25();
+ case 26 : return page26();
+ case 27 : return page27();
+ case 28 : return page28();
+ case 29 : return page29();
+ case 30 : return page30();
+ case 31 : return page31();
+ case 32 : return page32();
+ case 33 : return page33();
+ case 34 : return page34();
+ case 35 : return page35();
+ case 36 : return page36();
+ case 37 : return page37();
+ case 38 : return page38();
+ case 39 : return page39();
+ case 40 : return page40();
+ case 41 : return page41();
+ case 42 : return page42();
+ case 43 : return page43();
+ case 44 : return page44();
+ case 45 : return page45();
+ case 46 : return page46();
+ case 47 : return page47();
+ case 48 : return page48();
+ case 49 : return page49();
+ case 50 : return page50();
+ case 51 : return page51();
+ case 52 : return page52();
+ case 53 : return page53();
+ case 54 : return page54();
+ case 55 : return page55();
+ case 56 : return page56();
+ case 57 : return page57();
+ default : break;
+ }
+}
+
+main()
+{
+ srand(time(0));
+ instructions(); more();
+ character(); more();
+ while((page=next_page(page))!=0) more();
+}
diff --git a/rogue/CHANGES b/rogue/CHANGES
new file mode 100644
index 0000000..066ac12
--- /dev/null
+++ b/rogue/CHANGES
@@ -0,0 +1,55 @@
+$NetBSD: CHANGES,v 1.3 2000/03/13 22:53:22 soren Exp $
+
+From: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU
+Date: 30 Nov 87 15:08:15 PST (Mon)
+To: okeeffe.Berkeley.EDU!mckusick@ucbvax.Berkeley.EDU (Kirk McKusick)
+Subject: Re: Public domain rogue
+Return-Path: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU
+
+Here is a list of discrepencies from the documentation you sent me:
+
+The -d option not implemented.
+The -r option not implemented, use "rogue save_file" instead.
+Strength is between 1 and 99, not 3 and 32.
+The D command is not implemented.
+Only scrolls,potions,wands,and rings may be "call"ed something.
+The ^P command may be used to go 4 messages back, instead of just 1.
+The @ comand is not implemented.
+There are no dark rooms.
+ROGUEOPTS of flush,terse,seefloor,askme,inventory are ignored.
+ 'askquit' is added to prevent ^\ from terminating the game accidentally.
+ If 'noaskquit' is
+ found in the ROGUEOPTS string, the ^\ kills the game, otherwise,
+ the player is asked if he really wants to quit. In either case, no
+ score file processing is attempted.
+The score is keyed to winning scores, and no player may appear twice.
+
+
+
+
+
+
+Other differences from "standard" rogue 5.3. This list covers externally
+visible differences only.
+
+There should be NO bugs with any severe consequences. Absolutely NO
+ game-stopping, or game-winning bugs should be present.
+Traps fail occasionally, that is, they sometimes are sprung but miss.
+The ^A command prints out some stuff you're probably not interested in.
+The '&' command silently saves your screen into the file 'rogue.screen'
+Any inventory selection command that takes '*' as a request to list all
+ appropriate items, can take one of "=?:)]!/" to list only rings,
+ scrolls, or whatever.
+Scrolls and potions, once used, become identified. All other objects become
+ identified only by scroll of identification.
+There is only one scroll of identification, and it works on any item.
+ROGUEOPTS
+ Only the following are implemented:
+ file,jump,name,askquit,tombstone,passgo
+ "askquit" is used to prevent accidental termination of the game via ^\
+You may drop objects in doorways.
+Prints a picture of a skull, not a tombstone, upon death.
+The save/restore game function is faster and machine-independent, but sometimes
+ requires modification when new variables are added to the source.
+The potion of detect monster lasts for the whole level.
+Their is no wand of light.
diff --git a/rogue/Makefile b/rogue/Makefile
new file mode 100644
index 0000000..a0db14a
--- /dev/null
+++ b/rogue/Makefile
@@ -0,0 +1,19 @@
+# $NetBSD: Makefile,v 1.19 2014/07/05 19:22:03 dholland Exp $
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+
+PROG= rogue
+CPPFLAGS+=-DUNIX
+SRCS= hit.c init.c inventory.c level.c machdep.c main.c \
+ message.c monster.c move.c object.c pack.c play.c random.c ring.c \
+ room.c save.c score.c spec_hit.c throw.c trap.c use.c zap.c
+DPADD= ${LIBCURSES} ${LIBTERMINFO}
+# 20150209 bkw: remove -lterminfo, add -lbsd
+LDADD= -lcurses -lbsd
+HIDEGAME=hidegame
+SETGIDGAME=yes
+MAN= rogue.6
+
+SUBDIR+=USD.doc
+
+.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
diff --git a/rogue/USD.doc/Makefile b/rogue/USD.doc/Makefile
new file mode 100644
index 0000000..4e0bf2e
--- /dev/null
+++ b/rogue/USD.doc/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.7 2014/07/05 19:22:42 dholland Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+SECTION=reference/ref6
+ARTICLE=rogue
+SRCS= rogue.me
+MACROS= -me
+ROFF_TBL=yes
+
+.include <bsd.doc.mk>
diff --git a/rogue/USD.doc/rogue.me b/rogue/USD.doc/rogue.me
new file mode 100644
index 0000000..b1592dd
--- /dev/null
+++ b/rogue/USD.doc/rogue.me
@@ -0,0 +1,834 @@
+.\" $NetBSD: rogue.me,v 1.6 2004/02/13 11:36:08 wiz Exp $
+.\"
+.\" Copyright (c) 1986, 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.
+.\"
+.\" @(#)rogue.me 8.2 (Berkeley) 6/1/94
+.\"
+.ds E \s-2<ESCAPE>\s0
+.ds R \s-2<RETURN>\s0
+.ds U \s-2UNIX\s0
+.ie t .ds _ \d\(mi\u
+.el .ds _ _
+.de Cs
+\&\\$3\*(lq\\$1\*(rq\\$2
+..
+.sp 5
+.ce 1000
+.ps +4
+.vs +4p
+.b
+A Guide to the Dungeons of Doom
+.r
+.vs
+.ps
+.sp 2
+.i
+Michael C. Toy
+Kenneth C. R. C. Arnold
+.r
+.sp 2
+Computer Systems Research Group
+Department of Electrical Engineering and Computer Science
+University of California
+Berkeley, California 94720
+.sp 4
+.i ABSTRACT
+.ce 0
+.(b I F
+.bi Rogue
+is a visual CRT based fantasy game
+which runs under the \*U\(dg timesharing system.
+.(f
+\fR\(dg\*U is a trademark of Bell Laboratories\fP
+.)f
+This paper describes how to play rogue,
+and gives a few hints
+for those who might otherwise get lost in the Dungeons of Doom.
+.)b
+\".he '''\fBA Guide to the Dungeons of Doom\fP'
+\" .fo ''- % -''
+.eh 'USD:30-%''A Guide to the Dungeons of Doom'
+.oh 'A Guide to the Dungeons of Doom''USD:30-%'
+.sh 1 Introduction
+.pp
+You have just finished your years as a student at the local fighter's guild.
+After much practice and sweat you have finally completed your training
+and are ready to embark upon a perilous adventure.
+As a test of your skills,
+the local guildmasters have sent you into the Dungeons of Doom.
+Your task is to return with the Amulet of Yendor.
+Your reward for the completion of this task
+will be a full membership in the local guild.
+In addition,
+you are allowed to keep all the loot you bring back from the dungeons.
+.pp
+In preparation for your journey,
+you are given an enchanted mace,
+a bow, and a quiver of arrows
+taken from a dragon's hoard in the far off Dark Mountains.
+You are also outfitted with elf-crafted armor
+and given enough food to reach the dungeons.
+You say goodbye to family and friends for what may be the last time
+and head up the road.
+.pp
+You set out on your way to the dungeons
+and after several days of uneventful travel,
+you see the ancient ruins
+that mark the entrance to the Dungeons of Doom.
+It is late at night,
+so you make camp at the entrance
+and spend the night sleeping under the open skies.
+In the morning you gather your weapons,
+put on your armor,
+eat what is almost your last food,
+and enter the dungeons.
+.sh 1 "What is going on here?"
+.pp
+You have just begun a game of rogue.
+Your goal is to grab as much treasure as you can,
+find the Amulet of Yendor,
+and get out of the Dungeons of Doom alive.
+On the screen,
+a map of where you have been
+and what you have seen on the current dungeon level is kept.
+As you explore more of the level,
+it appears on the screen in front of you.
+.pp
+Rogue differs from most computer fantasy games in that it is screen oriented.
+Commands are all one or two keystrokes\**
+.(f
+\** As opposed to pseudo English sentences.
+.)f
+and the results of your commands
+are displayed graphically on the screen rather
+than being explained in words.\**
+.(f
+\** A minimum screen size of 24 lines by 80 columns is required.
+If the screen is larger, only the 24x80 section will be used
+for the map.
+.)f
+.pp
+Another major difference between rogue and other computer fantasy games
+is that once you have solved all the puzzles in a standard fantasy game,
+it has lost most of its excitement and it ceases to be fun.
+Rogue,
+on the other hand,
+generates a new dungeon every time you play it
+and even the author finds it an entertaining and exciting game.
+.sh 1 "What do all those things on the screen mean?"
+.pp
+In order to understand what is going on in rogue
+you have to first get some grasp of what rogue is doing with the screen.
+The rogue screen is intended
+to replace the \*(lqYou can see ...\*(rq descriptions
+of standard fantasy games.
+Figure 1 is a sample of what a rogue screen might look like.
+.(z
+.hl
+.nf
+.TS
+center;
+ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce.
+- - - - - - - - - - - -
+| . . . . . . . . . . +
+| . . @ . . . . ] . . |
+| . . . . B . . . . . |
+| . . . . . . . . . . |
+- - - - - + - - - - - -
+.TE
+
+
+.ce 1000
+Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0
+
+Figure 1
+.ce
+.hl
+.)z
+.sh 2 "The bottom line"
+.pp
+At the bottom line of the screen
+are a few pieces of cryptic information
+describing your current status.
+Here is an explanation of what these things mean:
+.ip Level \w'Level\ \ 'u
+This number indicates how deep you have gone in the dungeon.
+It starts at one and goes up as you go deeper into the dungeon.
+.ip Gold \w'Level\ \ 'u
+The number of gold pieces you have managed to find
+and keep with you so far.
+.ip Hp \w'Level\ \ 'u
+Your current and maximum health points.
+Health points indicate how much damage you can take before you die.
+The more you get hit in a fight,
+the lower they get.
+You can regain health points by resting.
+The number in parentheses
+is the maximum number your health points can reach.
+.ip Str \w'Level\ \ 'u
+Your current strength and maximum ever strength.
+This can be any integer less than or equal to 99,
+or greater than or equal to 1.
+The higher the number,
+the stronger you are.
+The number in the parentheses
+is the maximum strength you have attained so far this game.
+.ip Arm \w'Level\ \ 'u
+Your current armor protection.
+This number indicates how effective your armor is
+in stopping blows from unfriendly creatures.
+The higher this number is,
+the more effective the armor.
+.ip Exp \w'Level\ \ 'u
+These two numbers give your current experience level
+and experience points.
+As you do things,
+you gain experience points.
+At certain experience point totals,
+you gain an experience level.
+The more experienced you are,
+the better you are able to fight and to withstand magical attacks.
+.sh 2 "The top line"
+.pp
+The top line of the screen is reserved
+for printing messages that describe things
+that are impossible to represent visually.
+If you see a \*(lq--More--\*(rq on the top line,
+this means that rogue wants to print another message on the screen,
+but it wants to make certain
+that you have read the one that is there first.
+To read the next message,
+just type a space.
+.sh 2 "The rest of the screen"
+.pp
+The rest of the screen is the map of the level
+as you have explored it so far.
+Each symbol on the screen represents something.
+Here is a list of what the various symbols mean:
+.ip @
+This symbol represents you, the adventurer.
+.ip "-\^|"
+These symbols represent the walls of rooms.
+.ip +
+A door to/from a room.
+.ip .
+The floor of a room.
+.ip #
+The floor of a passage between rooms.
+.ip *
+A pile or pot of gold.
+.ip )
+A weapon of some sort.
+.ip ]
+A piece of armor.
+.ip !
+A flask containing a magic potion.
+.ip ?
+A piece of paper, usually a magic scroll.
+.ip =
+A ring with magic properties
+.ip /
+A magical staff or wand
+.ip ^
+A trap, watch out for these.
+.ip %
+A staircase to other levels
+.ip :
+A piece of food.
+.ip A-Z
+The uppercase letters
+represent the various inhabitants of the Dungeons of Doom.
+Watch out, they can be nasty and vicious.
+.sh 1 Commands
+.pp
+Commands are given to rogue by typing one or two characters.
+Most commands can be preceded by a count to repeat them
+(e.g. typing
+.Cs 10s
+will do ten searches).
+Commands for which counts make no sense
+have the count ignored.
+To cancel a count or a prefix,
+type \*E.
+The list of commands is rather long,
+but it can be read at any time during the game with the
+.Cs ?
+command.
+Here it is for reference,
+with a short explanation of each command.
+.ip ?
+The help command.
+Asks for a character to give help on.
+If you type a
+.Cs * ,
+it will list all the commands,
+otherwise it will explain what the character you typed does.
+.ip /
+This is the \*(lqWhat is that on the screen?\*(rq command.
+A
+.Cs /
+followed by any character that you see on the level,
+will tell you what that character is.
+For instance,
+typing
+.Cs /@
+will tell you that the
+.Cs @
+symbol represents you, the player.
+.ip "h, H, ^H"
+Move left.
+You move one space to the left.
+If you use upper case
+.Cs h ,
+you will continue to move left until you run into something.
+This works for all movement commands
+(e.g.
+.Cs L
+means run in direction
+.Cs l )
+If you use the \*(lqcontrol\*(rq
+.Cs h ,
+you will continue moving in the specified direction
+until you pass something interesting or run into a wall.
+You should experiment with this,
+since it is a very useful command,
+but very difficult to describe.
+This also works for all movement commands.
+.ip j
+Move down.
+.ip k
+Move up.
+.ip l
+Move right.
+.ip y
+Move diagonally up and left.
+.ip u
+Move diagonally up and right.
+.ip b
+Move diagonally down and left.
+.ip n
+Move diagonally down and right.
+.ip t
+Throw an object.
+This is a prefix command.
+When followed with a direction
+it throws an object in the specified direction.
+(e.g. type
+.Cs th
+to throw
+something to the left.)
+.ip f
+Fight until someone dies.
+When followed with a direction
+this will force you to fight the creature in that direction
+until either you or it bites the big one.
+.ip m
+Move onto something without picking it up.
+This will move you one space in the direction you specify and,
+if there is an object there you can pick up,
+it won't do it.
+.ip z
+Zap prefix.
+Point a staff or wand in a given direction
+and fire it.
+Even non-directional staves must be pointed in some direction
+to be used.
+.ip ^
+Identify trap command.
+If a trap is on your map
+and you can't remember what type it is,
+you can get rogue to remind you
+by getting next to it and typing
+.Cs ^
+followed by the direction that would move you on top of it.
+.ip s
+Search for traps and secret doors.
+Examine each space immediately adjacent to you
+for the existence of a trap or secret door.
+There is a large chance that even if there is something there,
+you won't find it,
+so you might have to search a while before you find something.
+.ip >
+Climb down a staircase to the next level.
+Not surprisingly, this can only be done if you are standing on staircase.
+.ip <
+Climb up a staircase to the level above.
+This can't be done without the Amulet of Yendor in your possession.
+.ip "."
+Rest.
+This is the \*(lqdo nothing\*(rq command.
+This is good for waiting and healing.
+.ip ,
+Pick up something.
+This picks up whatever you are currently standing on,
+if you are standing on anything at all.
+.ip i
+Inventory.
+List what you are carrying in your pack.
+.ip I
+Selective inventory.
+Tells you what a single item in your pack is.
+.ip q
+Quaff one of the potions you are carrying.
+.ip r
+Read one of the scrolls in your pack.
+.ip e
+Eat food from your pack.
+.ip w
+Wield a weapon.
+Take a weapon out of your pack and carry it for use in combat,
+replacing the one you are currently using (if any).
+.ip W
+Wear armor.
+You can only wear one suit of armor at a time.
+This takes extra time.
+.ip T
+Take armor off.
+You can't remove armor that is cursed.
+This takes extra time.
+.ip P
+Put on a ring.
+You can wear only two rings at a time
+(one on each hand).
+If you aren't wearing any rings,
+this command will ask you which hand you want to wear it on,
+otherwise, it will place it on the unused hand.
+The program assumes that you wield your sword in your right hand.
+.ip R
+Remove a ring.
+If you are only wearing one ring,
+this command takes it off.
+If you are wearing two,
+it will ask you which one you wish to remove,
+.ip d
+Drop an object.
+Take something out of your pack and leave it lying on the floor.
+Only one object can occupy each space.
+You cannot drop a cursed object at all
+if you are wielding or wearing it.
+.ip c
+Call an object something.
+If you have a type of object in your pack
+which you wish to remember something about,
+you can use the call command to give a name to that type of object.
+This is usually used when you figure out what a
+potion, scroll, ring, or staff is
+after you pick it up but before it is truly identified. Each type of
+scroll and potion will become identified after its first use.
+.ip o
+Examine and set options.
+This command is further explained in the section on options.
+.ip ^R
+Redraws the screen.
+Useful if spurious messages or transmission errors
+have messed up the display.
+.ip ^P
+Print last message.
+Useful when a message disappears before you can read it.
+Consecutive repetitions of this command will reveal the last
+five messages.
+.ip \*E
+Cancel a command, prefix, or count.
+.ip !
+Escape to a shell for some commands.
+.ip Q
+Quit.
+Leave the game.
+.ip S
+Save the current game in a file.
+It will ask you whether you wish to use the default save file.
+.i Caveat :
+Rogue won't let you start up a copy of a saved game,
+and it removes the save file as soon as you start up a restored game.
+This is to prevent people from saving a game just before a dangerous position
+and then restarting it if they die.
+To restore a saved game,
+give the file name as an argument to rogue.
+As in
+.ti +1i
+.nf
+% rogue \fIsave\*_file\fP
+.ip v
+Prints the program version number.
+.ip )
+Print the weapon you are currently wielding
+.ip ]
+Print the armor you are currently wearing
+.ip =
+Print the rings you are currently wearing
+.sh 1 Rooms
+.pp
+Rooms in the dungeons are lit as you enter them.
+Upon leaving a room,
+all monsters inside the room are erased from the screen.
+In the darkness of a corridor, you can only see one space
+in all directions around you.
+.sh 1 Fighting
+.pp
+If you see a monster and you wish to fight it,
+just attempt to run into it.
+Many times a monster you find will mind its own business
+unless you attack it.
+It is often the case that discretion is the better part of valor.
+.sh 1 "Objects you can find"
+.pp
+When you find something in the dungeon,
+it is common to want to pick the object up.
+This is accomplished in rogue by walking over the object
+(unless you use the
+.Cs m
+prefix, see above).
+If you are carrying too many things,
+the program will tell you and it won't pick up the object,
+otherwise it will add it to your pack
+and tell you what you just picked up.
+.pp
+Many of the commands that operate on objects must prompt you
+to find out which object you want to use.
+If you change your mind and don't want to do that command after all,
+just type an \*E and the command will be aborted.
+.pp
+Some objects, like armor and weapons,
+are easily differentiated.
+Others, like scrolls and potions,
+are given labels which vary according to type.
+During a game,
+any two of the same kind of object
+with the same label
+are the same type.
+However,
+the labels will vary from game to game.
+.pp
+When you use one of these labeled objects,
+if its effect may be obvious. Potions or scrolls will
+become identified at this point, but not other items.
+You may want to call these other items something
+so you will recognize it later,
+you can use the
+.Cs call
+command
+(see above).
+.sh 2 Weapons
+.pp
+Some weapons,
+like arrows,
+come in bunches,
+but most come one at a time.
+In order to use a weapon,
+you must wield it.
+To fire an arrow out of a bow,
+you must first wield the bow,
+then throw the arrow.
+You can only wield one weapon at a time,
+but you can't change weapons if the one
+you are currently wielding is cursed.
+The commands to use weapons are
+.Cs w
+(wield)
+and
+.Cs t
+(throw).
+.sh 2 Armor
+.pp
+There are various sorts of armor lying around in the dungeon.
+Some of it is enchanted,
+some is cursed,
+and some is just normal.
+Different armor types have different armor protection.
+The higher the armor protection,
+the more protection the armor affords against the blows of monsters.
+Here is a list of the various armor types and their normal armor protection:
+.(b
+.TS
+box center;
+l r.
+\ \ \fIType Protection\fP
+None 0
+Leather armor 2
+Studded leather / Ring mail 3
+Scale mail 4
+Chain mail 5
+Banded mail / Splint mail 6
+Plate mail 7
+.TE
+.)b
+.lp
+If a piece of armor is enchanted,
+its armor protection will be higher than normal.
+If a suit of armor is cursed,
+its armor protection will be lower,
+and you will not be able to remove it.
+However, not all armor with a protection that is lower than normal is cursed.
+.pp
+The commands to use weapons are
+.Cs W
+(wear)
+and
+.Cs T
+(take off).
+.sh 2 Scrolls
+.pp
+Scrolls come with titles in an unknown tongue\**.
+.(f
+\** Actually, it's a dialect spoken only by the twenty-seven members
+of a tribe in Outer Mongolia,
+but you're not supposed to
+.i know
+that.
+.)f
+After you read a scroll,
+it disappears from your pack.
+The command to use a scroll is
+.Cs r
+(read).
+.sh 2 Potions
+.pp
+Potions are labeled by the color of the liquid inside the flask.
+They disappear after being quaffed.
+The command to quaff a potion is
+.Cs q
+(quaff).
+.sh 2 "Staves and Wands"
+.pp
+Staves and wands do the same kinds of things.
+Staves are identified by a type of wood;
+wands by a type of metal or bone.
+They are generally things you want to do to something
+over a long distance,
+so you must point them at what you wish to affect
+to use them.
+Some staves are not affected by the direction they are pointed, though.
+Staves come with multiple magic charges,
+the number being random,
+and when they are used up,
+the staff is just a piece of wood or metal.
+.pp
+The command to use a wand or staff is
+.Cs z
+(zap)
+.sh 2 Rings
+.pp
+Rings are very useful items,
+since they are relatively permanent magic,
+unlike the usually fleeting effects of potions, scrolls, and staves.
+Of course,
+the bad rings are also more powerful.
+Most rings also cause you to use up food more rapidly,
+the rate varying with the type of ring.
+Rings are differentiated by their stone settings.
+The commands to use rings are
+.Cs P
+(put on)
+and
+.Cs R
+(remove).
+.sh 2 Food
+.pp
+Food is necessary to keep you going.
+If you go too long without eating you will faint,
+and eventually die of starvation.
+The command to use food is
+.Cs e
+(eat).
+.sh 1 Options
+.pp
+Due to variations in personal tastes
+and conceptions of the way rogue should do things,
+there are a set of options you can set
+that cause rogue to behave in various different ways.
+.ne 1i
+.sh 2 "Setting the options"
+.pp
+There are two ways to set the options.
+The first is with the
+.Cs o
+command of rogue;
+the second is with the
+.Cs ROGUEOPTS
+environment variable\**.
+.(f
+\** On Version 6 systems,
+there is no equivalent of the ROGUEOPTS feature.
+.br
+.)f
+.br
+.sh 3 "Using the `o' command"
+.pp
+When you type
+.Cs o
+in rogue,
+it clears the screen
+and displays the current settings for all the options.
+It then places the cursor by the value of the first option
+and waits for you to type.
+You can type a \*R
+which means to go to the next option,
+a
+.Cs \-
+which means to go to the previous option,
+an \*E
+which means to return to the game,
+or you can give the option a value.
+For boolean options this merely involves typing
+.Cs t
+for true or
+.Cs f
+for false.
+For string options,
+type the new value followed by a \*R.
+.sh 3 "Using the ROGUEOPTS variable"
+.pp
+The ROGUEOPTS variable is a string
+containing a comma separated list of initial values
+for the various options.
+Boolean variables can be turned on by listing their name
+or turned off by putting a
+.Cs no
+in front of the name.
+Thus to set up an environment variable so that
+.b jump
+is on,
+.b passgo
+is off,
+and the
+.b name
+is set to \*(lqBlue Meanie\*(rq,
+use the command
+.nf
+.ti +3n
+% setenv ROGUEOPTS "jump,nopassgo,name=Blue Meanie"\**
+.fi
+.(f
+\**
+For those of you who use the Bourne shell sh (1), the commands would be
+.in +3
+.nf
+$ ROGUEOPTS="jump,nopassgo,name=Blue Meanie"
+$ export ROGUEOPTS
+.fi
+.in +0
+.)f
+.sh 2 "Option list"
+.pp
+Here is a list of the options
+and an explanation of what each one is for.
+The default value for each is enclosed in square brackets.
+For character string options,
+input over forty characters will be ignored.
+.ip "\fBjump\fP [\fI\^nojump\^\fP]"
+If this option is set,
+running moves will not be displayed
+until you reach the end of the move.
+This saves considerable CPU and display time.
+This option defaults to
+.i jump
+if you are using a slow terminal.
+.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]"
+Follow turnings in passageways.
+If you run in a passage
+and you run into stone or a wall,
+rogue will see if it can turn to the right or left.
+If it can only turn one way,
+it will turn that way.
+If it can turn either or neither,
+it will stop.
+This algorithm can sometimes lead to slightly confusing occurrences
+which is why it defaults to \fInopassgo\fP.
+.ip "\fBskull\fP [\fI\^skull\^\fP]"
+Print out the skull at the end if you get killed.
+This is nice but slow, so you can turn it off if you like.
+.ip "\fBname\fP [account name]"
+This is the name of your character.
+It is used if you get on the top ten scorer's list.
+.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]"
+This should hold the name of a fruit that you enjoy eating.
+It is basically a whimsey that rogue uses in a couple of places.
+.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]"
+The default file name for saving the game.
+If your phone is hung up by accident,
+rogue will automatically save the game in this file.
+The file name may start with the special character
+.Cs ~
+which expands to be your home directory.
+.sh 1 Scoring
+.pp
+Rogue maintains a list
+of the top scoring people or scores on your machine.
+If you score higher than someone else on this list,
+or better your previous score on the list,
+you will be inserted in the proper place
+under your current name.
+.pp
+If you quit the game, you get out with all of your gold intact.
+If, however, you get killed in the Dungeons of Doom,
+your body is forwarded to your next-of-kin,
+along with 90% of your gold;
+ten percent of your gold is kept by the Dungeons' wizard as a fee\**.
+.(f
+\** The Dungeon's wizard is named Wally the Wonder Badger.
+Invocations should be accompanied by a sizable donation.
+.)f
+This should make you consider whether you want to take one last hit
+at that monster and possibly live,
+or quit and thus stop with whatever you have.
+If you quit, you do get all your gold,
+but if you swing and live, you might find more.
+.pp
+If you just want to see what the current top players/games list is,
+you can type
+.ti +1i
+.nf
+% rogue \-s
+.br
+.sh 1 Acknowledgements
+.pp
+Rogue was originally conceived of by Glenn Wichman and Michael Toy.
+Ken Arnold and Michael Toy then smoothed out the user interface,
+and added jillions of new features.
+We would like to thank
+Bob Arnold,
+Michelle Busch,
+Andy Hatcher,
+Kipp Hickman,
+Mark Horton,
+Daniel Jensen,
+Bill Joy,
+Joe Kalash,
+Steve Maurer,
+Marty McNary,
+Jan Miller,
+and
+Scott Nelson
+for their ideas and assistance;
+and also the teeming multitudes
+who graciously ignored work, school, and social life to play rogue
+and send us bugs, complaints, suggestions, and just plain flames.
+And also Mom.
+.pp
+The public domain version of rogue now distributed with Berkeley UNIX
+was written by Timothy Stoehr.
diff --git a/rogue/hit.c b/rogue/hit.c
new file mode 100644
index 0000000..9d27c1f
--- /dev/null
+++ b/rogue/hit.c
@@ -0,0 +1,466 @@
+/* $NetBSD: hit.c,v 1.10 2008/01/14 03:50:01 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)hit.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: hit.c,v 1.10 2008/01/14 03:50:01 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * hit.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+static int damage_for_strength(void);
+static int get_w_damage(const object *);
+static int to_hit(const object *);
+
+static object *fight_monster = NULL;
+char hit_message[HIT_MESSAGE_SIZE] = "";
+
+void
+mon_hit(object *monster)
+{
+ short damage, hit_chance;
+ const char *mn;
+ float minus;
+
+ if (fight_monster && (monster != fight_monster)) {
+ fight_monster = 0;
+ }
+ monster->trow = NO_ROOM;
+ if (cur_level >= (AMULET_LEVEL * 2)) {
+ hit_chance = 100;
+ } else {
+ hit_chance = monster->m_hit_chance;
+ hit_chance -= (((2 * rogue.exp) + (2 * ring_exp)) - r_rings);
+ }
+ if (wizard) {
+ hit_chance /= 2;
+ }
+ if (!fight_monster) {
+ interrupted = 1;
+ }
+ mn = mon_name(monster);
+
+ if (!rand_percent(hit_chance)) {
+ if (!fight_monster) {
+ messagef(1, "%sthe %s misses", hit_message, mn);
+ hit_message[0] = 0;
+ }
+ return;
+ }
+ if (!fight_monster) {
+ messagef(1, "%sthe %s hit", hit_message, mn);
+ hit_message[0] = 0;
+ }
+ if (!(monster->m_flags & STATIONARY)) {
+ damage = get_damage(monster->m_damage, 1);
+ if (cur_level >= (AMULET_LEVEL * 2)) {
+ minus = (float)((AMULET_LEVEL * 2) - cur_level);
+ } else {
+ minus = (float)get_armor_class(rogue.armor) * 3.00;
+ minus = minus/100.00 * (float)damage;
+ }
+ damage -= (short)minus;
+ } else {
+ damage = monster->stationary_damage++;
+ }
+ if (wizard) {
+ damage /= 3;
+ }
+ if (damage > 0) {
+ rogue_damage(damage, monster, 0);
+ }
+ if (monster->m_flags & SPECIAL_HIT) {
+ special_hit(monster);
+ }
+}
+
+void
+rogue_hit(object *monster, boolean force_hit)
+{
+ short damage, hit_chance;
+
+ if (monster) {
+ if (check_imitator(monster)) {
+ return;
+ }
+ hit_chance = force_hit ? 100 : get_hit_chance(rogue.weapon);
+
+ if (wizard) {
+ hit_chance *= 2;
+ }
+ if (!rand_percent(hit_chance)) {
+ if (!fight_monster) {
+ (void)strlcpy(hit_message, "you miss ",
+ sizeof(hit_message));
+ }
+ goto RET;
+ }
+ damage = get_weapon_damage(rogue.weapon);
+ if (wizard) {
+ damage *= 3;
+ }
+ if (con_mon) {
+ s_con_mon(monster);
+ }
+ if (mon_damage(monster, damage)) { /* still alive? */
+ if (!fight_monster) {
+ (void)strlcpy(hit_message, "you hit ",
+ sizeof(hit_message));
+ }
+ }
+RET: check_gold_seeker(monster);
+ wake_up(monster);
+ }
+}
+
+void
+rogue_damage(short d, object *monster, short other)
+{
+ if (d >= rogue.hp_current) {
+ rogue.hp_current = 0;
+ print_stats(STAT_HP);
+ killed_by(monster, other);
+ }
+ if (d > 0) {
+ rogue.hp_current -= d;
+ print_stats(STAT_HP);
+ }
+}
+
+int
+get_damage(const char *ds, boolean r)
+{
+ int i = 0, j, n, d, total = 0;
+
+ while (ds[i]) {
+ n = get_number(ds+i);
+ while ((ds[i] != 'd') && ds[i]) {
+ i++;
+ }
+ if (ds[i] == 'd') {
+ i++;
+ }
+
+ d = get_number(ds+i);
+ while ((ds[i] != '/') && ds[i]) {
+ i++;
+ }
+ if (ds[i] == '/') {
+ i++;
+ }
+
+ for (j = 0; j < n; j++) {
+ if (r) {
+ total += get_rand(1, d);
+ } else {
+ total += d;
+ }
+ }
+ }
+ return(total);
+}
+
+static int
+get_w_damage(const object *obj)
+{
+ char new_damage[32];
+ int tmp_to_hit, tmp_damage;
+ int i = 0;
+
+ if ((!obj) || (obj->what_is != WEAPON)) {
+ return(-1);
+ }
+ tmp_to_hit = get_number(obj->damage) + obj->hit_enchant;
+ while ((obj->damage[i] != 'd') && obj->damage[i]) {
+ i++;
+ }
+ if (obj->damage[i] == 'd') {
+ i++;
+ }
+ tmp_damage = get_number(obj->damage + i) + obj->d_enchant;
+
+ snprintf(new_damage, sizeof(new_damage), "%dd%d",
+ tmp_to_hit, tmp_damage);
+
+ return(get_damage(new_damage, 1));
+}
+
+int
+get_number(const char *s)
+{
+ int i = 0;
+ int total = 0;
+
+ while ((s[i] >= '0') && (s[i] <= '9')) {
+ total = (10 * total) + (s[i] - '0');
+ i++;
+ }
+ return(total);
+}
+
+long
+lget_number(const char *s)
+{
+ short i = 0;
+ long total = 0;
+
+ while ((s[i] >= '0') && (s[i] <= '9')) {
+ total = (10 * total) + (s[i] - '0');
+ i++;
+ }
+ return(total);
+}
+
+static int
+to_hit(const object *obj)
+{
+ if (!obj) {
+ return(1);
+ }
+ return(get_number(obj->damage) + obj->hit_enchant);
+}
+
+static int
+damage_for_strength(void)
+{
+ short strength;
+
+ strength = rogue.str_current + add_strength;
+
+ if (strength <= 6) {
+ return(strength-5);
+ }
+ if (strength <= 14) {
+ return(1);
+ }
+ if (strength <= 17) {
+ return(3);
+ }
+ if (strength <= 18) {
+ return(4);
+ }
+ if (strength <= 20) {
+ return(5);
+ }
+ if (strength <= 21) {
+ return(6);
+ }
+ if (strength <= 30) {
+ return(7);
+ }
+ return(8);
+}
+
+int
+mon_damage(object *monster, short damage)
+{
+ const char *mn;
+ short row, col;
+
+ monster->hp_to_kill -= damage;
+
+ if (monster->hp_to_kill <= 0) {
+ row = monster->row;
+ col = monster->col;
+ dungeon[row][col] &= ~MONSTER;
+ mvaddch(row, col, get_dungeon_char(row, col));
+
+ fight_monster = 0;
+ cough_up(monster);
+ mn = mon_name(monster);
+ messagef(1, "%sdefeated the %s", hit_message, mn);
+ hit_message[0] = 0;
+ add_exp(monster->kill_exp, 1);
+ take_from_pack(monster, &level_monsters);
+
+ if (monster->m_flags & HOLDS) {
+ being_held = 0;
+ }
+ free_object(monster);
+ return(0);
+ }
+ return(1);
+}
+
+void
+fight(boolean to_the_death)
+{
+ short ch, c, d;
+ short row, col;
+ boolean first_miss = 1;
+ short possible_damage;
+ object *monster;
+
+ ch = 0;
+ while (!is_direction(ch = rgetchar(), &d)) {
+ sound_bell();
+ if (first_miss) {
+ messagef(0, "direction?");
+ first_miss = 0;
+ }
+ }
+ check_message();
+ if (ch == CANCEL) {
+ return;
+ }
+ row = rogue.row; col = rogue.col;
+ get_dir_rc(d, &row, &col, 0);
+
+ c = mvinch(row, col);
+ if (((c < 'A') || (c > 'Z')) ||
+ (!can_move(rogue.row, rogue.col, row, col))) {
+ messagef(0, "I see no monster there");
+ return;
+ }
+ if (!(fight_monster = object_at(&level_monsters, row, col))) {
+ return;
+ }
+ if (!(fight_monster->m_flags & STATIONARY)) {
+ possible_damage = ((get_damage(fight_monster->m_damage, 0) * 2) / 3);
+ } else {
+ possible_damage = fight_monster->stationary_damage - 1;
+ }
+ while (fight_monster) {
+ (void)one_move_rogue(ch, 0);
+ if (((!to_the_death) && (rogue.hp_current <= possible_damage)) ||
+ interrupted || (!(dungeon[row][col] & MONSTER))) {
+ fight_monster = 0;
+ } else {
+ monster = object_at(&level_monsters, row, col);
+ if (monster != fight_monster) {
+ fight_monster = 0;
+ }
+ }
+ }
+}
+
+void
+get_dir_rc(short dir, short *row, short *col, short allow_off_screen)
+{
+ switch(dir) {
+ case LEFT:
+ if (allow_off_screen || (*col > 0)) {
+ (*col)--;
+ }
+ break;
+ case DOWN:
+ if (allow_off_screen || (*row < (DROWS-2))) {
+ (*row)++;
+ }
+ break;
+ case UPWARD:
+ if (allow_off_screen || (*row > MIN_ROW)) {
+ (*row)--;
+ }
+ break;
+ case RIGHT:
+ if (allow_off_screen || (*col < (DCOLS-1))) {
+ (*col)++;
+ }
+ break;
+ case UPLEFT:
+ if (allow_off_screen || ((*row > MIN_ROW) && (*col > 0))) {
+ (*row)--;
+ (*col)--;
+ }
+ break;
+ case UPRIGHT:
+ if (allow_off_screen || ((*row > MIN_ROW) && (*col < (DCOLS-1)))) {
+ (*row)--;
+ (*col)++;
+ }
+ break;
+ case DOWNRIGHT:
+ if (allow_off_screen || ((*row < (DROWS-2)) && (*col < (DCOLS-1)))) {
+ (*row)++;
+ (*col)++;
+ }
+ break;
+ case DOWNLEFT:
+ if (allow_off_screen || ((*row < (DROWS-2)) && (*col > 0))) {
+ (*row)++;
+ (*col)--;
+ }
+ break;
+ }
+}
+
+int
+get_hit_chance(const object *weapon)
+{
+ short hit_chance;
+
+ hit_chance = 40;
+ hit_chance += 3 * to_hit(weapon);
+ hit_chance += (((2 * rogue.exp) + (2 * ring_exp)) - r_rings);
+ return(hit_chance);
+}
+
+int
+get_weapon_damage(const object *weapon)
+{
+ short damage;
+
+ damage = get_w_damage(weapon);
+ damage += damage_for_strength();
+ damage += ((((rogue.exp + ring_exp) - r_rings) + 1) / 2);
+ return(damage);
+}
+
+void
+s_con_mon(object *monster)
+{
+ if (con_mon) {
+ monster->m_flags |= CONFUSED;
+ monster->moves_confused += get_rand(12, 22);
+ messagef(0, "the monster appears confused");
+ con_mon = 0;
+ }
+}
diff --git a/rogue/init.c b/rogue/init.c
new file mode 100644
index 0000000..04ccbb7
--- /dev/null
+++ b/rogue/init.c
@@ -0,0 +1,366 @@
+/* $NetBSD: init.c,v 1.18 2008/08/08 16:10:47 drochner Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: init.c,v 1.18 2008/08/08 16:10:47 drochner Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * init.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "rogue.h"
+
+static void do_args(int, char **);
+static void do_opts(void);
+static void env_get_value(char **, char *, boolean);
+static void init_str(char **, const char *);
+static void player_init(void);
+
+static char *rest_file = NULL;
+static boolean init_curses = 0;
+
+char login_name[MAX_OPT_LEN];
+char *nick_name = NULL;
+boolean cant_int = 0;
+boolean did_int = 0;
+boolean score_only;
+boolean save_is_interactive = 1;
+boolean ask_quit = 1;
+boolean no_skull = 0;
+boolean passgo = 0;
+const char *error_file = "rogue.esave";
+const char *byebye_string = "Okay, bye bye!";
+gid_t gid, egid;
+
+int
+init(int argc, char *argv[])
+{
+ const char *pn;
+ int seed;
+ int fd;
+
+ gid = getgid();
+ egid = getegid();
+ setegid(gid);
+ /* Check for dirty tricks with closed fds 0, 1, 2 */
+ fd = open("/dev/null", O_RDONLY);
+ if (fd < 3)
+ exit(1);
+ close(fd);
+
+ seed = 0;
+ pn = md_gln();
+ if ((!pn) || (strlen(pn) >= MAX_OPT_LEN)) {
+ clean_up("Hey! Who are you?");
+ }
+ /* LOGIN_NAME_SIZE == MAX_OPT_LEN now, but just in case... */
+ (void)strlcpy(login_name, pn, sizeof(login_name));
+
+ do_args(argc, argv);
+ do_opts();
+
+ if (!score_only && !rest_file) {
+ printf("Hello %s, just a moment while I dig the dungeon...",
+ nick_name);
+ fflush(stdout);
+ }
+
+ if (!initscr()) {
+ fprintf(stderr, "couldn't initialize screen\n");
+ exit (0);
+ }
+ if ((LINES < DROWS) || (COLS < DCOLS)) {
+ clean_up("must be played on at least 80 x 24 screen");
+ }
+ start_window();
+ init_curses = 1;
+
+ md_heed_signals();
+
+ if (score_only) {
+ put_scores(NULL, 0);
+ }
+ seed = md_gseed();
+ (void)srrandom(seed);
+ if (rest_file) {
+ restore(rest_file);
+ return(1);
+ }
+ mix_colors();
+ get_wand_and_ring_materials();
+ make_scroll_titles();
+
+ level_objects.next_object = NULL;
+ level_monsters.next_monster = NULL;
+ player_init();
+ ring_stats(0);
+ return(0);
+}
+
+static void
+player_init(void)
+{
+ object *obj;
+
+ rogue.pack.next_object = NULL;
+
+ obj = alloc_object();
+ get_food(obj, 1);
+ (void)add_to_pack(obj, &rogue.pack, 1);
+
+ obj = alloc_object(); /* initial armor */
+ obj->what_is = ARMOR;
+ obj->which_kind = RINGMAIL;
+ obj->class = RINGMAIL+2;
+ obj->is_protected = 0;
+ obj->d_enchant = 1;
+ (void)add_to_pack(obj, &rogue.pack, 1);
+ do_wear(obj);
+
+ obj = alloc_object(); /* initial weapons */
+ obj->what_is = WEAPON;
+ obj->which_kind = MACE;
+ obj->damage = "2d3";
+ obj->hit_enchant = obj->d_enchant = 1;
+ obj->identified = 1;
+ (void)add_to_pack(obj, &rogue.pack, 1);
+ do_wield(obj);
+
+ obj = alloc_object();
+ obj->what_is = WEAPON;
+ obj->which_kind = BOW;
+ obj->damage = "1d2";
+ obj->hit_enchant = 1;
+ obj->d_enchant = 0;
+ obj->identified = 1;
+ (void)add_to_pack(obj, &rogue.pack, 1);
+
+ obj = alloc_object();
+ obj->what_is = WEAPON;
+ obj->which_kind = ARROW;
+ obj->quantity = get_rand(25, 35);
+ obj->damage = "1d2";
+ obj->hit_enchant = 0;
+ obj->d_enchant = 0;
+ obj->identified = 1;
+ (void)add_to_pack(obj, &rogue.pack, 1);
+}
+
+void
+clean_up(const char *estr)
+{
+ if (save_is_interactive) {
+ if (init_curses) {
+ move(DROWS-1, 0);
+ refresh();
+ stop_window();
+ }
+ printf("\n%s\n", estr);
+ }
+ md_exit(0);
+}
+
+void
+start_window(void)
+{
+ cbreak();
+ noecho();
+#ifndef BAD_NONL
+ nonl();
+#endif
+}
+
+void
+stop_window(void)
+{
+ endwin();
+}
+
+void
+byebye(int dummy __attribute__((unused)))
+{
+ md_ignore_signals();
+ if (ask_quit) {
+ quit(1);
+ } else {
+ clean_up(byebye_string);
+ }
+ md_heed_signals();
+}
+
+void
+onintr(int dummy __attribute__((unused)))
+{
+ md_ignore_signals();
+ if (cant_int) {
+ did_int = 1;
+ } else {
+ check_message();
+ messagef(1, "interrupt");
+ }
+ md_heed_signals();
+}
+
+void
+error_save(int dummy __attribute__((unused)))
+{
+ save_is_interactive = 0;
+ save_into_file(error_file);
+ clean_up("");
+}
+
+static void
+do_args(int argc, char *argv[])
+{
+ int i, j;
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ for (j = 1; argv[i][j]; j++) {
+ switch(argv[i][j]) {
+ case 's':
+ score_only = 1;
+ break;
+ }
+ }
+ } else {
+ rest_file = argv[i];
+ }
+ }
+}
+
+static void
+do_opts(void)
+{
+ char *eptr;
+
+ if ((eptr = md_getenv("ROGUEOPTS")) != NULL) {
+ for (;;) {
+ while ((*eptr) == ' ') {
+ eptr++;
+ }
+ if (!(*eptr)) {
+ break;
+ }
+ if (!strncmp(eptr, "fruit=", 6)) {
+ eptr += 6;
+ env_get_value(&fruit, eptr, 1);
+ } else if (!strncmp(eptr, "file=", 5)) {
+ eptr += 5;
+ env_get_value(&save_file, eptr, 0);
+ } else if (!strncmp(eptr, "jump", 4)) {
+ jump = 1;
+ } else if (!strncmp(eptr, "name=", 5)) {
+ eptr += 5;
+ env_get_value(&nick_name, eptr, 0);
+ } else if (!strncmp(eptr, "noaskquit", 9)) {
+ ask_quit = 0;
+ } else if (!strncmp(eptr, "noskull", 5) ||
+ !strncmp(eptr,"notomb", 6)) {
+ no_skull = 1;
+ } else if (!strncmp(eptr, "passgo", 5)) {
+ passgo = 1;
+ }
+ while ((*eptr) && (*eptr != ',')) {
+ eptr++;
+ }
+ if (!(*(eptr++))) {
+ break;
+ }
+ }
+ }
+ /* If some strings have not been set through ROGUEOPTS, assign defaults
+ * to them so that the options editor has data to work with.
+ */
+ init_str(&nick_name, login_name);
+ init_str(&save_file, "rogue.save");
+ init_str(&fruit, "slime-mold");
+}
+
+static void
+env_get_value(char **s, char *e, boolean add_blank)
+{
+ short i = 0;
+ const char *t;
+
+ t = e;
+
+ while ((*e) && (*e != ',')) {
+ if (*e == ':') {
+ *e = ';'; /* ':' reserved for score file purposes */
+ }
+ e++;
+ if (++i >= MAX_OPT_LEN) {
+ break;
+ }
+ }
+ /* note: edit_opts() in room.c depends on this being the right size */
+ *s = md_malloc(MAX_OPT_LEN + 2);
+ if (*s == NULL)
+ clean_up("out of memory");
+ (void)strncpy(*s, t, i);
+ if (add_blank) {
+ (*s)[i++] = ' ';
+ }
+ (*s)[i] = '\0';
+}
+
+static void
+init_str(char **str, const char *dflt)
+{
+ if (!(*str)) {
+ /* note: edit_opts() in room.c depends on this size */
+ *str = md_malloc(MAX_OPT_LEN + 2);
+ if (*str == NULL)
+ clean_up("out of memory");
+ (void)strlcpy(*str, dflt, MAX_OPT_LEN + 2);
+ }
+}
diff --git a/rogue/inventory.c b/rogue/inventory.c
new file mode 100644
index 0000000..934e8aa
--- /dev/null
+++ b/rogue/inventory.c
@@ -0,0 +1,841 @@
+/* $NetBSD: inventory.c,v 1.15 2011/08/26 06:18:17 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)inventory.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: inventory.c,v 1.15 2011/08/26 06:18:17 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * inventory.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include <stdarg.h>
+#include "rogue.h"
+
+boolean is_wood[WANDS];
+const char *press_space = " --press space to continue--";
+
+static const char *const wand_materials[WAND_MATERIALS] = {
+ "steel ",
+ "bronze ",
+ "gold ",
+ "silver ",
+ "copper ",
+ "nickel ",
+ "cobalt ",
+ "tin ",
+ "iron ",
+ "magnesium ",
+ "chrome ",
+ "carbon ",
+ "platinum ",
+ "silicon ",
+ "titanium ",
+
+ "teak ",
+ "oak ",
+ "cherry ",
+ "birch ",
+ "pine ",
+ "cedar ",
+ "redwood ",
+ "balsa ",
+ "ivory ",
+ "walnut ",
+ "maple ",
+ "mahogany ",
+ "elm ",
+ "palm ",
+ "wooden "
+};
+
+static const char *const gems[GEMS] = {
+ "diamond ",
+ "stibotantalite ",
+ "lapi-lazuli ",
+ "ruby ",
+ "emerald ",
+ "sapphire ",
+ "amethyst ",
+ "quartz ",
+ "tiger-eye ",
+ "opal ",
+ "agate ",
+ "turquoise ",
+ "pearl ",
+ "garnet "
+};
+
+static const char *const syllables[MAXSYLLABLES] = {
+ "blech ",
+ "foo ",
+ "barf ",
+ "rech ",
+ "bar ",
+ "blech ",
+ "quo ",
+ "bloto ",
+ "oh ",
+ "caca ",
+ "blorp ",
+ "erp ",
+ "festr ",
+ "rot ",
+ "slie ",
+ "snorf ",
+ "iky ",
+ "yuky ",
+ "ooze ",
+ "ah ",
+ "bahl ",
+ "zep ",
+ "druhl ",
+ "flem ",
+ "behil ",
+ "arek ",
+ "mep ",
+ "zihr ",
+ "grit ",
+ "kona ",
+ "kini ",
+ "ichi ",
+ "tims ",
+ "ogr ",
+ "oo ",
+ "ighr ",
+ "coph ",
+ "swerr ",
+ "mihln ",
+ "poxi "
+};
+
+#define COMS 48
+
+struct id_com_s {
+ short com_char;
+ const char *com_desc;
+};
+
+static const struct id_com_s com_id_tab[COMS] = {
+ {'?', "? prints help"},
+ {'r', "r read scroll"},
+ {'/', "/ identify object"},
+ {'e', "e eat food"},
+ {'h', "h left "},
+ {'w', "w wield a weapon"},
+ {'j', "j down"},
+ {'W', "W wear armor"},
+ {'k', "k up"},
+ {'T', "T take armor off"},
+ {'l', "l right"},
+ {'P', "P put on ring"},
+ {'y', "y up & left"},
+ {'R', "R remove ring"},
+ {'u', "u up & right"},
+ {'d', "d drop object"},
+ {'b', "b down & left"},
+ {'c', "c call object"},
+ {'n', "n down & right"},
+ {'\0', "<SHIFT><dir>: run that way"},
+ {')', ") print current weapon"},
+ {'\0', "<CTRL><dir>: run till adjacent"},
+ {']', "] print current armor"},
+ {'f', "f<dir> fight till death or near death"},
+ {'=', "= print current rings"},
+ {'t', "t<dir> throw something"},
+ {'\001', "^A print Hp-raise average"},
+ {'m', "m<dir> move onto without picking up"},
+ {'z', "z<dir> zap a wand in a direction"},
+ {'o', "o examine/set options"},
+ {'^', "^<dir> identify trap type"},
+ {'\022', "^R redraw screen"},
+ {'&', "& save screen into 'rogue.screen'"},
+ {'s', "s search for trap/secret door"},
+ {'\020', "^P repeat last message"},
+ {'>', "> go down a staircase"},
+ {'\033', "^[ cancel command"},
+ {'<', "< go up a staircase"},
+ {'S', "S save game"},
+ {'.', ". rest for a turn"},
+ {'Q', "Q quit"},
+ {',', ", pick something up"},
+ {'!', "! shell escape"},
+ {'i', "i inventory"},
+ {'F', "F<dir> fight till either of you dies"},
+ {'I', "I inventory single item"},
+ {'v', "v print version number"},
+ {'q', "q quaff potion" }
+};
+
+static int get_com_id(int *, short);
+static int pr_com_id(int);
+static int pr_motion_char(int);
+
+void
+inventory(const object *pack, unsigned short mask)
+{
+ object *obj;
+ short i = 0, j;
+ size_t maxlen = 0, n;
+ short row, col;
+
+ struct {
+ short letter;
+ short sepchar;
+ char desc[DCOLS];
+ char savebuf[DCOLS+8];
+ } descs[MAX_PACK_COUNT+1];
+
+
+ obj = pack->next_object;
+
+ if (!obj) {
+ messagef(0, "your pack is empty");
+ return;
+ }
+ while (obj) {
+ if (obj->what_is & mask) {
+ descs[i].letter = obj->ichar;
+ descs[i].sepchar = ((obj->what_is & ARMOR) && obj->is_protected)
+ ? '}' : ')';
+ get_desc(obj, descs[i].desc, sizeof(descs[i].desc));
+ n = strlen(descs[i].desc) + 4;
+ if (n > maxlen) {
+ maxlen = n;
+ }
+ i++;
+ /*assert(i<=MAX_PACK_COUNT);*/
+ }
+ obj = obj->next_object;
+ }
+ if (maxlen < 27) maxlen = 27;
+ if (maxlen > DCOLS-2) maxlen = DCOLS-2;
+ col = DCOLS - (maxlen + 2);
+
+ for (row = 0; ((row <= i) && (row < DROWS)); row++) {
+ for (j = col; j < DCOLS; j++) {
+ descs[row].savebuf[j-col] = mvinch(row, j);
+ }
+ descs[row].savebuf[j-col] = 0;
+ if (row < i) {
+ mvprintw(row, col, " %c%c %s",
+ descs[row].letter, descs[row].sepchar,
+ descs[row].desc);
+ }
+ else {
+ mvaddstr(row, col, press_space);
+ }
+ clrtoeol();
+ }
+ refresh();
+ wait_for_ack();
+
+ move(0, 0);
+ clrtoeol();
+
+ for (j = 1; ((j <= i) && (j < DROWS)); j++) {
+ mvaddstr(j, col, descs[j].savebuf);
+ }
+}
+
+void
+id_com(void)
+{
+ int ch = 0;
+ short i, j, k;
+
+ while (ch != CANCEL) {
+ check_message();
+ messagef(0, "Character you want help for (* for all):");
+
+ refresh();
+ ch = getchar();
+
+ switch(ch) {
+ case LIST:
+ {
+ char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS];
+ short rows = (((COMS / 2) + (COMS % 2)) + 1);
+ boolean need_two_screens = FALSE;
+
+ if (rows > LINES) {
+ need_two_screens = 1;
+ rows = LINES;
+ }
+ k = 0;
+
+ for (i = 0; i < rows; i++) {
+ for (j = 0; j < DCOLS; j++) {
+ save[i][j] = mvinch(i, j);
+ }
+ }
+MORE:
+ for (i = 0; i < rows; i++) {
+ move(i, 0);
+ clrtoeol();
+ }
+ for (i = 0; i < (rows-1); i++) {
+ if (i < (LINES-1)) {
+ if (((i + i) < COMS) && ((i+i+k) < COMS)) {
+ mvaddstr(i, 0, com_id_tab[i+i+k].com_desc);
+ }
+ if (((i + i + 1) < COMS) && ((i+i+k+1) < COMS)) {
+ mvaddstr(i, (DCOLS/2),
+ com_id_tab[i+i+k+1].com_desc);
+ }
+ }
+ }
+ mvaddstr(rows - 1, 0, need_two_screens ? more : press_space);
+ refresh();
+ wait_for_ack();
+
+ if (need_two_screens) {
+ k += ((rows-1) * 2);
+ need_two_screens = 0;
+ goto MORE;
+ }
+ for (i = 0; i < rows; i++) {
+ move(i, 0);
+ for (j = 0; j < DCOLS; j++) {
+ addch(save[i][j]);
+ }
+ }
+ }
+ break;
+ default:
+ if (!pr_com_id(ch)) {
+ if (!pr_motion_char(ch)) {
+ check_message();
+ messagef(0, "unknown character");
+ }
+ }
+ ch = CANCEL;
+ break;
+ }
+ }
+}
+
+static int
+pr_com_id(int ch)
+{
+ int i;
+
+ if (!get_com_id(&i, ch)) {
+ return(0);
+ }
+ check_message();
+ messagef(0, "%s", com_id_tab[i].com_desc);
+ return(1);
+}
+
+static int
+get_com_id(int *indexp, short ch)
+{
+ short i;
+
+ for (i = 0; i < COMS; i++) {
+ if (com_id_tab[i].com_char == ch) {
+ *indexp = i;
+ return(1);
+ }
+ }
+ return(0);
+}
+
+static int
+pr_motion_char(int ch)
+{
+ if ( (ch == 'J') ||
+ (ch == 'K') ||
+ (ch == 'L') ||
+ (ch == 'H') ||
+ (ch == 'Y') ||
+ (ch == 'U') ||
+ (ch == 'N') ||
+ (ch == 'B') ||
+ (ch == '\012') ||
+ (ch == '\013') ||
+ (ch == '\010') ||
+ (ch == '\014') ||
+ (ch == '\025') ||
+ (ch == '\031') ||
+ (ch == '\016') ||
+ (ch == '\002')) {
+ const char *until;
+ int n = 0; /* XXX: GCC */
+ if (ch <= '\031') {
+ ch += 96;
+ until = " until adjacent";
+ } else {
+ ch += 32;
+ until = "";
+ }
+ (void)get_com_id(&n, ch);
+ check_message();
+ messagef(0, "run %s%s", com_id_tab[n].com_desc + 8, until);
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+void
+mix_colors(void)
+{
+ short i, j, k;
+ char t[MAX_ID_TITLE_LEN];
+
+ for (i = 0; i <= 32; i++) {
+ j = get_rand(0, (POTIONS - 1));
+ k = get_rand(0, (POTIONS - 1));
+ strlcpy(t, id_potions[j].title, sizeof(t));
+ strlcpy(id_potions[j].title, id_potions[k].title,
+ sizeof(id_potions[j].title));
+ strlcpy(id_potions[k].title, t, sizeof(id_potions[k].title));
+ }
+}
+
+void
+make_scroll_titles(void)
+{
+ short i, j, n;
+ short sylls, s;
+ size_t maxlen = sizeof(id_scrolls[0].title);
+
+ for (i = 0; i < SCROLS; i++) {
+ sylls = get_rand(2, 5);
+ (void)strlcpy(id_scrolls[i].title, "'", maxlen);
+
+ for (j = 0; j < sylls; j++) {
+ s = get_rand(1, (MAXSYLLABLES-1));
+ (void)strlcat(id_scrolls[i].title, syllables[s],
+ maxlen);
+ }
+ /* trim trailing space */
+ n = strlen(id_scrolls[i].title);
+ id_scrolls[i].title[n-1] = 0;
+
+ (void)strlcat(id_scrolls[i].title, "' ", maxlen);
+ }
+}
+
+struct sbuf {
+ char *buf;
+ size_t maxlen;
+};
+
+static void sbuf_init(struct sbuf *s, char *buf, size_t maxlen);
+static void sbuf_addstr(struct sbuf *s, const char *str);
+static void sbuf_addf(struct sbuf *s, const char *fmt, ...) __printflike(2,3);
+static void desc_count(struct sbuf *s, int n);
+static void desc_called(struct sbuf *s, const object *);
+
+static
+void
+sbuf_init(struct sbuf *s, char *buf, size_t maxlen)
+{
+ s->buf = buf;
+ s->maxlen = maxlen;
+ /*assert(maxlen>0);*/
+ s->buf[0] = 0;
+}
+
+static
+void
+sbuf_addstr(struct sbuf *s, const char *str)
+{
+ strlcat(s->buf, str, s->maxlen);
+}
+
+static
+void
+sbuf_addf(struct sbuf *s, const char *fmt, ...)
+{
+ va_list ap;
+ size_t initlen;
+
+ initlen = strlen(s->buf);
+ va_start(ap, fmt);
+ vsnprintf(s->buf+initlen, s->maxlen-initlen, fmt, ap);
+ va_end(ap);
+}
+
+static
+void
+desc_count(struct sbuf *s, int n)
+{
+ if (n == 1) {
+ sbuf_addstr(s, "an ");
+ } else {
+ sbuf_addf(s, "%d ", n);
+ }
+}
+
+static
+void
+desc_called(struct sbuf *s, const object *obj)
+{
+ struct id *id_table;
+
+ id_table = get_id_table(obj);
+ sbuf_addstr(s, name_of(obj));
+ sbuf_addstr(s, "called ");
+ sbuf_addstr(s, id_table[obj->which_kind].title);
+}
+
+void
+get_desc(const object *obj, char *desc, size_t desclen)
+{
+ const char *item_name;
+ struct id *id_table;
+ struct sbuf db;
+ unsigned short objtype_id_status;
+
+ if (obj->what_is == AMULET) {
+ (void)strlcpy(desc, "the amulet of Yendor ", desclen);
+ return;
+ }
+
+ if (obj->what_is == GOLD) {
+ snprintf(desc, desclen, "%d pieces of gold", obj->quantity);
+ return;
+ }
+
+ item_name = name_of(obj);
+ id_table = get_id_table(obj);
+ if (wizard || id_table == NULL) {
+ objtype_id_status = IDENTIFIED;
+ }
+ else {
+ objtype_id_status = id_table[obj->which_kind].id_status;
+ }
+ if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) {
+ if (obj->identified) {
+ objtype_id_status = IDENTIFIED;
+ }
+ }
+
+ sbuf_init(&db, desc, desclen);
+
+ switch (obj->what_is) {
+ case FOOD:
+ if (obj->which_kind == RATION) {
+ if (obj->quantity > 1) {
+ sbuf_addf(&db,
+ "%d rations of %s", obj->quantity,
+ item_name);
+ } else {
+ sbuf_addf(&db, "some %s", item_name);
+ }
+ } else {
+ sbuf_addf(&db, "an %s", item_name);
+ }
+ break;
+ case SCROL:
+ desc_count(&db, obj->quantity);
+ if (objtype_id_status==UNIDENTIFIED) {
+ sbuf_addstr(&db, item_name);
+ sbuf_addstr(&db, "entitled: ");
+ sbuf_addstr(&db, id_table[obj->which_kind].title);
+ } else if (objtype_id_status==CALLED) {
+ desc_called(&db, obj);
+ } else {
+ sbuf_addstr(&db, item_name);
+ sbuf_addstr(&db, id_table[obj->which_kind].real);
+ }
+ break;
+ case POTION:
+ desc_count(&db, obj->quantity);
+ if (objtype_id_status==UNIDENTIFIED) {
+ sbuf_addstr(&db, id_table[obj->which_kind].title);
+ sbuf_addstr(&db, item_name);
+ } else if (objtype_id_status==CALLED) {
+ desc_called(&db, obj);
+ } else {
+ sbuf_addstr(&db, item_name);
+ sbuf_addstr(&db, id_table[obj->which_kind].real);
+ }
+ break;
+ case WAND:
+ desc_count(&db, obj->quantity);
+ if (objtype_id_status==UNIDENTIFIED) {
+ sbuf_addstr(&db, id_table[obj->which_kind].title);
+ sbuf_addstr(&db, item_name);
+ } else if (objtype_id_status==CALLED) {
+ desc_called(&db, obj);
+ } else {
+ sbuf_addstr(&db, item_name);
+ sbuf_addstr(&db, id_table[obj->which_kind].real);
+ if (wizard || obj->identified) {
+ sbuf_addf(&db, "[%d]", obj->class);
+ }
+ }
+ break;
+ case RING:
+ desc_count(&db, obj->quantity);
+ if (objtype_id_status==UNIDENTIFIED) {
+ sbuf_addstr(&db, id_table[obj->which_kind].title);
+ sbuf_addstr(&db, item_name);
+ } else if (objtype_id_status==CALLED) {
+ desc_called(&db, obj);
+ } else {
+ if ((wizard || obj->identified) &&
+ (obj->which_kind == DEXTERITY ||
+ obj->which_kind == ADD_STRENGTH)) {
+ sbuf_addf(&db, "%+d ", obj->class);
+ }
+ sbuf_addstr(&db, item_name);
+ sbuf_addstr(&db, id_table[obj->which_kind].real);
+ }
+ break;
+ case ARMOR:
+ /* no desc_count() */
+ if (objtype_id_status==UNIDENTIFIED) {
+ sbuf_addstr(&db, id_table[obj->which_kind].title);
+ } else {
+ sbuf_addf(&db, "%+d %s[%d] ", obj->d_enchant,
+ id_table[obj->which_kind].title,
+ get_armor_class(obj));
+ }
+ break;
+ case WEAPON:
+ desc_count(&db, obj->quantity);
+ if (objtype_id_status==UNIDENTIFIED) {
+ sbuf_addstr(&db, name_of(obj));
+ } else {
+ sbuf_addf(&db, "%+d,%+d %s",
+ obj->hit_enchant, obj->d_enchant,
+ name_of(obj));
+ }
+ break;
+ }
+
+ if (obj->in_use_flags & BEING_WIELDED) {
+ sbuf_addstr(&db, "in hand");
+ } else if (obj->in_use_flags & BEING_WORN) {
+ sbuf_addstr(&db, "being worn");
+ } else if (obj->in_use_flags & ON_LEFT_HAND) {
+ sbuf_addstr(&db, "on left hand");
+ } else if (obj->in_use_flags & ON_RIGHT_HAND) {
+ sbuf_addstr(&db, "on right hand");
+ }
+
+ if (!strncmp(db.buf, "an ", 3)) {
+ if (!is_vowel(db.buf[3])) {
+ memmove(db.buf+2, db.buf+3, strlen(db.buf+3)+1);
+ db.buf[1] = ' ';
+ }
+ }
+}
+
+void
+get_wand_and_ring_materials(void)
+{
+ short i, j;
+ boolean used[WAND_MATERIALS];
+
+ for (i = 0; i < WAND_MATERIALS; i++) {
+ used[i] = 0;
+ }
+ for (i = 0; i < WANDS; i++) {
+ do {
+ j = get_rand(0, WAND_MATERIALS-1);
+ } while (used[j]);
+ used[j] = 1;
+ (void)strlcpy(id_wands[i].title, wand_materials[j],
+ sizeof(id_wands[i].title));
+ is_wood[i] = (j > MAX_METAL);
+ }
+ for (i = 0; i < GEMS; i++) {
+ used[i] = 0;
+ }
+ for (i = 0; i < RINGS; i++) {
+ do {
+ j = get_rand(0, GEMS-1);
+ } while (used[j]);
+ used[j] = 1;
+ (void)strlcpy(id_rings[i].title, gems[j],
+ sizeof(id_rings[i].title));
+ }
+}
+
+void
+single_inv(short ichar)
+{
+ short ch, ch2;
+ char desc[DCOLS];
+ object *obj;
+
+ ch = ichar ? ichar : pack_letter("inventory what?", ALL_OBJECTS);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ ch2 = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')';
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "%c%c %s", ch, ch2, desc);
+}
+
+struct id *
+get_id_table(const object *obj)
+{
+ switch(obj->what_is) {
+ case SCROL:
+ return(id_scrolls);
+ case POTION:
+ return(id_potions);
+ case WAND:
+ return(id_wands);
+ case RING:
+ return(id_rings);
+ case WEAPON:
+ return(id_weapons);
+ case ARMOR:
+ return(id_armors);
+ }
+ return((struct id *)0);
+}
+
+void
+inv_armor_weapon(boolean is_weapon)
+{
+ if (is_weapon) {
+ if (rogue.weapon) {
+ single_inv(rogue.weapon->ichar);
+ } else {
+ messagef(0, "not wielding anything");
+ }
+ } else {
+ if (rogue.armor) {
+ single_inv(rogue.armor->ichar);
+ } else {
+ messagef(0, "not wearing anything");
+ }
+ }
+}
+
+void
+id_type(void)
+{
+ const char *id;
+ int ch;
+
+ messagef(0, "what do you want identified?");
+
+ ch = rgetchar();
+
+ if ((ch >= 'A') && (ch <= 'Z')) {
+ id = m_names[ch-'A'];
+ } else if (ch < 32) {
+ check_message();
+ return;
+ } else {
+ switch(ch) {
+ case '@':
+ id = "you";
+ break;
+ case '%':
+ id = "staircase";
+ break;
+ case '^':
+ id = "trap";
+ break;
+ case '+':
+ id = "door";
+ break;
+ case '-':
+ case '|':
+ id = "wall of a room";
+ break;
+ case '.':
+ id = "floor";
+ break;
+ case '#':
+ id = "passage";
+ break;
+ case ' ':
+ id = "solid rock";
+ break;
+ case '=':
+ id = "ring";
+ break;
+ case '?':
+ id = "scroll";
+ break;
+ case '!':
+ id = "potion";
+ break;
+ case '/':
+ id = "wand or staff";
+ break;
+ case ')':
+ id = "weapon";
+ break;
+ case ']':
+ id = "armor";
+ break;
+ case '*':
+ id = "gold";
+ break;
+ case ':':
+ id = "food";
+ break;
+ case ',':
+ id = "the Amulet of Yendor";
+ break;
+ default:
+ id = "unknown character";
+ break;
+ }
+ }
+ check_message();
+ messagef(0, "'%c': %s", ch, id);
+}
diff --git a/rogue/level.c b/rogue/level.c
new file mode 100644
index 0000000..4b95277
--- /dev/null
+++ b/rogue/level.c
@@ -0,0 +1,900 @@
+/* $NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)level.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * level.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+#define SWAP(x,y) (t = (x), (x) = (y), (y) = t)
+
+static void add_mazes(void);
+static int connect_rooms(short, short);
+static void draw_simple_passage(short, short, short, short, short);
+static void fill_it(int, boolean);
+static void fill_out_level(void);
+static int get_exp_level(long);
+static void hide_boxed_passage(short, short, short, short, short);
+static void make_maze(short, short, short, short, short, short);
+static void make_room(short, short, short, short);
+static boolean mask_room(short, short *, short *, unsigned short);
+static void mix_random_rooms(void);
+static void put_door(room *, short, short *, short *);
+static void recursive_deadend(short, const short *, short, short);
+static int same_col(int, int);
+static int same_row(int, int);
+
+short cur_level = 0;
+short max_level = 1;
+short cur_room;
+const char *new_level_message = NULL;
+short party_room = NO_ROOM;
+
+static short r_de;
+
+const long level_points[MAX_EXP_LEVEL] = {
+ 10L,
+ 20L,
+ 40L,
+ 80L,
+ 160L,
+ 320L,
+ 640L,
+ 1300L,
+ 2600L,
+ 5200L,
+ 10000L,
+ 20000L,
+ 40000L,
+ 80000L,
+ 160000L,
+ 320000L,
+ 1000000L,
+ 3333333L,
+ 6666666L,
+ MAX_EXP,
+ 99900000L
+};
+
+static short random_rooms[MAXROOMS] = {3, 7, 5, 2, 0, 6, 1, 4, 8};
+
+void
+make_level(void)
+{
+ short i, j;
+ short must_1, must_2, must_3;
+ boolean big_room;
+
+ must_2 = must_3 = 0;
+ if (cur_level < LAST_DUNGEON) {
+ cur_level++;
+ }
+ if (cur_level > max_level) {
+ max_level = cur_level;
+ }
+ must_1 = get_rand(0, 5);
+
+ switch(must_1) {
+ case 0:
+ must_1 = 0;
+ must_2 = 1;
+ must_3 = 2;
+ break;
+ case 1:
+ must_1 = 3;
+ must_2 = 4;
+ must_3 = 5;
+ break;
+ case 2:
+ must_1 = 6;
+ must_2 = 7;
+ must_3 = 8;
+ break;
+ case 3:
+ must_1 = 0;
+ must_2 = 3;
+ must_3 = 6;
+ break;
+ case 4:
+ must_1 = 1;
+ must_2 = 4;
+ must_3 = 7;
+ break;
+ case 5:
+ must_1 = 2;
+ must_2 = 5;
+ must_3 = 8;
+ break;
+ }
+ if (rand_percent(8)) {
+ party_room = 0;
+ }
+ big_room = ((party_room != NO_ROOM) && rand_percent(1));
+ if (big_room) {
+ make_room(BIG_ROOM, 0, 0, 0);
+ } else {
+ for (i = 0; i < MAXROOMS; i++) {
+ make_room(i, must_1, must_2, must_3);
+ }
+ }
+ if (!big_room) {
+ add_mazes();
+
+ mix_random_rooms();
+
+ for (j = 0; j < MAXROOMS; j++) {
+
+ i = random_rooms[j];
+
+ if (i < (MAXROOMS-1)) {
+ (void)connect_rooms(i, i+1);
+ }
+ if (i < (MAXROOMS-3)) {
+ (void)connect_rooms(i, i+3);
+ }
+ if (i < (MAXROOMS-2)) {
+ if (rooms[i+1].is_room & R_NOTHING) {
+ if (connect_rooms(i, i+2)) {
+ rooms[i+1].is_room = R_CROSS;
+ }
+ }
+ }
+ if (i < (MAXROOMS-6)) {
+ if (rooms[i+3].is_room & R_NOTHING) {
+ if (connect_rooms(i, i+6)) {
+ rooms[i+3].is_room = R_CROSS;
+ }
+ }
+ }
+ if (is_all_connected()) {
+ break;
+ }
+ }
+ fill_out_level();
+ }
+ if (!has_amulet() && (cur_level >= AMULET_LEVEL)) {
+ put_amulet();
+ }
+}
+
+static void
+make_room(short rn, short r1, short r2, short r3)
+{
+ short left_col, right_col, top_row, bottom_row;
+ short width, height;
+ short row_offset, col_offset;
+ short i, j, ch;
+
+ left_col = right_col = top_row = bottom_row = 0;
+ switch(rn) {
+ case 0:
+ left_col = 0;
+ right_col = COL1-1;
+ top_row = MIN_ROW;
+ bottom_row = ROW1-1;
+ break;
+ case 1:
+ left_col = COL1+1;
+ right_col = COL2-1;
+ top_row = MIN_ROW;
+ bottom_row = ROW1-1;
+ break;
+ case 2:
+ left_col = COL2+1;
+ right_col = DCOLS-1;
+ top_row = MIN_ROW;
+ bottom_row = ROW1-1;
+ break;
+ case 3:
+ left_col = 0;
+ right_col = COL1-1;
+ top_row = ROW1+1;
+ bottom_row = ROW2-1;
+ break;
+ case 4:
+ left_col = COL1+1;
+ right_col = COL2-1;
+ top_row = ROW1+1;
+ bottom_row = ROW2-1;
+ break;
+ case 5:
+ left_col = COL2+1;
+ right_col = DCOLS-1;
+ top_row = ROW1+1;
+ bottom_row = ROW2-1;
+ break;
+ case 6:
+ left_col = 0;
+ right_col = COL1-1;
+ top_row = ROW2+1;
+ bottom_row = DROWS - 2;
+ break;
+ case 7:
+ left_col = COL1+1;
+ right_col = COL2-1;
+ top_row = ROW2+1;
+ bottom_row = DROWS - 2;
+ break;
+ case 8:
+ left_col = COL2+1;
+ right_col = DCOLS-1;
+ top_row = ROW2+1;
+ bottom_row = DROWS - 2;
+ break;
+ case BIG_ROOM:
+ top_row = get_rand(MIN_ROW, MIN_ROW+5);
+ bottom_row = get_rand(DROWS-7, DROWS-2);
+ left_col = get_rand(0, 10);
+ right_col = get_rand(DCOLS-11, DCOLS-1);
+ rn = 0;
+ goto B;
+ }
+ height = get_rand(4, (bottom_row - top_row + 1));
+ width = get_rand(7, (right_col - left_col - 2));
+
+ row_offset = get_rand(0, ((bottom_row - top_row) - height + 1));
+ col_offset = get_rand(0, ((right_col - left_col) - width + 1));
+
+ top_row += row_offset;
+ bottom_row = top_row + height - 1;
+
+ left_col += col_offset;
+ right_col = left_col + width - 1;
+
+ if ((rn != r1) && (rn != r2) && (rn != r3) && rand_percent(40)) {
+ goto END;
+ }
+B:
+ rooms[rn].is_room = R_ROOM;
+
+ for (i = top_row; i <= bottom_row; i++) {
+ for (j = left_col; j <= right_col; j++) {
+ if ((i == top_row) || (i == bottom_row)) {
+ ch = HORWALL;
+ } else if ( ((i != top_row) && (i != bottom_row)) &&
+ ((j == left_col) || (j == right_col))) {
+ ch = VERTWALL;
+ } else {
+ ch = FLOOR;
+ }
+ dungeon[i][j] = ch;
+ }
+ }
+END:
+ rooms[rn].top_row = top_row;
+ rooms[rn].bottom_row = bottom_row;
+ rooms[rn].left_col = left_col;
+ rooms[rn].right_col = right_col;
+}
+
+static int
+connect_rooms(short room1, short room2)
+{
+ short row1, col1, row2, col2, dir;
+
+ if ((!(rooms[room1].is_room & (R_ROOM | R_MAZE))) ||
+ (!(rooms[room2].is_room & (R_ROOM | R_MAZE)))) {
+ return(0);
+ }
+ if (same_row(room1, room2) &&
+ (rooms[room1].left_col > rooms[room2].right_col)) {
+ put_door(&rooms[room1], LEFT, &row1, &col1);
+ put_door(&rooms[room2], RIGHT, &row2, &col2);
+ dir = LEFT;
+ } else if (same_row(room1, room2) &&
+ (rooms[room2].left_col > rooms[room1].right_col)) {
+ put_door(&rooms[room1], RIGHT, &row1, &col1);
+ put_door(&rooms[room2], LEFT, &row2, &col2);
+ dir = RIGHT;
+ } else if (same_col(room1, room2) &&
+ (rooms[room1].top_row > rooms[room2].bottom_row)) {
+ put_door(&rooms[room1], UPWARD, &row1, &col1);
+ put_door(&rooms[room2], DOWN, &row2, &col2);
+ dir = UPWARD;
+ } else if (same_col(room1, room2) &&
+ (rooms[room2].top_row > rooms[room1].bottom_row)) {
+ put_door(&rooms[room1], DOWN, &row1, &col1);
+ put_door(&rooms[room2], UPWARD, &row2, &col2);
+ dir = DOWN;
+ } else {
+ return(0);
+ }
+
+ do {
+ draw_simple_passage(row1, col1, row2, col2, dir);
+ } while (rand_percent(4));
+
+ rooms[room1].doors[dir/2].oth_room = room2;
+ rooms[room1].doors[dir/2].oth_row = row2;
+ rooms[room1].doors[dir/2].oth_col = col2;
+
+ rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_room = room1;
+ rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_row = row1;
+ rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_col = col1;
+ return(1);
+}
+
+void
+clear_level(void)
+{
+ short i, j;
+
+ for (i = 0; i < MAXROOMS; i++) {
+ rooms[i].is_room = R_NOTHING;
+ for (j = 0; j < 4; j++) {
+ rooms[i].doors[j].oth_room = NO_ROOM;
+ }
+ }
+
+ for (i = 0; i < MAX_TRAPS; i++) {
+ traps[i].trap_type = NO_TRAP;
+ }
+ for (i = 0; i < DROWS; i++) {
+ for (j = 0; j < DCOLS; j++) {
+ dungeon[i][j] = NOTHING;
+ }
+ }
+ detect_monster = see_invisible = 0;
+ being_held = bear_trap = 0;
+ party_room = NO_ROOM;
+ rogue.row = rogue.col = -1;
+ clear();
+}
+
+static void
+put_door(room *rm, short dir, short *row, short *col)
+{
+ short wall_width;
+
+ wall_width = (rm->is_room & R_MAZE) ? 0 : 1;
+
+ switch(dir) {
+ case UPWARD:
+ case DOWN:
+ *row = ((dir == UPWARD) ? rm->top_row : rm->bottom_row);
+ do {
+ *col = get_rand(rm->left_col+wall_width,
+ rm->right_col-wall_width);
+ } while (!(dungeon[*row][*col] & (HORWALL | TUNNEL)));
+ break;
+ case RIGHT:
+ case LEFT:
+ *col = (dir == LEFT) ? rm->left_col : rm->right_col;
+ do {
+ *row = get_rand(rm->top_row+wall_width,
+ rm->bottom_row-wall_width);
+ } while (!(dungeon[*row][*col] & (VERTWALL | TUNNEL)));
+ break;
+ }
+ if (rm->is_room & R_ROOM) {
+ dungeon[*row][*col] = DOOR;
+ }
+ if ((cur_level > 2) && rand_percent(HIDE_PERCENT)) {
+ dungeon[*row][*col] |= HIDDEN;
+ }
+ rm->doors[dir/2].door_row = *row;
+ rm->doors[dir/2].door_col = *col;
+}
+
+static void
+draw_simple_passage(short row1, short col1, short row2, short col2, short dir)
+{
+ short i, middle, t;
+
+ if ((dir == LEFT) || (dir == RIGHT)) {
+ if (col1 > col2) {
+ SWAP(row1, row2);
+ SWAP(col1, col2);
+ }
+ middle = get_rand(col1+1, col2-1);
+ for (i = col1+1; i != middle; i++) {
+ dungeon[row1][i] = TUNNEL;
+ }
+ for (i = row1; i != row2; i += (row1 > row2) ? -1 : 1) {
+ dungeon[i][middle] = TUNNEL;
+ }
+ for (i = middle; i != col2; i++) {
+ dungeon[row2][i] = TUNNEL;
+ }
+ } else {
+ if (row1 > row2) {
+ SWAP(row1, row2);
+ SWAP(col1, col2);
+ }
+ middle = get_rand(row1+1, row2-1);
+ for (i = row1+1; i != middle; i++) {
+ dungeon[i][col1] = TUNNEL;
+ }
+ for (i = col1; i != col2; i += (col1 > col2) ? -1 : 1) {
+ dungeon[middle][i] = TUNNEL;
+ }
+ for (i = middle; i != row2; i++) {
+ dungeon[i][col2] = TUNNEL;
+ }
+ }
+ if (rand_percent(HIDE_PERCENT)) {
+ hide_boxed_passage(row1, col1, row2, col2, 1);
+ }
+}
+
+static int
+same_row(int room1, int room2)
+{
+ return((room1 / 3) == (room2 / 3));
+}
+
+static int
+same_col(int room1, int room2)
+{
+ return((room1 % 3) == (room2 % 3));
+}
+
+static void
+add_mazes(void)
+{
+ short i, j;
+ short start;
+ short maze_percent;
+
+ if (cur_level > 1) {
+ start = get_rand(0, (MAXROOMS-1));
+ maze_percent = (cur_level * 5) / 4;
+
+ if (cur_level > 15) {
+ maze_percent += cur_level;
+ }
+ for (i = 0; i < MAXROOMS; i++) {
+ j = ((start + i) % MAXROOMS);
+ if (rooms[j].is_room & R_NOTHING) {
+ if (rand_percent(maze_percent)) {
+ rooms[j].is_room = R_MAZE;
+ make_maze(get_rand(rooms[j].top_row+1, rooms[j].bottom_row-1),
+ get_rand(rooms[j].left_col+1, rooms[j].right_col-1),
+ rooms[j].top_row, rooms[j].bottom_row,
+ rooms[j].left_col, rooms[j].right_col);
+ hide_boxed_passage(rooms[j].top_row, rooms[j].left_col,
+ rooms[j].bottom_row, rooms[j].right_col,
+ get_rand(0, 2));
+ }
+ }
+ }
+ }
+}
+
+static void
+fill_out_level(void)
+{
+ short i, rn;
+
+ mix_random_rooms();
+
+ r_de = NO_ROOM;
+
+ for (i = 0; i < MAXROOMS; i++) {
+ rn = random_rooms[i];
+ if ((rooms[rn].is_room & R_NOTHING) ||
+ ((rooms[rn].is_room & R_CROSS) && coin_toss())) {
+ fill_it(rn, 1);
+ }
+ }
+ if (r_de != NO_ROOM) {
+ fill_it(r_de, 0);
+ }
+}
+
+static void
+fill_it(int rn, boolean do_rec_de)
+{
+ short i, tunnel_dir, door_dir, drow, dcol;
+ short target_room, rooms_found = 0;
+ short srow, scol, t;
+ static short offsets[4] = {-1, 1, 3, -3};
+ boolean did_this = 0;
+
+ for (i = 0; i < 10; i++) {
+ srow = get_rand(0, 3);
+ scol = get_rand(0, 3);
+ t = offsets[srow];
+ offsets[srow] = offsets[scol];
+ offsets[scol] = t;
+ }
+ for (i = 0; i < 4; i++) {
+
+ target_room = rn + offsets[i];
+
+ if (((target_room < 0) || (target_room >= MAXROOMS)) ||
+ (!(same_row(rn,target_room) || same_col(rn,target_room))) ||
+ (!(rooms[target_room].is_room & (R_ROOM | R_MAZE)))) {
+ continue;
+ }
+ if (same_row(rn, target_room)) {
+ tunnel_dir = (rooms[rn].left_col < rooms[target_room].left_col) ?
+ RIGHT : LEFT;
+ } else {
+ tunnel_dir = (rooms[rn].top_row < rooms[target_room].top_row) ?
+ DOWN : UPWARD;
+ }
+ door_dir = ((tunnel_dir + 4) % DIRS);
+ if (rooms[target_room].doors[door_dir/2].oth_room != NO_ROOM) {
+ continue;
+ }
+ if (((!do_rec_de) || did_this) ||
+ (!mask_room(rn, &srow, &scol, TUNNEL))) {
+ srow = (rooms[rn].top_row + rooms[rn].bottom_row) / 2;
+ scol = (rooms[rn].left_col + rooms[rn].right_col) / 2;
+ }
+ put_door(&rooms[target_room], door_dir, &drow, &dcol);
+ rooms_found++;
+ draw_simple_passage(srow, scol, drow, dcol, tunnel_dir);
+ rooms[rn].is_room = R_DEADEND;
+ dungeon[srow][scol] = TUNNEL;
+
+ if ((i < 3) && (!did_this)) {
+ did_this = 1;
+ if (coin_toss()) {
+ continue;
+ }
+ }
+ if ((rooms_found < 2) && do_rec_de) {
+ recursive_deadend(rn, offsets, srow, scol);
+ }
+ break;
+ }
+}
+
+static void
+recursive_deadend(short rn, const short *offsets, short srow, short scol)
+{
+ short i, de;
+ short drow, dcol, tunnel_dir;
+
+ rooms[rn].is_room = R_DEADEND;
+ dungeon[srow][scol] = TUNNEL;
+
+ for (i = 0; i < 4; i++) {
+ de = rn + offsets[i];
+ if (((de < 0) || (de >= MAXROOMS)) ||
+ (!(same_row(rn, de) || same_col(rn, de)))) {
+ continue;
+ }
+ if (!(rooms[de].is_room & R_NOTHING)) {
+ continue;
+ }
+ drow = (rooms[de].top_row + rooms[de].bottom_row) / 2;
+ dcol = (rooms[de].left_col + rooms[de].right_col) / 2;
+ if (same_row(rn, de)) {
+ tunnel_dir = (rooms[rn].left_col < rooms[de].left_col) ?
+ RIGHT : LEFT;
+ } else {
+ tunnel_dir = (rooms[rn].top_row < rooms[de].top_row) ?
+ DOWN : UPWARD;
+ }
+ draw_simple_passage(srow, scol, drow, dcol, tunnel_dir);
+ r_de = de;
+ recursive_deadend(de, offsets, drow, dcol);
+ }
+}
+
+static boolean
+mask_room(short rn, short *row, short *col, unsigned short mask)
+{
+ short i, j;
+
+ for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) {
+ for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) {
+ if (dungeon[i][j] & mask) {
+ *row = i;
+ *col = j;
+ return(1);
+ }
+ }
+ }
+ return(0);
+}
+
+static void
+make_maze(short r, short c, short tr, short br, short lc, short rc)
+{
+ char dirs[4];
+ short i, t;
+
+ dirs[0] = UPWARD;
+ dirs[1] = DOWN;
+ dirs[2] = LEFT;
+ dirs[3] = RIGHT;
+
+ dungeon[r][c] = TUNNEL;
+
+ if (rand_percent(20)) {
+ for (i = 0; i < 10; i++) {
+ short t1, t2;
+
+ t1 = get_rand(0, 3);
+ t2 = get_rand(0, 3);
+
+ SWAP(dirs[t1], dirs[t2]);
+ }
+ }
+ for (i = 0; i < 4; i++) {
+ switch(dirs[i]) {
+ case UPWARD:
+ if (((r-1) >= tr) &&
+ (dungeon[r-1][c] != TUNNEL) &&
+ (dungeon[r-1][c-1] != TUNNEL) &&
+ (dungeon[r-1][c+1] != TUNNEL) &&
+ (dungeon[r-2][c] != TUNNEL)) {
+ make_maze((r-1), c, tr, br, lc, rc);
+ }
+ break;
+ case DOWN:
+ if (((r+1) <= br) &&
+ (dungeon[r+1][c] != TUNNEL) &&
+ (dungeon[r+1][c-1] != TUNNEL) &&
+ (dungeon[r+1][c+1] != TUNNEL) &&
+ (dungeon[r+2][c] != TUNNEL)) {
+ make_maze((r+1), c, tr, br, lc, rc);
+ }
+ break;
+ case LEFT:
+ if (((c-1) >= lc) &&
+ (dungeon[r][c-1] != TUNNEL) &&
+ (dungeon[r-1][c-1] != TUNNEL) &&
+ (dungeon[r+1][c-1] != TUNNEL) &&
+ (dungeon[r][c-2] != TUNNEL)) {
+ make_maze(r, (c-1), tr, br, lc, rc);
+ }
+ break;
+ case RIGHT:
+ if (((c+1) <= rc) &&
+ (dungeon[r][c+1] != TUNNEL) &&
+ (dungeon[r-1][c+1] != TUNNEL) &&
+ (dungeon[r+1][c+1] != TUNNEL) &&
+ (dungeon[r][c+2] != TUNNEL)) {
+ make_maze(r, (c+1), tr, br, lc, rc);
+ }
+ break;
+ }
+ }
+}
+
+static void
+hide_boxed_passage(short row1, short col1, short row2, short col2, short n)
+{
+ short i, j, t;
+ short row, col, row_cut, col_cut;
+ short h, w;
+
+ if (cur_level > 2) {
+ if (row1 > row2) {
+ SWAP(row1, row2);
+ }
+ if (col1 > col2) {
+ SWAP(col1, col2);
+ }
+ h = row2 - row1;
+ w = col2 - col1;
+
+ if ((w >= 5) || (h >= 5)) {
+ row_cut = ((h >= 2) ? 1 : 0);
+ col_cut = ((w >= 2) ? 1 : 0);
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < 10; j++) {
+ row = get_rand(row1 + row_cut, row2 - row_cut);
+ col = get_rand(col1 + col_cut, col2 - col_cut);
+ if (dungeon[row][col] == TUNNEL) {
+ dungeon[row][col] |= HIDDEN;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * try not to put in room NR
+ */
+void
+put_player(short nr)
+{
+ short rn = nr, misses;
+ short row, col;
+
+ for (misses = 0; ((misses < 2) && (rn == nr)); misses++) {
+ gr_row_col(&row, &col, (FLOOR | TUNNEL | OBJECT | STAIRS));
+ rn = get_room_number(row, col);
+ }
+ rogue.row = row;
+ rogue.col = col;
+
+ if (dungeon[rogue.row][rogue.col] & TUNNEL) {
+ cur_room = PASSAGE;
+ } else {
+ cur_room = rn;
+ }
+ if (cur_room != PASSAGE) {
+ light_up_room(cur_room);
+ } else {
+ light_passage(rogue.row, rogue.col);
+ }
+ rn = get_room_number(rogue.row, rogue.col);
+ wake_room(rn, 1, rogue.row, rogue.col);
+ if (new_level_message) {
+ messagef(0, "%s", new_level_message);
+ new_level_message = NULL;
+ }
+ mvaddch(rogue.row, rogue.col, rogue.fchar);
+}
+
+int
+drop_check(void)
+{
+ if (wizard) {
+ return(1);
+ }
+ if (dungeon[rogue.row][rogue.col] & STAIRS) {
+ if (levitate) {
+ messagef(0, "you're floating in the air!");
+ return(0);
+ }
+ return(1);
+ }
+ messagef(0, "I see no way down");
+ return(0);
+}
+
+int
+check_up(void)
+{
+ if (!wizard) {
+ if (!(dungeon[rogue.row][rogue.col] & STAIRS)) {
+ messagef(0, "I see no way up");
+ return(0);
+ }
+ if (!has_amulet()) {
+ messagef(0, "your way is magically blocked");
+ return(0);
+ }
+ }
+ new_level_message = "you feel a wrenching sensation in your gut";
+ if (cur_level == 1) {
+ win();
+ } else {
+ cur_level -= 2;
+ return(1);
+ }
+ return(0);
+}
+
+void
+add_exp(int e, boolean promotion)
+{
+ short new_exp;
+ short i, hp;
+
+ rogue.exp_points += e;
+
+ if (rogue.exp_points >= level_points[rogue.exp-1]) {
+ new_exp = get_exp_level(rogue.exp_points);
+ if (rogue.exp_points > MAX_EXP) {
+ rogue.exp_points = MAX_EXP + 1;
+ }
+ for (i = rogue.exp+1; i <= new_exp; i++) {
+ messagef(0, "welcome to level %d", i);
+ if (promotion) {
+ hp = hp_raise();
+ rogue.hp_current += hp;
+ rogue.hp_max += hp;
+ }
+ rogue.exp = i;
+ print_stats(STAT_HP | STAT_EXP);
+ }
+ } else {
+ print_stats(STAT_EXP);
+ }
+}
+
+static int
+get_exp_level(long e)
+{
+ short i;
+
+ for (i = 0; i < (MAX_EXP_LEVEL - 1); i++) {
+ if (level_points[i] > e) {
+ break;
+ }
+ }
+ return(i+1);
+}
+
+int
+hp_raise(void)
+{
+ int hp;
+
+ hp = (wizard ? 10 : get_rand(3, 10));
+ return(hp);
+}
+
+void
+show_average_hp(void)
+{
+ float real_average;
+ float effective_average;
+
+ if (rogue.exp == 1) {
+ real_average = effective_average = 0.00;
+ } else {
+ real_average = (float)
+ ((rogue.hp_max - extra_hp - INIT_HP) + less_hp) / (rogue.exp - 1);
+ effective_average = (float)(rogue.hp_max - INIT_HP) / (rogue.exp - 1);
+
+ }
+ messagef(0, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average,
+ effective_average, extra_hp, less_hp);
+}
+
+static void
+mix_random_rooms(void)
+{
+ short i, t;
+ short x, y;
+
+ for (i = 0; i < (3 * MAXROOMS); i++) {
+ do {
+ x = get_rand(0, (MAXROOMS-1));
+ y = get_rand(0, (MAXROOMS-1));
+ } while (x == y);
+ SWAP(random_rooms[x], random_rooms[y]);
+ }
+}
diff --git a/rogue/machdep.c b/rogue/machdep.c
new file mode 100644
index 0000000..7f5b261
--- /dev/null
+++ b/rogue/machdep.c
@@ -0,0 +1,493 @@
+/* $NetBSD: machdep.c,v 1.20 2012/12/01 11:37:27 mbalmer Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#include <time.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)machdep.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: machdep.c,v 1.20 2012/12/01 11:37:27 mbalmer Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * machdep.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+/* Included in this file are all system dependent routines. Extensive use
+ * of #ifdef's will be used to compile the appropriate code on each system:
+ *
+ * UNIX: all UNIX systems.
+ * UNIX_BSD4_2: UNIX BSD 4.2 and later, UTEK, (4.1 BSD too?)
+ * UNIX_SYSV: UNIX system V
+ * UNIX_V7: UNIX version 7
+ *
+ * All UNIX code should be included between the single "#ifdef UNIX" at the
+ * top of this file, and the "#endif" at the bottom.
+ *
+ * To change a routine to include a new UNIX system, simply #ifdef the
+ * existing routine, as in the following example:
+ *
+ * To make a routine compatible with UNIX system 5, change the first
+ * function to the second:
+ *
+ * md_function()
+ * {
+ * code;
+ * }
+ *
+ * md_function()
+ * {
+ * #ifdef UNIX_SYSV
+ * sys5code;
+ * #else
+ * code;
+ * #endif
+ * }
+ *
+ * Appropriate variations of this are of course acceptable.
+ * The use of "#elseif" is discouraged because of non-portability.
+ * If the correct #define doesn't exist, "UNIX_SYSV" in this case, make it up
+ * and insert it in the list at the top of the file. Alter the CFLAGS
+ * in you Makefile appropriately.
+ *
+ */
+
+#ifdef UNIX
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <pwd.h>
+
+#ifdef UNIX_BSD4_2
+#include <sys/time.h>
+#endif
+
+#ifdef UNIX_SYSV
+#include <time.h>
+#endif
+
+#include <signal.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include "rogue.h"
+#include "pathnames.h"
+
+/* md_slurp:
+ *
+ * This routine throws away all keyboard input that has not
+ * yet been read. It is used to get rid of input that the user may have
+ * typed-ahead.
+ *
+ * This function is not necessary, so it may be stubbed. The might cause
+ * message-line output to flash by because the game has continued to read
+ * input without waiting for the user to read the message. Not such a
+ * big deal.
+ */
+
+void
+md_slurp(void)
+{
+ (void)fpurge(stdin);
+}
+
+/* md_heed_signals():
+ *
+ * This routine tells the program to call particular routines when
+ * certain interrupts/events occur:
+ *
+ * SIGINT: call onintr() to interrupt fight with monster or long rest.
+ * SIGQUIT: call byebye() to check for game termination.
+ * SIGHUP: call error_save() to save game when terminal hangs up.
+ *
+ * On VMS, SIGINT and SIGQUIT correspond to ^C and ^Y.
+ *
+ * This routine is not strictly necessary and can be stubbed. This will
+ * mean that the game cannot be interrupted properly with keyboard
+ * input, this is not usually critical.
+ */
+
+void
+md_heed_signals(void)
+{
+ signal(SIGINT, onintr);
+ signal(SIGQUIT, byebye);
+ signal(SIGHUP, error_save);
+}
+
+/* md_ignore_signals():
+ *
+ * This routine tells the program to completely ignore the events mentioned
+ * in md_heed_signals() above. The event handlers will later be turned on
+ * by a future call to md_heed_signals(), so md_heed_signals() and
+ * md_ignore_signals() need to work together.
+ *
+ * This function should be implemented or the user risks interrupting
+ * critical sections of code, which could cause score file, or saved-game
+ * file, corruption.
+ */
+
+void
+md_ignore_signals(void)
+{
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+}
+
+/* md_get_file_id():
+ *
+ * This function returns an integer that uniquely identifies the specified
+ * file. It need not check for the file's existence. In UNIX, the inode
+ * number is used.
+ *
+ * This function is used to identify saved-game files.
+ */
+
+int
+md_get_file_id(const char *fname)
+{
+ struct stat sbuf;
+
+ if (stat(fname, &sbuf)) {
+ return(-1);
+ }
+ return((int)sbuf.st_ino);
+}
+
+/* md_link_count():
+ *
+ * This routine returns the number of hard links to the specified file.
+ *
+ * This function is not strictly necessary. On systems without hard links
+ * this routine can be stubbed by just returning 1.
+ */
+
+int
+md_link_count(const char *fname)
+{
+ struct stat sbuf;
+
+ stat(fname, &sbuf);
+ return((int)sbuf.st_nlink);
+}
+
+/* md_gct(): (Get Current Time)
+ *
+ * This function returns the current year, month(1-12), day(1-31), hour(0-23),
+ * minute(0-59), and second(0-59). This is used for identifying the time
+ * at which a game is saved.
+ *
+ * This function is not strictly necessary. It can be stubbed by returning
+ * zeros instead of the correct year, month, etc. If your operating
+ * system doesn't provide all of the time units requested here, then you
+ * can provide only those that it does, and return zeros for the others.
+ * If you cannot provide good time values, then users may be able to copy
+ * saved-game files and play them.
+ */
+
+void
+md_gct(struct rogue_time *rt_buf)
+{
+ struct tm *t;
+ time_t seconds;
+
+ time(&seconds);
+ t = localtime(&seconds);
+
+ rt_buf->year = t->tm_year;
+ rt_buf->month = t->tm_mon + 1;
+ rt_buf->day = t->tm_mday;
+ rt_buf->hour = t->tm_hour;
+ rt_buf->minute = t->tm_min;
+ rt_buf->second = t->tm_sec;
+}
+
+/* md_gfmt: (Get File Modification Time)
+ *
+ * This routine returns a file's date of last modification in the same format
+ * as md_gct() above.
+ *
+ * This function is not strictly necessary. It is used to see if saved-game
+ * files have been modified since they were saved. If you have stubbed the
+ * routine md_gct() above by returning constant values, then you may do
+ * exactly the same here.
+ * Or if md_gct() is implemented correctly, but your system does not provide
+ * file modification dates, you may return some date far in the past so
+ * that the program will never know that a saved-game file being modified.
+ * You may also do this if you wish to be able to restore games from
+ * saved-games that have been modified.
+ */
+
+void
+md_gfmt(const char *fname, struct rogue_time *rt_buf)
+{
+ struct stat sbuf;
+ time_t seconds;
+ struct tm *t;
+
+ stat(fname, &sbuf);
+ seconds = sbuf.st_mtime;
+ t = localtime(&seconds);
+
+ rt_buf->year = t->tm_year;
+ rt_buf->month = t->tm_mon + 1;
+ rt_buf->day = t->tm_mday;
+ rt_buf->hour = t->tm_hour;
+ rt_buf->minute = t->tm_min;
+ rt_buf->second = t->tm_sec;
+}
+
+/* md_df: (Delete File)
+ *
+ * This function deletes the specified file, and returns true (1) if the
+ * operation was successful. This is used to delete saved-game files
+ * after restoring games from them.
+ *
+ * Again, this function is not strictly necessary, and can be stubbed
+ * by simply returning 1. In this case, saved-game files will not be
+ * deleted and can be replayed.
+ */
+
+boolean
+md_df(const char *fname)
+{
+ if (unlink(fname)) {
+ return(0);
+ }
+ return(1);
+}
+
+/* md_gln: (Get login name)
+ *
+ * This routine returns the login name of the user. This string is
+ * used mainly for identifying users in score files.
+ *
+ * A dummy string may be returned if you are unable to implement this
+ * function, but then the score file would only have one name in it.
+ */
+
+const char *
+md_gln(void)
+{
+ struct passwd *p;
+
+ if (!(p = getpwuid(getuid())))
+ return NULL;
+ return p->pw_name;
+}
+
+/* md_sleep:
+ *
+ * This routine causes the game to pause for the specified number of
+ * seconds.
+ *
+ * This routine is not particularly necessary at all. It is used for
+ * delaying execution, which is useful to this program at some times.
+ */
+
+void
+md_sleep(int nsecs)
+{
+ (void)sleep(nsecs);
+}
+
+/* md_getenv()
+ *
+ * This routine gets certain values from the user's environment. These
+ * values are strings, and each string is identified by a name. The names
+ * of the values needed, and their use, is as follows:
+ *
+ * ROGUEOPTS
+ * A string containing the various game options. This need not be
+ * defined.
+ * HOME
+ * The user's home directory. This is only used when the user specifies
+ * '~' as the first character of a saved-game file. This string need
+ * not be defined.
+ * SHELL
+ * The user's favorite shell. If not found, "/bin/sh" is assumed.
+ *
+ * If your system does not provide a means of searching for these values,
+ * you will have to do it yourself. None of the values above really need
+ * to be defined; you can get by with simply always returning zero.
+ * Returning zero indicates that their is no defined value for the
+ * given string.
+ */
+
+char *
+md_getenv(const char *name)
+{
+ char *value;
+
+ value = getenv(name);
+
+ return(value);
+}
+
+/* md_malloc()
+ *
+ * This routine allocates, and returns a pointer to, the specified number
+ * of bytes. This routines absolutely MUST be implemented for your
+ * particular system or the program will not run at all. Return zero
+ * when no more memory can be allocated.
+ */
+
+void *
+md_malloc(size_t n)
+{
+ char *t;
+
+ t = malloc(n);
+ return(t);
+}
+
+/* md_gseed() (Get Seed)
+ *
+ * This function returns a seed for the random number generator (RNG). This
+ * seed causes the RNG to begin generating numbers at some point in its
+ * sequence. Without a random seed, the RNG will generate the same set
+ * of numbers, and every game will start out exactly the same way. A good
+ * number to use is the process id, given by getpid() on most UNIX systems.
+ *
+ * You need to find some single random integer, such as:
+ * process id.
+ * current time (minutes + seconds) returned from md_gct(), if implemented.
+ *
+ * It will not help to return "get_rand()" or "rand()" or the return value of
+ * any pseudo-RNG. If you don't have a random number, you can just return 1,
+ * but this means your games will ALWAYS start the same way, and will play
+ * exactly the same way given the same input.
+ */
+
+int
+md_gseed(void)
+{
+ time_t seconds;
+
+ time(&seconds);
+ return((int)seconds);
+}
+
+/* md_exit():
+ *
+ * This function causes the program to discontinue execution and exit.
+ * This function must be implemented or the program will continue to
+ * hang when it should quit.
+ */
+
+void
+md_exit(int status)
+{
+ exit(status);
+}
+
+/* md_lock():
+ *
+ * This function is intended to give the user exclusive access to the score
+ * file. It does so by flock'ing the score file. The full path name of the
+ * score file should be defined for any particular site in rogue.h. The
+ * constants _PATH_SCOREFILE defines this file name.
+ *
+ * When the parameter 'l' is non-zero (true), a lock is requested. Otherwise
+ * the lock is released.
+ */
+
+void
+md_lock(boolean l)
+{
+ static int fd = -1;
+ short tries;
+
+ if (l) {
+ setegid(egid);
+ if ((fd = open(_PATH_SCOREFILE, O_RDONLY)) < 1) {
+ setegid(gid);
+ messagef(0, "cannot lock score file");
+ return;
+ }
+ setegid(gid);
+ for (tries = 0; tries < 5; tries++)
+ if (!flock(fd, LOCK_EX|LOCK_NB))
+ return;
+ } else {
+ (void)flock(fd, LOCK_UN|LOCK_NB);
+ (void)close(fd);
+ }
+}
+
+/* md_shell():
+ *
+ * This function spawns a shell for the user to use. When this shell is
+ * terminated, the game continues.
+ *
+ * It is important that the game not give the shell the privileges the
+ * game uses to access the scores file. This version of the game runs
+ * with privileges low by default; only the saved gid (if setgid) or uid
+ * (if setuid) will be privileged, but that privilege is discarded by
+ * exec().
+ */
+
+void
+md_shell(const char *shell)
+{
+ int w;
+ pid_t pid;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ break;
+ case 0:
+ execl(shell, shell, (char *)NULL);
+ _exit(255);
+ default:
+ waitpid(pid, &w, 0);
+ break;
+ }
+}
+
+#endif /* UNIX */
diff --git a/rogue/main.c b/rogue/main.c
new file mode 100644
index 0000000..fb88436
--- /dev/null
+++ b/rogue/main.c
@@ -0,0 +1,84 @@
+/* $NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1988, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * main.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+int
+main(int argc, char *argv[])
+{
+ if (init(argc, argv)) { /* restored game */
+ goto PL;
+ }
+
+ for (;;) {
+ clear_level();
+ make_level();
+ put_objects();
+ put_stairs();
+ add_traps();
+ put_mons();
+ put_player(party_room);
+ print_stats(STAT_ALL);
+PL:
+ play_level();
+ free_stuff(&level_objects);
+ free_stuff(&level_monsters);
+ }
+}
diff --git a/rogue/message.c b/rogue/message.c
new file mode 100644
index 0000000..e38377d
--- /dev/null
+++ b/rogue/message.c
@@ -0,0 +1,378 @@
+/* $NetBSD: message.c,v 1.14 2009/08/12 08:44:45 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)message.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: message.c,v 1.14 2009/08/12 08:44:45 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * message.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include <signal.h>
+#include <termios.h>
+#include <stdarg.h>
+#include "rogue.h"
+#include "pathnames.h"
+
+static char msgs[NMESSAGES][DCOLS] = {"", "", "", "", ""};
+static short msg_col = 0, imsg = -1;
+static boolean rmsg = 0;
+
+boolean msg_cleared = 1;
+char hunger_str[HUNGER_STR_LEN] = "";
+const char *more = "-more-";
+
+static void save_screen(void);
+
+static
+void
+message(const char *msg, boolean intrpt)
+{
+ cant_int = 1;
+
+ if (!save_is_interactive) {
+ return;
+ }
+ if (intrpt) {
+ interrupted = 1;
+ md_slurp();
+ }
+
+ if (!msg_cleared) {
+ mvaddstr(MIN_ROW-1, msg_col, more);
+ refresh();
+ wait_for_ack();
+ check_message();
+ }
+ if (!rmsg) {
+ imsg = (imsg + 1) % NMESSAGES;
+ (void)strlcpy(msgs[imsg], msg, sizeof(msgs[imsg]));
+ }
+ mvaddstr(MIN_ROW-1, 0, msg);
+ addch(' ');
+ refresh();
+ msg_cleared = 0;
+ msg_col = strlen(msg);
+
+ cant_int = 0;
+
+ if (did_int) {
+ did_int = 0;
+ onintr(0);
+ }
+}
+
+void
+messagef(boolean intrpt, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[DCOLS];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ message(buf, intrpt);
+}
+
+void
+remessage(short c)
+{
+ if (imsg != -1) {
+ check_message();
+ rmsg = 1;
+ while (c > imsg) {
+ c -= NMESSAGES;
+ }
+ message(msgs[((imsg - c) % NMESSAGES)], 0);
+ rmsg = 0;
+ move(rogue.row, rogue.col);
+ refresh();
+ }
+}
+
+void
+check_message(void)
+{
+ if (msg_cleared) {
+ return;
+ }
+ move(MIN_ROW-1, 0);
+ clrtoeol();
+ refresh();
+ msg_cleared = 1;
+}
+
+int
+get_input_line(const char *prompt, const char *insert,
+ char *buf, size_t buflen,
+ const char *if_cancelled,
+ boolean add_blank, boolean do_echo)
+{
+ short ch;
+ size_t i = 0, n;
+
+ message(prompt, 0);
+ n = strlen(prompt);
+
+ if (insert[0]) {
+ mvaddstr(0, n + 1, insert);
+ (void)strlcpy(buf, insert, buflen);
+ i = strlen(buf);
+ move(0, (n + i + 1));
+ refresh();
+ }
+
+ while (((ch = rgetchar()) != '\r') && (ch != '\n') && (ch != CANCEL)) {
+ if ((ch >= ' ') && (ch <= '~') && (i < buflen-2)) {
+ if ((ch != ' ') || (i > 0)) {
+ buf[i++] = ch;
+ if (do_echo) {
+ addch(ch);
+ }
+ }
+ }
+ if ((ch == '\b') && (i > 0)) {
+ if (do_echo) {
+ mvaddch(0, i + n, ' ');
+ move(MIN_ROW-1, i+n);
+ }
+ i--;
+ }
+ refresh();
+ }
+ check_message();
+ if (add_blank) {
+ buf[i++] = ' ';
+ } else {
+ while ((i > 0) && (buf[i-1] == ' ')) {
+ i--;
+ }
+ }
+
+ buf[i] = 0;
+
+ if ((ch == CANCEL) || (i == 0) || ((i == 1) && add_blank)) {
+ if (if_cancelled) {
+ message(if_cancelled, 0);
+ }
+ return(0);
+ }
+ return(i);
+}
+
+int
+rgetchar(void)
+{
+ int ch;
+
+ for(;;) {
+ ch = getchar();
+
+ switch(ch) {
+ case '\022': /* ^R */
+ wrefresh(curscr);
+ break;
+#ifdef UNIX_BSD4_2
+ case '\032': /* ^Z */
+ printf("%s", CL);
+ fflush(stdout);
+ tstp();
+ break;
+#endif
+ case '&':
+ save_screen();
+ break;
+ default:
+ return(ch);
+ }
+ }
+}
+
+/*
+Level: 99 Gold: 999999 Hp: 999(999) Str: 99(99) Arm: 99 Exp: 21/10000000 Hungry
+0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5
+*/
+
+void
+print_stats(int stat_mask)
+{
+ char buf[16];
+ boolean label;
+ int row = DROWS - 1;
+
+ label = (stat_mask & STAT_LABEL) ? 1 : 0;
+
+ if (stat_mask & STAT_LEVEL) {
+ if (label) {
+ mvaddstr(row, 0, "Level: ");
+ }
+ /* max level taken care of in make_level() */
+ mvprintw(row, 7, "%-2d", cur_level);
+ }
+ if (stat_mask & STAT_GOLD) {
+ if (label) {
+ mvaddstr(row, 10, "Gold: ");
+ }
+ if (rogue.gold > MAX_GOLD) {
+ rogue.gold = MAX_GOLD;
+ }
+ mvprintw(row, 16, "%-6ld", rogue.gold);
+ }
+ if (stat_mask & STAT_HP) {
+ if (label) {
+ mvaddstr(row, 23, "Hp: ");
+ }
+ if (rogue.hp_max > MAX_HP) {
+ rogue.hp_current -= (rogue.hp_max - MAX_HP);
+ rogue.hp_max = MAX_HP;
+ }
+ snprintf(buf, sizeof(buf), "%d(%d)",
+ rogue.hp_current, rogue.hp_max);
+ mvprintw(row, 27, "%-8s", buf);
+ }
+ if (stat_mask & STAT_STRENGTH) {
+ if (label) {
+ mvaddstr(row, 36, "Str: ");
+ }
+ if (rogue.str_max > MAX_STRENGTH) {
+ rogue.str_current -= (rogue.str_max - MAX_STRENGTH);
+ rogue.str_max = MAX_STRENGTH;
+ }
+ snprintf(buf, sizeof(buf), "%d(%d)",
+ (rogue.str_current + add_strength), rogue.str_max);
+ mvprintw(row, 41, "%-6s", buf);
+ }
+ if (stat_mask & STAT_ARMOR) {
+ if (label) {
+ mvaddstr(row, 48, "Arm: ");
+ }
+ if (rogue.armor && (rogue.armor->d_enchant > MAX_ARMOR)) {
+ rogue.armor->d_enchant = MAX_ARMOR;
+ }
+ mvprintw(row, 53, "%-2d", get_armor_class(rogue.armor));
+ }
+ if (stat_mask & STAT_EXP) {
+ if (label) {
+ mvaddstr(row, 56, "Exp: ");
+ }
+ if (rogue.exp_points > MAX_EXP) {
+ rogue.exp_points = MAX_EXP;
+ }
+ if (rogue.exp > MAX_EXP_LEVEL) {
+ rogue.exp = MAX_EXP_LEVEL;
+ }
+ snprintf(buf, sizeof(buf), "%d/%ld",
+ rogue.exp, rogue.exp_points);
+ mvprintw(row, 61, "%-11s", buf);
+ }
+ if (stat_mask & STAT_HUNGER) {
+ mvaddstr(row, 73, hunger_str);
+ clrtoeol();
+ }
+ refresh();
+}
+
+static void
+save_screen(void)
+{
+ FILE *fp;
+ short i, j;
+ char buf[DCOLS+2];
+
+ if ((fp = fopen(_PATH_SCREENDUMP, "w")) != NULL) {
+ for (i = 0; i < DROWS; i++) {
+ for (j=0; j<DCOLS; j++) {
+ buf[j] = mvinch(i, j);
+ }
+ /*buf[DCOLS] = 0; -- redundant */
+ for (j=DCOLS; j>0 && buf[j-1]==' '; j--);
+ buf[j] = 0;
+
+ fputs(buf, fp);
+ putc('\n', fp);
+ }
+ fclose(fp);
+ } else {
+ sound_bell();
+ }
+}
+
+void
+sound_bell(void)
+{
+ putchar(7);
+ fflush(stdout);
+}
+
+boolean
+is_digit(int ch)
+{
+ return((ch >= '0') && (ch <= '9'));
+}
+
+int
+r_index(const char *str, int ch, boolean last)
+{
+ int i = 0;
+
+ if (last) {
+ for (i = strlen(str) - 1; i >= 0; i--) {
+ if (str[i] == ch) {
+ return(i);
+ }
+ }
+ } else {
+ for (i = 0; str[i]; i++) {
+ if (str[i] == ch) {
+ return(i);
+ }
+ }
+ }
+ return(-1);
+}
diff --git a/rogue/monster.c b/rogue/monster.c
new file mode 100644
index 0000000..1d644f6
--- /dev/null
+++ b/rogue/monster.c
@@ -0,0 +1,886 @@
+/* $NetBSD: monster.c,v 1.17 2013/08/11 03:44:27 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)monster.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: monster.c,v 1.17 2013/08/11 03:44:27 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * monster.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+object level_monsters;
+boolean mon_disappeared;
+
+const char *const m_names[] = {
+ "aquator",
+ "bat",
+ "centaur",
+ "dragon",
+ "emu",
+ "venus fly-trap",
+ "griffin",
+ "hobgoblin",
+ "ice monster",
+ "jabberwock",
+ "kestrel",
+ "leprechaun",
+ "medusa",
+ "nymph",
+ "orc",
+ "phantom",
+ "quagga",
+ "rattlesnake",
+ "snake",
+ "troll",
+ "black unicorn",
+ "vampire",
+ "wraith",
+ "xeroc",
+ "yeti",
+ "zombie"
+};
+
+#define FILL 0,0,0,0,0,0,0,0,0,0,0,0,0,NULL
+
+static object mon_tab[MONSTERS] = {
+ {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0, FILL},
+ {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0, FILL},
+ {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10, FILL},
+ {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90, FILL},
+ {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0, FILL},
+ {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0, FILL},
+ {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G',
+ 2000,20,126,85,0,10, FILL},
+ {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0, FILL},
+ {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0, FILL},
+ {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0, FILL},
+ {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0, FILL},
+ {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0, FILL},
+ {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M',
+ 250,18,126,85,0,25, FILL},
+ {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100, FILL},
+ {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10, FILL},
+ {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50, FILL},
+ {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20, FILL},
+ {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0, FILL},
+ {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0, FILL},
+ {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33, FILL},
+ {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U',
+ 200,17,26,85,0,33, FILL},
+ {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V',
+ 350,19,126,85,0,18, FILL},
+ {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0, FILL},
+ {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0, FILL},
+ {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20, FILL},
+ {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0, FILL}
+};
+
+static void aim_monster(object *);
+static int flit(object *);
+static int move_confused(object *);
+static int mtry(object *, short, short);
+static int no_room_for_monster(int);
+static void put_m_at(short, short, object *);
+static int rogue_is_around(int, int);
+
+void
+put_mons(void)
+{
+ short i;
+ short n;
+ object *monster;
+ short row, col;
+
+ n = get_rand(4, 6);
+
+ for (i = 0; i < n; i++) {
+ monster = gr_monster(NULL, 0);
+ if ((monster->m_flags & WANDERS) && coin_toss()) {
+ wake_up(monster);
+ }
+ gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
+ put_m_at(row, col, monster);
+ }
+}
+
+object *
+gr_monster(object *monster, int mn)
+{
+ if (!monster) {
+ monster = alloc_object();
+
+ for (;;) {
+ mn = get_rand(0, MONSTERS-1);
+ if ((cur_level >= mon_tab[mn].first_level) &&
+ (cur_level <= mon_tab[mn].last_level)) {
+ break;
+ }
+ }
+ }
+ *monster = mon_tab[mn];
+ if (monster->m_flags & IMITATES) {
+ monster->disguise = gr_obj_char();
+ }
+ if (cur_level > (AMULET_LEVEL + 2)) {
+ monster->m_flags |= HASTED;
+ }
+ monster->trow = NO_ROOM;
+ return(monster);
+}
+
+void
+mv_mons(void)
+{
+ object *monster, *next_monster, *test_mons;
+ boolean flew;
+
+ if (haste_self % 2) {
+ return;
+ }
+
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ next_monster = monster->next_monster;
+ mon_disappeared = 0;
+ if (monster->m_flags & HASTED) {
+ mv_1_monster(monster, rogue.row, rogue.col);
+ if (mon_disappeared) {
+ goto NM;
+ }
+ } else if (monster->m_flags & SLOWED) {
+ monster->slowed_toggle = !monster->slowed_toggle;
+ if (monster->slowed_toggle) {
+ goto NM;
+ }
+ }
+ if ((monster->m_flags & CONFUSED) && move_confused(monster)) {
+ goto NM;
+ }
+ flew = 0;
+ if ( (monster->m_flags & FLIES) &&
+ !(monster->m_flags & NAPPING) &&
+ !mon_can_go(monster, rogue.row, rogue.col)) {
+ flew = 1;
+ mv_1_monster(monster, rogue.row, rogue.col);
+ if (mon_disappeared) {
+ goto NM;
+ }
+ }
+ if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) {
+ mv_1_monster(monster, rogue.row, rogue.col);
+ }
+NM: test_mons = level_monsters.next_monster;
+ monster = NULL;
+ while (test_mons)
+ {
+ if (next_monster == test_mons)
+ {
+ monster = next_monster;
+ break;
+ }
+ test_mons = test_mons -> next_monster;
+ }
+ }
+}
+
+void
+party_monsters(int rn, int n)
+{
+ short i, j;
+ short row, col;
+ object *monster;
+ boolean found;
+
+ row = col = 0;
+ n += n;
+
+ for (i = 0; i < MONSTERS; i++) {
+ mon_tab[i].first_level -= (cur_level % 3);
+ }
+ for (i = 0; i < n; i++) {
+ if (no_room_for_monster(rn)) {
+ break;
+ }
+ for (j = found = 0; ((!found) && (j < 250)); j++) {
+ row = get_rand(rooms[rn].top_row+1,
+ rooms[rn].bottom_row-1);
+ col = get_rand(rooms[rn].left_col+1,
+ rooms[rn].right_col-1);
+ if ((!(dungeon[row][col] & MONSTER)) &&
+ (dungeon[row][col] & (FLOOR | TUNNEL))) {
+ found = 1;
+ }
+ }
+ if (found) {
+ monster = gr_monster((object *)0, 0);
+ if (!(monster->m_flags & IMITATES)) {
+ monster->m_flags |= WAKENS;
+ }
+ put_m_at(row, col, monster);
+ }
+ }
+ for (i = 0; i < MONSTERS; i++) {
+ mon_tab[i].first_level += (cur_level % 3);
+ }
+}
+
+char
+gmc_row_col(int row, int col)
+{
+ object *monster;
+
+ if ((monster = object_at(&level_monsters, row, col)) != NULL) {
+ if ((!(detect_monster || see_invisible || r_see_invisible) &&
+ (monster->m_flags & INVISIBLE)) || blind) {
+ return(monster->trail_char);
+ }
+ if (monster->m_flags & IMITATES) {
+ return(monster->disguise);
+ }
+ return(monster->m_char);
+ } else {
+ return('&'); /* BUG if this ever happens */
+ }
+}
+
+char
+gmc(object *monster)
+{
+ if ((!(detect_monster || see_invisible || r_see_invisible) &&
+ (monster->m_flags & INVISIBLE))
+ || blind) {
+ return(monster->trail_char);
+ }
+ if (monster->m_flags & IMITATES) {
+ return(monster->disguise);
+ }
+ return(monster->m_char);
+}
+
+void
+mv_1_monster(object *monster, short row, short col)
+{
+ short i, n;
+ boolean tried[6];
+
+ if (monster->m_flags & ASLEEP) {
+ if (monster->m_flags & NAPPING) {
+ if (--monster->nap_length <= 0) {
+ monster->m_flags &= (~(NAPPING | ASLEEP));
+ }
+ return;
+ }
+ if ((monster->m_flags & WAKENS) &&
+ rogue_is_around(monster->row, monster->col) &&
+ rand_percent(((stealthy > 0) ?
+ (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) :
+ WAKE_PERCENT))) {
+ wake_up(monster);
+ }
+ return;
+ } else if (monster->m_flags & ALREADY_MOVED) {
+ monster->m_flags &= (~ALREADY_MOVED);
+ return;
+ }
+ if ((monster->m_flags & FLITS) && flit(monster)) {
+ return;
+ }
+ if ((monster->m_flags & STATIONARY) &&
+ (!mon_can_go(monster, rogue.row, rogue.col))) {
+ return;
+ }
+ if (monster->m_flags & FREEZING_ROGUE) {
+ return;
+ }
+ if ((monster->m_flags & CONFUSES) && m_confuse(monster)) {
+ return;
+ }
+ if (mon_can_go(monster, rogue.row, rogue.col)) {
+ mon_hit(monster);
+ return;
+ }
+ if ((monster->m_flags & FLAMES) && flame_broil(monster)) {
+ return;
+ }
+ if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) {
+ return;
+ }
+ if ((monster->trow == monster->row) &&
+ (monster->tcol == monster->col)) {
+ monster->trow = NO_ROOM;
+ } else if (monster->trow != NO_ROOM) {
+ row = monster->trow;
+ col = monster->tcol;
+ }
+ if (monster->row > row) {
+ row = monster->row - 1;
+ } else if (monster->row < row) {
+ row = monster->row + 1;
+ }
+ if ((dungeon[row][monster->col] & DOOR) &&
+ mtry(monster, row, monster->col)) {
+ return;
+ }
+ if (monster->col > col) {
+ col = monster->col - 1;
+ } else if (monster->col < col) {
+ col = monster->col + 1;
+ }
+ if ((dungeon[monster->row][col] & DOOR) &&
+ mtry(monster, monster->row, col)) {
+ return;
+ }
+ if (mtry(monster, row, col)) {
+ return;
+ }
+
+ for (i = 0; i <= 5; i++) tried[i] = 0;
+
+ for (i = 0; i < 6; i++) {
+NEXT_TRY: n = get_rand(0, 5);
+ switch(n) {
+ case 0:
+ if (!tried[n] && mtry(monster, row, monster->col-1)) {
+ goto O;
+ }
+ break;
+ case 1:
+ if (!tried[n] && mtry(monster, row, monster->col)) {
+ goto O;
+ }
+ break;
+ case 2:
+ if (!tried[n] && mtry(monster, row, monster->col+1)) {
+ goto O;
+ }
+ break;
+ case 3:
+ if (!tried[n] && mtry(monster, monster->row-1, col)) {
+ goto O;
+ }
+ break;
+ case 4:
+ if (!tried[n] && mtry(monster, monster->row, col)) {
+ goto O;
+ }
+ break;
+ case 5:
+ if (!tried[n] && mtry(monster, monster->row+1, col)) {
+ goto O;
+ }
+ break;
+ }
+ if (!tried[n]) {
+ tried[n] = 1;
+ } else {
+ goto NEXT_TRY;
+ }
+ }
+O:
+ if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) {
+ if (++(monster->o) > 4) {
+ if ((monster->trow == NO_ROOM) &&
+ (!mon_sees(monster, rogue.row, rogue.col))) {
+ monster->trow = get_rand(1, (DROWS - 2));
+ monster->tcol = get_rand(0, (DCOLS - 1));
+ } else {
+ monster->trow = NO_ROOM;
+ monster->o = 0;
+ }
+ }
+ } else {
+ monster->o_row = monster->row;
+ monster->o_col = monster->col;
+ monster->o = 0;
+ }
+}
+
+static int
+mtry(object *monster, short row, short col)
+{
+ if (mon_can_go(monster, row, col)) {
+ move_mon_to(monster, row, col);
+ return(1);
+ }
+ return(0);
+}
+
+void
+move_mon_to(object *monster, short row, short col)
+{
+ short c;
+ int mrow, mcol;
+
+ mrow = monster->row;
+ mcol = monster->col;
+
+ dungeon[mrow][mcol] &= ~MONSTER;
+ dungeon[row][col] |= MONSTER;
+
+ c = mvinch(mrow, mcol);
+
+ if ((c >= 'A') && (c <= 'Z')) {
+ if (!detect_monster) {
+ mvaddch(mrow, mcol, monster->trail_char);
+ } else {
+ if (rogue_can_see(mrow, mcol)) {
+ mvaddch(mrow, mcol, monster->trail_char);
+ } else {
+ if (monster->trail_char == '.') {
+ monster->trail_char = ' ';
+ }
+ mvaddch(mrow, mcol, monster->trail_char);
+ }
+ }
+ }
+ monster->trail_char = mvinch(row, col);
+ if (!blind && (detect_monster || rogue_can_see(row, col))) {
+ if ((!(monster->m_flags & INVISIBLE) ||
+ (detect_monster || see_invisible || r_see_invisible))) {
+ mvaddch(row, col, gmc(monster));
+ }
+ }
+ if ((dungeon[row][col] & DOOR) &&
+ (get_room_number(row, col) != cur_room) &&
+ (dungeon[mrow][mcol] == FLOOR) && !blind) {
+ mvaddch(mrow, mcol, ' ');
+ }
+ if (dungeon[row][col] & DOOR) {
+ dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0),
+ row, col);
+ } else {
+ monster->row = row;
+ monster->col = col;
+ }
+}
+
+int
+mon_can_go(const object *monster, short row, short col)
+{
+ object *obj;
+ short dr, dc;
+
+ dr = monster->row - row; /* check if move distance > 1 */
+ if ((dr >= 2) || (dr <= -2)) {
+ return(0);
+ }
+ dc = monster->col - col;
+ if ((dc >= 2) || (dc <= -2)) {
+ return(0);
+ }
+ if ((!dungeon[monster->row][col]) || (!dungeon[row][monster->col])) {
+ return(0);
+ }
+ if ((!is_passable(row, col)) || (dungeon[row][col] & MONSTER)) {
+ return(0);
+ }
+ if ((monster->row!=row)&&(monster->col!=col)&&((dungeon[row][col]&DOOR) ||
+ (dungeon[monster->row][monster->col]&DOOR))) {
+ return(0);
+ }
+ if (!(monster->m_flags & (FLITS | CONFUSED | CAN_FLIT)) &&
+ (monster->trow == NO_ROOM)) {
+ if ((monster->row < rogue.row) && (row < monster->row)) return(0);
+ if ((monster->row > rogue.row) && (row > monster->row)) return(0);
+ if ((monster->col < rogue.col) && (col < monster->col)) return(0);
+ if ((monster->col > rogue.col) && (col > monster->col)) return(0);
+ }
+ if (dungeon[row][col] & OBJECT) {
+ obj = object_at(&level_objects, row, col);
+ if ((obj->what_is == SCROL) && (obj->which_kind == SCARE_MONSTER)) {
+ return(0);
+ }
+ }
+ return(1);
+}
+
+void
+wake_up(object *monster)
+{
+ if (!(monster->m_flags & NAPPING)) {
+ monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS));
+ }
+}
+
+void
+wake_room(short rn, boolean entering, short row, short col)
+{
+ object *monster;
+ short wake_percent;
+ boolean in_room;
+
+ wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT;
+ if (stealthy > 0) {
+ wake_percent /= (STEALTH_FACTOR + stealthy);
+ }
+
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ in_room = (rn == get_room_number(monster->row, monster->col));
+ if (in_room) {
+ if (entering) {
+ monster->trow = NO_ROOM;
+ } else {
+ monster->trow = row;
+ monster->tcol = col;
+ }
+ }
+ if ((monster->m_flags & WAKENS) &&
+ (rn == get_room_number(monster->row, monster->col))) {
+ if (rand_percent(wake_percent)) {
+ wake_up(monster);
+ }
+ }
+ monster = monster->next_monster;
+ }
+}
+
+const char *
+mon_name(const object *monster)
+{
+ short ch;
+
+ if (blind || ((monster->m_flags & INVISIBLE) &&
+ !(detect_monster || see_invisible || r_see_invisible))) {
+ return("something");
+ }
+ if (halluc) {
+ ch = get_rand('A', 'Z') - 'A';
+ return(m_names[ch]);
+ }
+ ch = monster->m_char - 'A';
+ return(m_names[ch]);
+}
+
+static int
+rogue_is_around(int row, int col)
+{
+ short rdif, cdif, retval;
+
+ rdif = row - rogue.row;
+ cdif = col - rogue.col;
+
+ retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
+ return(retval);
+}
+
+void
+wanderer(void)
+{
+ object *monster;
+ short row, col, i;
+ boolean found = 0;
+
+ monster = NULL; /* XXXGCC -Wuninitialized [powerpc] */
+
+ for (i = 0; ((i < 15) && (!found)); i++) {
+ monster = gr_monster(NULL, 0);
+ if (!(monster->m_flags & (WAKENS | WANDERS))) {
+ free_object(monster);
+ } else {
+ found = 1;
+ }
+ }
+ if (found) {
+ found = 0;
+ wake_up(monster);
+ for (i = 0; ((i < 25) && (!found)); i++) {
+ gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
+ if (!rogue_can_see(row, col)) {
+ put_m_at(row, col, monster);
+ found = 1;
+ }
+ }
+ if (!found) {
+ free_object(monster);
+ }
+ }
+}
+
+void
+show_monsters(void)
+{
+ object *monster;
+
+ detect_monster = 1;
+
+ if (blind) {
+ return;
+ }
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ mvaddch(monster->row, monster->col, monster->m_char);
+ if (monster->m_flags & IMITATES) {
+ monster->m_flags &= (~IMITATES);
+ monster->m_flags |= WAKENS;
+ }
+ monster = monster->next_monster;
+ }
+}
+
+void
+create_monster(void)
+{
+ short row, col;
+ short i;
+ boolean found = 0;
+ object *monster;
+
+ row = rogue.row;
+ col = rogue.col;
+
+ for (i = 0; i < 9; i++) {
+ rand_around(i, &row, &col);
+ if (((row == rogue.row) && (col == rogue.col)) ||
+ (row < MIN_ROW) || (row > (DROWS-2)) ||
+ (col < 0) || (col > (DCOLS-1))) {
+ continue;
+ }
+ if ((!(dungeon[row][col] & MONSTER)) &&
+ (dungeon[row][col] & (FLOOR|TUNNEL|STAIRS|DOOR))) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ monster = gr_monster((object *)0, 0);
+ put_m_at(row, col, monster);
+ mvaddch(row, col, gmc(monster));
+ if (monster->m_flags & (WANDERS | WAKENS)) {
+ wake_up(monster);
+ }
+ } else {
+ messagef(0, "you hear a faint cry of anguish in the distance");
+ }
+}
+
+static void
+put_m_at(short row, short col, object *monster)
+{
+ monster->row = row;
+ monster->col = col;
+ dungeon[row][col] |= MONSTER;
+ monster->trail_char = mvinch(row, col);
+ (void)add_to_pack(monster, &level_monsters, 0);
+ aim_monster(monster);
+}
+
+static void
+aim_monster(object *monster)
+{
+ short i, rn, d, r;
+
+ rn = get_room_number(monster->row, monster->col);
+ if (rn == NO_ROOM)
+ clean_up("aim_monster: monster not in room");
+ r = get_rand(0, 12);
+
+ for (i = 0; i < 4; i++) {
+ d = (r + i) % 4;
+ if (rooms[rn].doors[d].oth_room != NO_ROOM) {
+ monster->trow = rooms[rn].doors[d].door_row;
+ monster->tcol = rooms[rn].doors[d].door_col;
+ break;
+ }
+ }
+}
+
+int
+rogue_can_see(int row, int col)
+{
+ int retval;
+
+ retval = !blind &&
+ (((get_room_number(row, col) == cur_room) &&
+ !(rooms[cur_room].is_room & R_MAZE)) ||
+ rogue_is_around(row, col));
+
+ return(retval);
+}
+
+static int
+move_confused(object *monster)
+{
+ short i, row, col;
+
+ if (!(monster->m_flags & ASLEEP)) {
+ if (--monster->moves_confused <= 0) {
+ monster->m_flags &= (~CONFUSED);
+ }
+ if (monster->m_flags & STATIONARY) {
+ return(coin_toss() ? 1 : 0);
+ } else if (rand_percent(15)) {
+ return(1);
+ }
+ row = monster->row;
+ col = monster->col;
+
+ for (i = 0; i < 9; i++) {
+ rand_around(i, &row, &col);
+ if ((row == rogue.row) && (col == rogue.col)) {
+ return(0);
+ }
+ if (mtry(monster, row, col)) {
+ return(1);
+ }
+ }
+ }
+ return(0);
+}
+
+static int
+flit(object *monster)
+{
+ short i, row, col;
+
+ if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) {
+ return(0);
+ }
+ if (rand_percent(10)) {
+ return(1);
+ }
+ row = monster->row;
+ col = monster->col;
+
+ for (i = 0; i < 9; i++) {
+ rand_around(i, &row, &col);
+ if ((row == rogue.row) && (col == rogue.col)) {
+ continue;
+ }
+ if (mtry(monster, row, col)) {
+ return(1);
+ }
+ }
+ return(1);
+}
+
+char
+gr_obj_char(void)
+{
+ short r;
+ const char *rs = "%!?]=/):*";
+
+ r = get_rand(0, 8);
+
+ return(rs[r]);
+}
+
+static int
+no_room_for_monster(int rn)
+{
+ short i, j;
+
+ for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) {
+ for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) {
+ if (!(dungeon[i][j] & MONSTER)) {
+ return(0);
+ }
+ }
+ }
+ return(1);
+}
+
+void
+aggravate(void)
+{
+ object *monster;
+
+ messagef(0, "you hear a high pitched humming noise");
+
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ wake_up(monster);
+ monster->m_flags &= (~IMITATES);
+ if (rogue_can_see(monster->row, monster->col)) {
+ mvaddch(monster->row, monster->col, monster->m_char);
+ }
+ monster = monster->next_monster;
+ }
+}
+
+boolean
+mon_sees(const object *monster, int row, int col)
+{
+ short rn, rdif, cdif, retval;
+
+ rn = get_room_number(row, col);
+
+ if ( (rn != NO_ROOM) &&
+ (rn == get_room_number(monster->row, monster->col)) &&
+ !(rooms[rn].is_room & R_MAZE)) {
+ return(1);
+ }
+ rdif = row - monster->row;
+ cdif = col - monster->col;
+
+ retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1);
+ return(retval);
+}
+
+void
+mv_aquatars(void)
+{
+ object *monster;
+
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ if ((monster->m_char == 'A') &&
+ mon_can_go(monster, rogue.row, rogue.col)) {
+ mv_1_monster(monster, rogue.row, rogue.col);
+ monster->m_flags |= ALREADY_MOVED;
+ }
+ monster = monster->next_monster;
+ }
+}
diff --git a/rogue/move.c b/rogue/move.c
new file mode 100644
index 0000000..8378654
--- /dev/null
+++ b/rogue/move.c
@@ -0,0 +1,649 @@
+/* $NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * move.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+short m_moves = 0;
+boolean jump = 0;
+const char you_can_move_again[] = "you can move again";
+
+static boolean can_turn(short, short);
+static boolean check_hunger(boolean);
+static char gr_dir(void);
+static void heal(void);
+static boolean next_to_something(int, int);
+static void turn_passage(short, boolean);
+
+int
+one_move_rogue(short dirch, short pickup)
+{
+ short row, col;
+ object *obj;
+ char desc[DCOLS];
+ short status, d = 0; /* XXX: GCC */
+
+ row = rogue.row;
+ col = rogue.col;
+
+ if (confused) {
+ dirch = gr_dir();
+ }
+ (void)is_direction(dirch, &d);
+ get_dir_rc(d, &row, &col, 1);
+
+ if (!can_move(rogue.row, rogue.col, row, col)) {
+ return(MOVE_FAILED);
+ }
+ if (being_held || bear_trap) {
+ if (!(dungeon[row][col] & MONSTER)) {
+ if (being_held) {
+ messagef(1, "you are being held");
+ } else {
+ messagef(0, "you are still stuck in the bear trap");
+ (void)reg_move();
+ }
+ return(MOVE_FAILED);
+ }
+ }
+ if (r_teleport) {
+ if (rand_percent(R_TELE_PERCENT)) {
+ tele();
+ return(STOPPED_ON_SOMETHING);
+ }
+ }
+ if (dungeon[row][col] & MONSTER) {
+ rogue_hit(object_at(&level_monsters, row, col), 0);
+ (void)reg_move();
+ return(MOVE_FAILED);
+ }
+ if (dungeon[row][col] & DOOR) {
+ if (cur_room == PASSAGE) {
+ cur_room = get_room_number(row, col);
+ if (cur_room == NO_ROOM)
+ clean_up("one_move_rogue: door to nowhere");
+ light_up_room(cur_room);
+ wake_room(cur_room, 1, row, col);
+ } else {
+ light_passage(row, col);
+ }
+ } else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
+ (dungeon[row][col] & TUNNEL)) {
+ light_passage(row, col);
+ wake_room(cur_room, 0, rogue.row, rogue.col);
+ darken_room(cur_room);
+ cur_room = PASSAGE;
+ } else if (dungeon[row][col] & TUNNEL) {
+ light_passage(row, col);
+ }
+ mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
+ mvaddch(row, col, rogue.fchar);
+
+ if (!jump) {
+ refresh();
+ }
+ rogue.row = row;
+ rogue.col = col;
+ if (dungeon[row][col] & OBJECT) {
+ if (levitate && pickup) {
+ return(STOPPED_ON_SOMETHING);
+ }
+ if (pickup && !levitate) {
+ if ((obj = pick_up(row, col, &status)) != NULL) {
+ get_desc(obj, desc, sizeof(desc));
+ if (obj->what_is == GOLD) {
+ free_object(obj);
+ messagef(1, "%s", desc);
+ goto NOT_IN_PACK;
+ }
+ } else if (!status) {
+ goto MVED;
+ } else {
+ goto MOVE_ON;
+ }
+ } else {
+MOVE_ON:
+ obj = object_at(&level_objects, row, col);
+ get_desc(obj, desc, sizeof(desc));
+ messagef(1, "moved onto %s", desc);
+ goto NOT_IN_PACK;
+ }
+ messagef(1, "%s(%c)", desc, obj->ichar);
+NOT_IN_PACK:
+ (void)reg_move();
+ return(STOPPED_ON_SOMETHING);
+ }
+ if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
+ if ((!levitate) && (dungeon[row][col] & TRAP)) {
+ trap_player(row, col);
+ }
+ (void)reg_move();
+ return(STOPPED_ON_SOMETHING);
+ }
+MVED: if (reg_move()) { /* fainted from hunger */
+ return(STOPPED_ON_SOMETHING);
+ }
+ return((confused ? STOPPED_ON_SOMETHING : MOVED));
+}
+
+void
+multiple_move_rogue(short dirch)
+{
+ short row, col;
+ short m;
+
+ switch(dirch) {
+ case '\010':
+ case '\012':
+ case '\013':
+ case '\014':
+ case '\031':
+ case '\025':
+ case '\016':
+ case '\002':
+ do {
+ row = rogue.row;
+ col = rogue.col;
+ if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
+ (m == STOPPED_ON_SOMETHING) ||
+ interrupted) {
+ break;
+ }
+ } while (!next_to_something(row, col));
+ if ( (!interrupted) && passgo && (m == MOVE_FAILED) &&
+ (dungeon[rogue.row][rogue.col] & TUNNEL)) {
+ turn_passage(dirch + 96, 0);
+ }
+ break;
+ case 'H':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'B':
+ case 'Y':
+ case 'U':
+ case 'N':
+ while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED))
+ ;
+
+ if ( (!interrupted) && passgo &&
+ (dungeon[rogue.row][rogue.col] & TUNNEL)) {
+ turn_passage(dirch + 32, 1);
+ }
+ break;
+ }
+}
+
+boolean
+is_passable(int row, int col)
+{
+ if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
+ (col > (DCOLS-1))) {
+ return(0);
+ }
+ if (dungeon[row][col] & HIDDEN) {
+ return((dungeon[row][col] & TRAP) ? 1 : 0);
+ }
+ return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
+}
+
+static boolean
+next_to_something(int drow, int dcol)
+{
+ short i, j, i_end, j_end, row, col;
+ short pass_count = 0;
+ unsigned short s;
+
+ if (confused) {
+ return(1);
+ }
+ if (blind) {
+ return(0);
+ }
+ i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
+ j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
+
+ for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
+ for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
+ if ((i == 0) && (j == 0)) {
+ continue;
+ }
+ if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
+ continue;
+ }
+ row = rogue.row + i;
+ col = rogue.col + j;
+ s = dungeon[row][col];
+ if (s & HIDDEN) {
+ continue;
+ }
+ /* If the rogue used to be right, up, left, down, or right of
+ * row,col, and now isn't, then don't stop */
+ if (s & (MONSTER | OBJECT | STAIRS)) {
+ if (((row == drow) || (col == dcol)) &&
+ (!((row == rogue.row) || (col == rogue.col)))) {
+ continue;
+ }
+ return(1);
+ }
+ if (s & TRAP) {
+ if (!(s & HIDDEN)) {
+ if (((row == drow) || (col == dcol)) &&
+ (!((row == rogue.row) || (col == rogue.col)))) {
+ continue;
+ }
+ return(1);
+ }
+ }
+ if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
+ if (++pass_count > 1) {
+ return(1);
+ }
+ }
+ if ((s & DOOR) && ((i == 0) || (j == 0))) {
+ return(1);
+ }
+ }
+ }
+ return(0);
+}
+
+boolean
+can_move(int row1, int col1, int row2, int col2)
+{
+ if (!is_passable(row2, col2)) {
+ return(0);
+ }
+ if ((row1 != row2) && (col1 != col2)) {
+ if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
+ return(0);
+ }
+ if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
+ return(0);
+ }
+ }
+ return(1);
+}
+
+void
+move_onto(void)
+{
+ short ch, d;
+ boolean first_miss = 1;
+
+ while (!is_direction(ch = rgetchar(), &d)) {
+ sound_bell();
+ if (first_miss) {
+ messagef(0, "direction? ");
+ first_miss = 0;
+ }
+ }
+ check_message();
+ if (ch != CANCEL) {
+ (void)one_move_rogue(ch, 0);
+ }
+}
+
+boolean
+is_direction(short c, short *d)
+{
+ switch(c) {
+ case 'h':
+ *d = LEFT;
+ break;
+ case 'j':
+ *d = DOWN;
+ break;
+ case 'k':
+ *d = UPWARD;
+ break;
+ case 'l':
+ *d = RIGHT;
+ break;
+ case 'b':
+ *d = DOWNLEFT;
+ break;
+ case 'y':
+ *d = UPLEFT;
+ break;
+ case 'u':
+ *d = UPRIGHT;
+ break;
+ case 'n':
+ *d = DOWNRIGHT;
+ break;
+ case CANCEL:
+ break;
+ default:
+ return(0);
+ }
+ return(1);
+}
+
+static boolean
+check_hunger(boolean msg_only)
+{
+ short i, n;
+ boolean fainted = 0;
+
+ if (rogue.moves_left == HUNGRY) {
+ (void)strlcpy(hunger_str, "hungry", sizeof(hunger_str));
+ messagef(0, "%s", hunger_str);
+ print_stats(STAT_HUNGER);
+ }
+ if (rogue.moves_left == WEAK) {
+ (void)strlcpy(hunger_str, "weak", sizeof(hunger_str));
+ messagef(1, "%s", hunger_str);
+ print_stats(STAT_HUNGER);
+ }
+ if (rogue.moves_left <= FAINT) {
+ if (rogue.moves_left == FAINT) {
+ (void)strlcpy(hunger_str, "faint", sizeof(hunger_str));
+ messagef(1, "%s", hunger_str);
+ print_stats(STAT_HUNGER);
+ }
+ n = get_rand(0, (FAINT - rogue.moves_left));
+ if (n > 0) {
+ fainted = 1;
+ if (rand_percent(40)) {
+ rogue.moves_left++;
+ }
+ messagef(1, "you faint");
+ for (i = 0; i < n; i++) {
+ if (coin_toss()) {
+ mv_mons();
+ }
+ }
+ messagef(1, "%s", you_can_move_again);
+ }
+ }
+ if (msg_only) {
+ return(fainted);
+ }
+ if (rogue.moves_left <= STARVE) {
+ killed_by(NULL, STARVATION);
+ }
+
+ switch(e_rings) {
+ /*case -2:
+ Subtract 0, i.e. do nothing.
+ break;*/
+ case -1:
+ rogue.moves_left -= (rogue.moves_left % 2);
+ break;
+ case 0:
+ rogue.moves_left--;
+ break;
+ case 1:
+ rogue.moves_left--;
+ (void)check_hunger(1);
+ rogue.moves_left -= (rogue.moves_left % 2);
+ break;
+ case 2:
+ rogue.moves_left--;
+ (void)check_hunger(1);
+ rogue.moves_left--;
+ break;
+ }
+ return(fainted);
+}
+
+boolean
+reg_move(void)
+{
+ boolean fainted;
+
+ if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
+ fainted = check_hunger(0);
+ } else {
+ fainted = 0;
+ }
+
+ mv_mons();
+
+ if (++m_moves >= 120) {
+ m_moves = 0;
+ wanderer();
+ }
+ if (halluc) {
+ if (!(--halluc)) {
+ unhallucinate();
+ } else {
+ hallucinate();
+ }
+ }
+ if (blind) {
+ if (!(--blind)) {
+ unblind();
+ }
+ }
+ if (confused) {
+ if (!(--confused)) {
+ unconfuse();
+ }
+ }
+ if (bear_trap) {
+ bear_trap--;
+ }
+ if (levitate) {
+ if (!(--levitate)) {
+ messagef(1, "you float gently to the ground");
+ if (dungeon[rogue.row][rogue.col] & TRAP) {
+ trap_player(rogue.row, rogue.col);
+ }
+ }
+ }
+ if (haste_self) {
+ if (!(--haste_self)) {
+ messagef(0, "you feel yourself slowing down");
+ }
+ }
+ heal();
+ if (auto_search > 0) {
+ search(auto_search, auto_search);
+ }
+ return(fainted);
+}
+
+void
+rest(int count)
+{
+ int i;
+
+ interrupted = 0;
+
+ for (i = 0; i < count; i++) {
+ if (interrupted) {
+ break;
+ }
+ (void)reg_move();
+ }
+}
+
+static char
+gr_dir(void)
+{
+ short d;
+
+ d = get_rand(1, 8);
+
+ switch(d) {
+ case 1:
+ d = 'j';
+ break;
+ case 2:
+ d = 'k';
+ break;
+ case 3:
+ d = 'l';
+ break;
+ case 4:
+ d = 'h';
+ break;
+ case 5:
+ d = 'y';
+ break;
+ case 6:
+ d = 'u';
+ break;
+ case 7:
+ d = 'b';
+ break;
+ case 8:
+ d = 'n';
+ break;
+ }
+ return(d);
+}
+
+static void
+heal(void)
+{
+ static short heal_exp = -1, n, c = 0;
+ static boolean alt;
+
+ if (rogue.hp_current == rogue.hp_max) {
+ c = 0;
+ return;
+ }
+ if (rogue.exp != heal_exp) {
+ heal_exp = rogue.exp;
+
+ switch(heal_exp) {
+ case 1:
+ n = 20;
+ break;
+ case 2:
+ n = 18;
+ break;
+ case 3:
+ n = 17;
+ break;
+ case 4:
+ n = 14;
+ break;
+ case 5:
+ n = 13;
+ break;
+ case 6:
+ n = 10;
+ break;
+ case 7:
+ n = 9;
+ break;
+ case 8:
+ n = 8;
+ break;
+ case 9:
+ n = 7;
+ break;
+ case 10:
+ n = 4;
+ break;
+ case 11:
+ n = 3;
+ break;
+ case 12:
+ default:
+ n = 2;
+ }
+ }
+ if (++c >= n) {
+ c = 0;
+ rogue.hp_current++;
+ if ((alt = !alt) != 0) {
+ rogue.hp_current++;
+ }
+ if ((rogue.hp_current += regeneration) > rogue.hp_max) {
+ rogue.hp_current = rogue.hp_max;
+ }
+ print_stats(STAT_HP);
+ }
+}
+
+static boolean
+can_turn(short nrow, short ncol)
+{
+ if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
+ return(1);
+ }
+ return(0);
+}
+
+static void
+turn_passage(short dir, boolean fast)
+{
+ short crow = rogue.row, ccol = rogue.col, turns = 0;
+ short ndir = 0;
+
+ if ((dir != 'h') && can_turn(crow, ccol + 1)) {
+ turns++;
+ ndir = 'l';
+ }
+ if ((dir != 'l') && can_turn(crow, ccol - 1)) {
+ turns++;
+ ndir = 'h';
+ }
+ if ((dir != 'k') && can_turn(crow + 1, ccol)) {
+ turns++;
+ ndir = 'j';
+ }
+ if ((dir != 'j') && can_turn(crow - 1, ccol)) {
+ turns++;
+ ndir = 'k';
+ }
+ if (turns == 1) {
+ multiple_move_rogue(ndir - (fast ? 32 : 96));
+ }
+}
diff --git a/rogue/object.c b/rogue/object.c
new file mode 100644
index 0000000..9897ec6
--- /dev/null
+++ b/rogue/object.c
@@ -0,0 +1,802 @@
+/* $NetBSD: object.c,v 1.14 2009/08/12 08:44:45 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)object.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: object.c,v 1.14 2009/08/12 08:44:45 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * object.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+object level_objects;
+unsigned short dungeon[DROWS][DCOLS];
+short foods = 0;
+char *fruit = NULL;
+
+static object *free_list = NULL;
+
+fighter rogue = {
+ INIT_AW, /* armor */
+ INIT_AW, /* weapon */
+ INIT_RINGS, /* left ring */
+ INIT_RINGS, /* right ring */
+ INIT_HP, /* Hp current */
+ INIT_HP, /* Hp max */
+ INIT_STR, /* Str current */
+ INIT_STR, /* Str max */
+ INIT_PACK, /* pack */
+ INIT_GOLD, /* gold */
+ INIT_EXPLEVEL, /* exp level */
+ INIT_EXP, /* exp points */
+ 0, 0, /* row, col */
+ INIT_CHAR, /* char */
+ INIT_MOVES /* moves */
+};
+
+struct id id_potions[POTIONS] = {
+{100, "blue ", "of increase strength ", 0},
+{250, "red ", "of restore strength ", 0},
+{100, "green ", "of healing ", 0},
+{200, "grey ", "of extra healing ", 0},
+ {10, "brown ", "of poison ", 0},
+{300, "clear ", "of raise level ", 0},
+ {10, "pink ", "of blindness ", 0},
+ {25, "white ", "of hallucination ", 0},
+{100, "purple ", "of detect monster ", 0},
+{100, "black ", "of detect things ", 0},
+ {10, "yellow ", "of confusion ", 0},
+ {80, "plaid ", "of levitation ", 0},
+{150, "burgundy ", "of haste self ", 0},
+{145, "beige ", "of see invisible ", 0}
+};
+
+struct id id_scrolls[SCROLS] = {
+{505, "", "of protect armor ", 0},
+{200, "", "of hold monster ", 0},
+{235, "", "of enchant weapon ", 0},
+{235, "", "of enchant armor ", 0},
+{175, "", "of identify ", 0},
+{190, "", "of teleportation ", 0},
+ {25, "", "of sleep ", 0},
+{610, "", "of scare monster ", 0},
+{210, "", "of remove curse ", 0},
+ {80, "", "of create monster ",0},
+ {25, "", "of aggravate monster ",0},
+{180, "", "of magic mapping ", 0},
+ {90, "", "of confuse monster ", 0}
+};
+
+struct id id_weapons[WEAPONS] = {
+ {150, "short bow ", "", 0},
+ {8, "darts ", "", 0},
+ {15, "arrows ", "", 0},
+ {27, "daggers ", "", 0},
+ {35, "shurikens ", "", 0},
+ {360, "mace ", "", 0},
+ {470, "long sword ", "", 0},
+ {580, "two-handed sword ", "", 0}
+};
+
+struct id id_armors[ARMORS] = {
+ {300, "leather armor ", "", (UNIDENTIFIED)},
+ {300, "ring mail ", "", (UNIDENTIFIED)},
+ {400, "scale mail ", "", (UNIDENTIFIED)},
+ {500, "chain mail ", "", (UNIDENTIFIED)},
+ {600, "banded mail ", "", (UNIDENTIFIED)},
+ {600, "splint mail ", "", (UNIDENTIFIED)},
+ {700, "plate mail ", "", (UNIDENTIFIED)}
+};
+
+struct id id_wands[WANDS] = {
+ {25, "", "of teleport away ",0},
+ {50, "", "of slow monster ", 0},
+ {8, "", "of invisibility ",0},
+ {55, "", "of polymorph ",0},
+ {2, "", "of haste monster ",0},
+ {20, "", "of magic missile ",0},
+ {20, "", "of cancellation ",0},
+ {0, "", "of do nothing ",0},
+ {35, "", "of drain life ",0},
+ {20, "", "of cold ",0},
+ {20, "", "of fire ",0}
+};
+
+struct id id_rings[RINGS] = {
+ {250, "", "of stealth ",0},
+ {100, "", "of teleportation ", 0},
+ {255, "", "of regeneration ",0},
+ {295, "", "of slow digestion ",0},
+ {200, "", "of add strength ",0},
+ {250, "", "of sustain strength ",0},
+ {250, "", "of dexterity ",0},
+ {25, "", "of adornment ",0},
+ {300, "", "of see invisible ",0},
+ {290, "", "of maintain armor ",0},
+ {270, "", "of searching ",0},
+};
+
+static void gr_armor(object *);
+static void gr_potion(object *);
+static void gr_scroll(object *);
+static void gr_wand(object *);
+static void gr_weapon(object *, int);
+static unsigned short gr_what_is(void);
+static void make_party(void);
+static void plant_gold(short, short, boolean);
+static void put_gold(void);
+static void rand_place(object *);
+
+void
+put_objects(void)
+{
+ short i, n;
+ object *obj;
+
+ if (cur_level < max_level) {
+ return;
+ }
+ n = coin_toss() ? get_rand(2, 4) : get_rand(3, 5);
+ while (rand_percent(33)) {
+ n++;
+ }
+ if (party_room != NO_ROOM) {
+ make_party();
+ }
+ for (i = 0; i < n; i++) {
+ obj = gr_object();
+ rand_place(obj);
+ }
+ put_gold();
+}
+
+static void
+put_gold(void)
+{
+ short i, j;
+ short row,col;
+ boolean is_maze, is_room;
+
+ for (i = 0; i < MAXROOMS; i++) {
+ is_maze = (rooms[i].is_room & R_MAZE) ? 1 : 0;
+ is_room = (rooms[i].is_room & R_ROOM) ? 1 : 0;
+
+ if (!(is_room || is_maze)) {
+ continue;
+ }
+ if (is_maze || rand_percent(GOLD_PERCENT)) {
+ for (j = 0; j < 50; j++) {
+ row = get_rand(rooms[i].top_row+1,
+ rooms[i].bottom_row-1);
+ col = get_rand(rooms[i].left_col+1,
+ rooms[i].right_col-1);
+ if ((dungeon[row][col] == FLOOR) ||
+ (dungeon[row][col] == TUNNEL)) {
+ plant_gold(row, col, is_maze);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+plant_gold(short row, short col, boolean is_maze)
+{
+ object *obj;
+
+ obj = alloc_object();
+ obj->row = row; obj->col = col;
+ obj->what_is = GOLD;
+ obj->quantity = get_rand((2 * cur_level), (16 * cur_level));
+ if (is_maze) {
+ obj->quantity += obj->quantity / 2;
+ }
+ dungeon[row][col] |= OBJECT;
+ (void)add_to_pack(obj, &level_objects, 0);
+}
+
+void
+place_at(object *obj, int row, int col)
+{
+ obj->row = row;
+ obj->col = col;
+ dungeon[row][col] |= OBJECT;
+ (void)add_to_pack(obj, &level_objects, 0);
+}
+
+object *
+object_at(object *pack, short row, short col)
+{
+ object *obj = NULL;
+
+ if (dungeon[row][col] & (MONSTER | OBJECT)) {
+ obj = pack->next_object;
+
+ while (obj && ((obj->row != row) || (obj->col != col))) {
+ obj = obj->next_object;
+ }
+ if (!obj) {
+ messagef(1, "object_at(): inconsistent");
+ }
+ }
+ return(obj);
+}
+
+object *
+get_letter_object(int ch)
+{
+ object *obj;
+
+ obj = rogue.pack.next_object;
+
+ while (obj && (obj->ichar != ch)) {
+ obj = obj->next_object;
+ }
+ return(obj);
+}
+
+void
+free_stuff(object *objlist)
+{
+ object *obj;
+
+ while (objlist->next_object) {
+ obj = objlist->next_object;
+ objlist->next_object =
+ objlist->next_object->next_object;
+ free_object(obj);
+ }
+}
+
+const char *
+name_of(const object *obj)
+{
+ const char *retstring;
+
+ switch(obj->what_is) {
+ case SCROL:
+ retstring = obj->quantity > 1 ? "scrolls " : "scroll ";
+ break;
+ case POTION:
+ retstring = obj->quantity > 1 ? "potions " : "potion ";
+ break;
+ case FOOD:
+ if (obj->which_kind == RATION) {
+ retstring = "food ";
+ } else {
+ retstring = fruit;
+ }
+ break;
+ case WAND:
+ retstring = is_wood[obj->which_kind] ? "staff " : "wand ";
+ break;
+ case WEAPON:
+ switch(obj->which_kind) {
+ case DART:
+ retstring=obj->quantity > 1 ? "darts " : "dart ";
+ break;
+ case ARROW:
+ retstring=obj->quantity > 1 ? "arrows " : "arrow ";
+ break;
+ case DAGGER:
+ retstring=obj->quantity > 1 ? "daggers " : "dagger ";
+ break;
+ case SHURIKEN:
+ retstring=obj->quantity > 1?"shurikens ":"shuriken ";
+ break;
+ default:
+ retstring = id_weapons[obj->which_kind].title;
+ }
+ break;
+ case ARMOR:
+ retstring = "armor ";
+ break;
+ case RING:
+ retstring = "ring ";
+ break;
+ case AMULET:
+ retstring = "amulet ";
+ break;
+ default:
+ retstring = "unknown ";
+ break;
+ }
+ return(retstring);
+}
+
+object *
+gr_object(void)
+{
+ object *obj;
+
+ obj = alloc_object();
+
+ if (foods < (cur_level / 3)) {
+ obj->what_is = FOOD;
+ foods++;
+ } else {
+ obj->what_is = gr_what_is();
+ }
+ switch(obj->what_is) {
+ case SCROL:
+ gr_scroll(obj);
+ break;
+ case POTION:
+ gr_potion(obj);
+ break;
+ case WEAPON:
+ gr_weapon(obj, 1);
+ break;
+ case ARMOR:
+ gr_armor(obj);
+ break;
+ case WAND:
+ gr_wand(obj);
+ break;
+ case FOOD:
+ get_food(obj, 0);
+ break;
+ case RING:
+ gr_ring(obj, 1);
+ break;
+ }
+ return(obj);
+}
+
+static unsigned short
+gr_what_is(void)
+{
+ short percent;
+ unsigned short what_is;
+
+ percent = get_rand(1, 91);
+
+ if (percent <= 30) {
+ what_is = SCROL;
+ } else if (percent <= 60) {
+ what_is = POTION;
+ } else if (percent <= 64) {
+ what_is = WAND;
+ } else if (percent <= 74) {
+ what_is = WEAPON;
+ } else if (percent <= 83) {
+ what_is = ARMOR;
+ } else if (percent <= 88) {
+ what_is = FOOD;
+ } else {
+ what_is = RING;
+ }
+ return(what_is);
+}
+
+static void
+gr_scroll(object *obj)
+{
+ short percent;
+
+ percent = get_rand(0, 91);
+
+ obj->what_is = SCROL;
+
+ if (percent <= 5) {
+ obj->which_kind = PROTECT_ARMOR;
+ } else if (percent <= 10) {
+ obj->which_kind = HOLD_MONSTER;
+ } else if (percent <= 20) {
+ obj->which_kind = CREATE_MONSTER;
+ } else if (percent <= 35) {
+ obj->which_kind = IDENTIFY;
+ } else if (percent <= 43) {
+ obj->which_kind = TELEPORT;
+ } else if (percent <= 50) {
+ obj->which_kind = SLEEP;
+ } else if (percent <= 55) {
+ obj->which_kind = SCARE_MONSTER;
+ } else if (percent <= 64) {
+ obj->which_kind = REMOVE_CURSE;
+ } else if (percent <= 69) {
+ obj->which_kind = ENCH_ARMOR;
+ } else if (percent <= 74) {
+ obj->which_kind = ENCH_WEAPON;
+ } else if (percent <= 80) {
+ obj->which_kind = AGGRAVATE_MONSTER;
+ } else if (percent <= 86) {
+ obj->which_kind = CON_MON;
+ } else {
+ obj->which_kind = MAGIC_MAPPING;
+ }
+}
+
+static void
+gr_potion(object *obj)
+{
+ short percent;
+
+ percent = get_rand(1, 118);
+
+ obj->what_is = POTION;
+
+ if (percent <= 5) {
+ obj->which_kind = RAISE_LEVEL;
+ } else if (percent <= 15) {
+ obj->which_kind = DETECT_OBJECTS;
+ } else if (percent <= 25) {
+ obj->which_kind = DETECT_MONSTER;
+ } else if (percent <= 35) {
+ obj->which_kind = INCREASE_STRENGTH;
+ } else if (percent <= 45) {
+ obj->which_kind = RESTORE_STRENGTH;
+ } else if (percent <= 55) {
+ obj->which_kind = HEALING;
+ } else if (percent <= 65) {
+ obj->which_kind = EXTRA_HEALING;
+ } else if (percent <= 75) {
+ obj->which_kind = BLINDNESS;
+ } else if (percent <= 85) {
+ obj->which_kind = HALLUCINATION;
+ } else if (percent <= 95) {
+ obj->which_kind = CONFUSION;
+ } else if (percent <= 105) {
+ obj->which_kind = POISON;
+ } else if (percent <= 110) {
+ obj->which_kind = LEVITATION;
+ } else if (percent <= 114) {
+ obj->which_kind = HASTE_SELF;
+ } else {
+ obj->which_kind = SEE_INVISIBLE;
+ }
+}
+
+static void
+gr_weapon(object *obj, int assign_wk)
+{
+ short percent;
+ short i;
+ short blessing, increment;
+
+ obj->what_is = WEAPON;
+ if (assign_wk) {
+ obj->which_kind = get_rand(0, (WEAPONS - 1));
+ }
+ if ((obj->which_kind == ARROW) || (obj->which_kind == DAGGER) ||
+ (obj->which_kind == SHURIKEN) || (obj->which_kind == DART)) {
+ obj->quantity = get_rand(3, 15);
+ obj->quiver = get_rand(0, 126);
+ } else {
+ obj->quantity = 1;
+ }
+ obj->hit_enchant = obj->d_enchant = 0;
+
+ percent = get_rand(1, 96);
+ blessing = get_rand(1, 3);
+
+ if (percent <= 32) {
+ if (percent <= 16) {
+ increment = 1;
+ } else {
+ increment = -1;
+ obj->is_cursed = 1;
+ }
+ for (i = 0; i < blessing; i++) {
+ if (coin_toss()) {
+ obj->hit_enchant += increment;
+ } else {
+ obj->d_enchant += increment;
+ }
+ }
+ }
+ switch(obj->which_kind) {
+ case BOW:
+ case DART:
+ obj->damage = "1d1";
+ break;
+ case ARROW:
+ obj->damage = "1d2";
+ break;
+ case DAGGER:
+ obj->damage = "1d3";
+ break;
+ case SHURIKEN:
+ obj->damage = "1d4";
+ break;
+ case MACE:
+ obj->damage = "2d3";
+ break;
+ case LONG_SWORD:
+ obj->damage = "3d4";
+ break;
+ case TWO_HANDED_SWORD:
+ obj->damage = "4d5";
+ break;
+ }
+}
+
+static void
+gr_armor(object *obj)
+{
+ short percent;
+ short blessing;
+
+ obj->what_is = ARMOR;
+ obj->which_kind = get_rand(0, (ARMORS - 1));
+ obj->class = obj->which_kind + 2;
+ if ((obj->which_kind == PLATE) || (obj->which_kind == SPLINT)) {
+ obj->class--;
+ }
+ obj->is_protected = 0;
+ obj->d_enchant = 0;
+
+ percent = get_rand(1, 100);
+ blessing = get_rand(1, 3);
+
+ if (percent <= 16) {
+ obj->is_cursed = 1;
+ obj->d_enchant -= blessing;
+ } else if (percent <= 33) {
+ obj->d_enchant += blessing;
+ }
+}
+
+static void
+gr_wand(object *obj)
+{
+ obj->what_is = WAND;
+ obj->which_kind = get_rand(0, (WANDS - 1));
+ obj->class = get_rand(3, 7);
+}
+
+void
+get_food(object *obj, boolean force_ration)
+{
+ obj->what_is = FOOD;
+
+ if (force_ration || rand_percent(80)) {
+ obj->which_kind = RATION;
+ } else {
+ obj->which_kind = FRUIT;
+ }
+}
+
+void
+put_stairs(void)
+{
+ short row, col;
+
+ gr_row_col(&row, &col, (FLOOR | TUNNEL));
+ dungeon[row][col] |= STAIRS;
+}
+
+int
+get_armor_class(const object *obj)
+{
+ if (obj) {
+ return(obj->class + obj->d_enchant);
+ }
+ return(0);
+}
+
+object *
+alloc_object(void)
+{
+ object *obj;
+
+ if (free_list) {
+ obj = free_list;
+ free_list = free_list->next_object;
+ } else if (!(obj = md_malloc(sizeof(object)))) {
+ messagef(0, "cannot allocate object, saving game");
+ save_into_file(error_file);
+ clean_up("alloc_object: save failed");
+ }
+ obj->quantity = 1;
+ obj->ichar = 'L';
+ obj->picked_up = obj->is_cursed = 0;
+ obj->in_use_flags = NOT_USED;
+ obj->identified = UNIDENTIFIED;
+ obj->damage = "1d1";
+ return(obj);
+}
+
+void
+free_object(object *obj)
+{
+ obj->next_object = free_list;
+ free_list = obj;
+}
+
+static void
+make_party(void)
+{
+ short n;
+
+ party_room = gr_room();
+
+ n = rand_percent(99) ? party_objects(party_room) : 11;
+ if (rand_percent(99)) {
+ party_monsters(party_room, n);
+ }
+}
+
+void
+show_objects(void)
+{
+ object *obj;
+ short mc, rc, row, col;
+ object *monster;
+
+ obj = level_objects.next_object;
+
+ while (obj) {
+ row = obj->row;
+ col = obj->col;
+
+ rc = get_mask_char(obj->what_is);
+
+ if (dungeon[row][col] & MONSTER) {
+ if ((monster =
+ object_at(&level_monsters, row, col)) != NULL) {
+ monster->trail_char = rc;
+ }
+ }
+ mc = mvinch(row, col);
+ if (((mc < 'A') || (mc > 'Z')) &&
+ ((row != rogue.row) || (col != rogue.col))) {
+ mvaddch(row, col, rc);
+ }
+ obj = obj->next_object;
+ }
+
+ monster = level_monsters.next_object;
+
+ while (monster) {
+ if (monster->m_flags & IMITATES) {
+ mvaddch(monster->row, monster->col, (int)monster->disguise);
+ }
+ monster = monster->next_monster;
+ }
+}
+
+void
+put_amulet(void)
+{
+ object *obj;
+
+ obj = alloc_object();
+ obj->what_is = AMULET;
+ rand_place(obj);
+}
+
+static void
+rand_place(object *obj)
+{
+ short row, col;
+
+ gr_row_col(&row, &col, (FLOOR | TUNNEL));
+ place_at(obj, row, col);
+}
+
+void
+c_object_for_wizard(void)
+{
+ short ch, max, wk;
+ object *obj;
+ char buf[80];
+
+ max = 0;
+ if (pack_count(NULL) >= MAX_PACK_COUNT) {
+ messagef(0, "pack full");
+ return;
+ }
+ messagef(0, "type of object?");
+
+ while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) {
+ sound_bell();
+ }
+ check_message();
+
+ if (ch == '\033') {
+ return;
+ }
+ obj = alloc_object();
+
+ switch(ch) {
+ case '!':
+ obj->what_is = POTION;
+ max = POTIONS - 1;
+ break;
+ case '?':
+ obj->what_is = SCROL;
+ max = SCROLS - 1;
+ break;
+ case ',':
+ obj->what_is = AMULET;
+ break;
+ case ':':
+ get_food(obj, 0);
+ break;
+ case ')':
+ gr_weapon(obj, 0);
+ max = WEAPONS - 1;
+ break;
+ case ']':
+ gr_armor(obj);
+ max = ARMORS - 1;
+ break;
+ case '/':
+ gr_wand(obj);
+ max = WANDS - 1;
+ break;
+ case '=':
+ max = RINGS - 1;
+ obj->what_is = RING;
+ break;
+ }
+ if ((ch != ',') && (ch != ':')) {
+GIL:
+ if (get_input_line("which kind?", "", buf, sizeof(buf), "", 0, 1)) {
+ wk = get_number(buf);
+ if ((wk >= 0) && (wk <= max)) {
+ obj->which_kind = wk;
+ if (obj->what_is == RING) {
+ gr_ring(obj, 0);
+ }
+ } else {
+ sound_bell();
+ goto GIL;
+ }
+ } else {
+ free_object(obj);
+ return;
+ }
+ }
+ get_desc(obj, buf, sizeof(buf));
+ messagef(0, "%s", buf);
+ (void)add_to_pack(obj, &rogue.pack, 1);
+}
diff --git a/rogue/pack.c b/rogue/pack.c
new file mode 100644
index 0000000..f8aed6e
--- /dev/null
+++ b/rogue/pack.c
@@ -0,0 +1,574 @@
+/* $NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pack.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * pack.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+const char curse_message[] = "you can't, it appears to be cursed";
+
+static object *check_duplicate(object *, object *);
+static boolean is_pack_letter(short *, unsigned short *);
+static boolean mask_pack(const object *, unsigned short);
+static short next_avail_ichar(void);
+
+object *
+add_to_pack(object *obj, object *pack, int condense)
+{
+ object *op;
+
+ if (condense) {
+ if ((op = check_duplicate(obj, pack)) != NULL) {
+ free_object(obj);
+ return(op);
+ } else {
+ obj->ichar = next_avail_ichar();
+ }
+ }
+ if (pack->next_object == 0) {
+ pack->next_object = obj;
+ } else {
+ op = pack->next_object;
+
+ while (op->next_object) {
+ op = op->next_object;
+ }
+ op->next_object = obj;
+ }
+ obj->next_object = 0;
+ return(obj);
+}
+
+void
+take_from_pack(object *obj, object *pack)
+{
+ while (pack->next_object != obj) {
+ pack = pack->next_object;
+ }
+ pack->next_object = pack->next_object->next_object;
+}
+
+/* Note: *status is set to 0 if the rogue attempts to pick up a scroll
+ * of scare-monster and it turns to dust. *status is otherwise set to 1.
+ */
+
+object *
+pick_up(int row, int col, short *status)
+{
+ object *obj;
+
+ *status = 1;
+
+ if (levitate) {
+ messagef(0, "you're floating in the air!");
+ return NULL;
+ }
+ obj = object_at(&level_objects, row, col);
+ if (!obj) {
+ messagef(1, "pick_up(): inconsistent");
+ return(obj);
+ }
+ if ( (obj->what_is == SCROL) &&
+ (obj->which_kind == SCARE_MONSTER) &&
+ obj->picked_up) {
+ messagef(0, "the scroll turns to dust as you pick it up");
+ dungeon[row][col] &= (~OBJECT);
+ vanish(obj, 0, &level_objects);
+ *status = 0;
+ if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) {
+ id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED;
+ }
+ return NULL;
+ }
+ if (obj->what_is == GOLD) {
+ rogue.gold += obj->quantity;
+ dungeon[row][col] &= ~(OBJECT);
+ take_from_pack(obj, &level_objects);
+ print_stats(STAT_GOLD);
+ return(obj); /* obj will be free_object()ed in caller */
+ }
+ if (pack_count(obj) >= MAX_PACK_COUNT) {
+ messagef(1, "pack too full");
+ return NULL;
+ }
+ dungeon[row][col] &= ~(OBJECT);
+ take_from_pack(obj, &level_objects);
+ obj = add_to_pack(obj, &rogue.pack, 1);
+ obj->picked_up = 1;
+ return(obj);
+}
+
+void
+drop(void)
+{
+ object *obj, *new;
+ short ch;
+ char desc[DCOLS];
+
+ if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) {
+ messagef(0, "there's already something there");
+ return;
+ }
+ if (!rogue.pack.next_object) {
+ messagef(0, "you have nothing to drop");
+ return;
+ }
+ if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (obj->in_use_flags & BEING_WIELDED) {
+ if (obj->is_cursed) {
+ messagef(0, "%s", curse_message);
+ return;
+ }
+ unwield(rogue.weapon);
+ } else if (obj->in_use_flags & BEING_WORN) {
+ if (obj->is_cursed) {
+ messagef(0, "%s", curse_message);
+ return;
+ }
+ mv_aquatars();
+ unwear(rogue.armor);
+ print_stats(STAT_ARMOR);
+ } else if (obj->in_use_flags & ON_EITHER_HAND) {
+ if (obj->is_cursed) {
+ messagef(0, "%s", curse_message);
+ return;
+ }
+ un_put_on(obj);
+ }
+ obj->row = rogue.row;
+ obj->col = rogue.col;
+
+ if ((obj->quantity > 1) && (obj->what_is != WEAPON)) {
+ obj->quantity--;
+ new = alloc_object();
+ *new = *obj;
+ new->quantity = 1;
+ obj = new;
+ } else {
+ obj->ichar = 'L';
+ take_from_pack(obj, &rogue.pack);
+ }
+ place_at(obj, rogue.row, rogue.col);
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "dropped %s", desc);
+ (void)reg_move();
+}
+
+static object *
+check_duplicate(object *obj, object *pack)
+{
+ object *op;
+
+ if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
+ return(0);
+ }
+ if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
+ return(0);
+ }
+ op = pack->next_object;
+
+ while (op) {
+ if ((op->what_is == obj->what_is) &&
+ (op->which_kind == obj->which_kind)) {
+
+ if ((obj->what_is != WEAPON) ||
+ ((obj->what_is == WEAPON) &&
+ ((obj->which_kind == ARROW) ||
+ (obj->which_kind == DAGGER) ||
+ (obj->which_kind == DART) ||
+ (obj->which_kind == SHURIKEN)) &&
+ (obj->quiver == op->quiver))) {
+ op->quantity += obj->quantity;
+ return(op);
+ }
+ }
+ op = op->next_object;
+ }
+ return(0);
+}
+
+static short
+next_avail_ichar(void)
+{
+ object *obj;
+ int i;
+ boolean ichars[26];
+
+ for (i = 0; i < 26; i++) {
+ ichars[i] = 0;
+ }
+ obj = rogue.pack.next_object;
+ while (obj) {
+ if (obj->ichar >= 'a' && obj->ichar <= 'z') {
+ ichars[(obj->ichar - 'a')] = 1;
+ }
+ obj = obj->next_object;
+ }
+ for (i = 0; i < 26; i++) {
+ if (!ichars[i]) {
+ return(i + 'a');
+ }
+ }
+ return('?');
+}
+
+void
+wait_for_ack(void)
+{
+ while (rgetchar() != ' ')
+ ;
+}
+
+short
+pack_letter(const char *prompt, unsigned short mask)
+{
+ short ch;
+ unsigned short tmask = mask;
+
+ if (!mask_pack(&rogue.pack, mask)) {
+ messagef(0, "nothing appropriate");
+ return(CANCEL);
+ }
+ for (;;) {
+
+ messagef(0, "%s", prompt);
+
+ for (;;) {
+ ch = rgetchar();
+ if (!is_pack_letter(&ch, &mask)) {
+ sound_bell();
+ } else {
+ break;
+ }
+ }
+
+ if (ch == LIST) {
+ check_message();
+ mask = tmask;
+ inventory(&rogue.pack, mask);
+ } else {
+ break;
+ }
+ mask = tmask;
+ }
+ check_message();
+ return(ch);
+}
+
+void
+take_off(void)
+{
+ char desc[DCOLS];
+ object *obj;
+
+ if (rogue.armor) {
+ if (rogue.armor->is_cursed) {
+ messagef(0, "%s", curse_message);
+ } else {
+ mv_aquatars();
+ obj = rogue.armor;
+ unwear(rogue.armor);
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "was wearing %s", desc);
+ print_stats(STAT_ARMOR);
+ (void)reg_move();
+ }
+ } else {
+ messagef(0, "not wearing any");
+ }
+}
+
+void
+wear(void)
+{
+ short ch;
+ object *obj;
+ char desc[DCOLS];
+
+ if (rogue.armor) {
+ messagef(0, "you're already wearing some");
+ return;
+ }
+ ch = pack_letter("wear what?", ARMOR);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (obj->what_is != ARMOR) {
+ messagef(0, "you can't wear that");
+ return;
+ }
+ obj->identified = 1;
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "wearing %s", desc);
+ do_wear(obj);
+ print_stats(STAT_ARMOR);
+ (void)reg_move();
+}
+
+void
+unwear(object *obj)
+{
+ if (obj) {
+ obj->in_use_flags &= (~BEING_WORN);
+ }
+ rogue.armor = NULL;
+}
+
+void
+do_wear(object *obj)
+{
+ rogue.armor = obj;
+ obj->in_use_flags |= BEING_WORN;
+ obj->identified = 1;
+}
+
+void
+wield(void)
+{
+ short ch;
+ object *obj;
+ char desc[DCOLS];
+
+ if (rogue.weapon && rogue.weapon->is_cursed) {
+ messagef(0, "%s", curse_message);
+ return;
+ }
+ ch = pack_letter("wield what?", WEAPON);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "No such item.");
+ return;
+ }
+ if (obj->what_is & (ARMOR | RING)) {
+ messagef(0, "you can't wield %s",
+ ((obj->what_is == ARMOR) ? "armor" : "rings"));
+ return;
+ }
+ if (obj->in_use_flags & BEING_WIELDED) {
+ messagef(0, "in use");
+ } else {
+ unwield(rogue.weapon);
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "wielding %s", desc);
+ do_wield(obj);
+ (void)reg_move();
+ }
+}
+
+void
+do_wield(object *obj)
+{
+ rogue.weapon = obj;
+ obj->in_use_flags |= BEING_WIELDED;
+}
+
+void
+unwield(object *obj)
+{
+ if (obj) {
+ obj->in_use_flags &= (~BEING_WIELDED);
+ }
+ rogue.weapon = NULL;
+}
+
+void
+call_it(void)
+{
+ short ch;
+ object *obj;
+ struct id *id_table;
+ char buf[MAX_TITLE_LENGTH+2];
+
+ ch = pack_letter("call what?", (SCROL | POTION | WAND | RING));
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
+ messagef(0, "surely you already know what that's called");
+ return;
+ }
+ id_table = get_id_table(obj);
+
+ if (get_input_line("call it:", "", buf, sizeof(buf),
+ id_table[obj->which_kind].title, 1, 1)) {
+ id_table[obj->which_kind].id_status = CALLED;
+ (void)strlcpy(id_table[obj->which_kind].title, buf,
+ sizeof(id_table[obj->which_kind].title));
+ }
+}
+
+short
+pack_count(const object *new_obj)
+{
+ object *obj;
+ short count = 0;
+
+ obj = rogue.pack.next_object;
+
+ while (obj) {
+ if (obj->what_is != WEAPON) {
+ count += obj->quantity;
+ } else if (!new_obj) {
+ count++;
+ } else if ((new_obj->what_is != WEAPON) ||
+ ((obj->which_kind != ARROW) &&
+ (obj->which_kind != DAGGER) &&
+ (obj->which_kind != DART) &&
+ (obj->which_kind != SHURIKEN)) ||
+ (new_obj->which_kind != obj->which_kind) ||
+ (obj->quiver != new_obj->quiver)) {
+ count++;
+ }
+ obj = obj->next_object;
+ }
+ return(count);
+}
+
+static boolean
+mask_pack(const object *pack, unsigned short mask)
+{
+ while (pack->next_object) {
+ pack = pack->next_object;
+ if (pack->what_is & mask) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+static boolean
+is_pack_letter(short *c, unsigned short *mask)
+{
+ if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') ||
+ (*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) {
+ switch(*c) {
+ case '?':
+ *mask = SCROL;
+ break;
+ case '!':
+ *mask = POTION;
+ break;
+ case ':':
+ *mask = FOOD;
+ break;
+ case ')':
+ *mask = WEAPON;
+ break;
+ case ']':
+ *mask = ARMOR;
+ break;
+ case '/':
+ *mask = WAND;
+ break;
+ case '=':
+ *mask = RING;
+ break;
+ case ',':
+ *mask = AMULET;
+ break;
+ }
+ *c = LIST;
+ return(1);
+ }
+ return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST));
+}
+
+boolean
+has_amulet(void)
+{
+ return(mask_pack(&rogue.pack, AMULET));
+}
+
+void
+kick_into_pack(void)
+{
+ object *obj;
+ char desc[DCOLS];
+ short stat;
+
+ if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
+ messagef(0, "nothing here");
+ } else {
+ if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) {
+ get_desc(obj, desc, sizeof(desc));
+ if (obj->what_is == GOLD) {
+ messagef(0, "%s", desc);
+ free_object(obj);
+ } else {
+ messagef(0, "%s(%c)", desc, obj->ichar);
+ }
+ }
+ if (obj || (!stat)) {
+ (void)reg_move();
+ }
+ }
+}
diff --git a/rogue/pathnames.h b/rogue/pathnames.h
new file mode 100644
index 0000000..d6e91a5
--- /dev/null
+++ b/rogue/pathnames.h
@@ -0,0 +1,35 @@
+/* $NetBSD: pathnames.h,v 1.5 2008/01/14 03:50:02 dholland Exp $ */
+
+/*-
+ * 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_SCOREFILE "/var/games/rogue.scores"
+#define _PATH_SCREENDUMP "rogue.screen"
diff --git a/rogue/play.c b/rogue/play.c
new file mode 100644
index 0000000..8824538
--- /dev/null
+++ b/rogue/play.c
@@ -0,0 +1,299 @@
+/* $NetBSD: play.c,v 1.9 2008/01/14 03:50:02 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)play.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: play.c,v 1.9 2008/01/14 03:50:02 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * play.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+boolean interrupted = 0;
+
+static const char unknown_command[] = "unknown command";
+
+void
+play_level(void)
+{
+ short ch;
+ int count;
+
+ for (;;) {
+ interrupted = 0;
+ if (hit_message[0]) {
+ messagef(1, "%s", hit_message);
+ hit_message[0] = 0;
+ }
+ if (trap_door) {
+ trap_door = 0;
+ return;
+ }
+ move(rogue.row, rogue.col);
+ refresh();
+
+ ch = rgetchar();
+CMCH:
+ check_message();
+ count = 0;
+CH:
+ switch(ch) {
+ case '.':
+ rest((count > 0) ? count : 1);
+ break;
+ case 's':
+ search(((count > 0) ? count : 1), 0);
+ break;
+ case 'i':
+ inventory(&rogue.pack, ALL_OBJECTS);
+ break;
+ case 'f':
+ fight(0);
+ break;
+ case 'F':
+ fight(1);
+ break;
+ case 'h':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'y':
+ case 'u':
+ case 'n':
+ case 'b':
+ (void)one_move_rogue(ch, 1);
+ break;
+ case 'H':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'B':
+ case 'Y':
+ case 'U':
+ case 'N':
+ case '\010':
+ case '\012':
+ case '\013':
+ case '\014':
+ case '\031':
+ case '\025':
+ case '\016':
+ case '\002':
+ multiple_move_rogue(ch);
+ break;
+ case 'e':
+ eat();
+ break;
+ case 'q':
+ quaff();
+ break;
+ case 'r':
+ read_scroll();
+ break;
+ case 'm':
+ move_onto();
+ break;
+ case ',':
+ kick_into_pack();
+ break;
+ case 'd':
+ drop();
+ break;
+ case 'P':
+ put_on_ring();
+ break;
+ case 'R':
+ remove_ring();
+ break;
+ case '\020':
+ do {
+ remessage(count++);
+ ch = rgetchar();
+ } while (ch == '\020');
+ goto CMCH;
+ break;
+ case '\027':
+ wizardize();
+ break;
+ case '>':
+ if (drop_check()) {
+ return;
+ }
+ break;
+ case '<':
+ if (check_up()) {
+ return;
+ }
+ break;
+ case ')':
+ case ']':
+ inv_armor_weapon(ch == ')');
+ break;
+ case '=':
+ inv_rings();
+ break;
+ case '^':
+ id_trap();
+ break;
+ case '/':
+ id_type();
+ break;
+ case '?':
+ id_com();
+ break;
+ case '!':
+ do_shell();
+ break;
+ case 'o':
+ edit_opts();
+ break;
+ case 'I':
+ single_inv(0);
+ break;
+ case 'T':
+ take_off();
+ break;
+ case 'W':
+ wear();
+ break;
+ case 'w':
+ wield();
+ break;
+ case 'c':
+ call_it();
+ break;
+ case 'z':
+ zapp();
+ break;
+ case 't':
+ throw();
+ break;
+ case 'v':
+ messagef(0, "rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims");
+ break;
+ case 'Q':
+ quit(0);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ move(rogue.row, rogue.col);
+ refresh();
+ do {
+ if (count < 100) {
+ count = (10 * count) + (ch - '0');
+ }
+ ch = rgetchar();
+ } while (is_digit(ch));
+ if (ch != CANCEL) {
+ goto CH;
+ }
+ break;
+ case ' ':
+ break;
+ case '\011':
+ if (wizard) {
+ inventory(&level_objects, ALL_OBJECTS);
+ } else {
+ messagef(0, "%s", unknown_command);
+ }
+ break;
+ case '\023':
+ if (wizard) {
+ draw_magic_map();
+ } else {
+ messagef(0, "%s", unknown_command);
+ }
+ break;
+ case '\024':
+ if (wizard) {
+ show_traps();
+ } else {
+ messagef(0, "%s", unknown_command);
+ }
+ break;
+ case '\017':
+ if (wizard) {
+ show_objects();
+ } else {
+ messagef(0, "%s", unknown_command);
+ }
+ break;
+ case '\001':
+ show_average_hp();
+ break;
+ case '\003':
+ if (wizard) {
+ c_object_for_wizard();
+ } else {
+ messagef(0, "%s", unknown_command);
+ }
+ break;
+ case '\015':
+ if (wizard) {
+ show_monsters();
+ } else {
+ messagef(0, "%s", unknown_command);
+ }
+ break;
+ case 'S':
+ save_game();
+ break;
+ default:
+ messagef(0, "%s", unknown_command);
+ break;
+ }
+ }
+}
diff --git a/rogue/random.c b/rogue/random.c
new file mode 100644
index 0000000..f119c0f
--- /dev/null
+++ b/rogue/random.c
@@ -0,0 +1,146 @@
+/* $NetBSD: random.c,v 1.8 2009/08/12 08:44:45 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)random.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: random.c,v 1.8 2009/08/12 08:44:45 dholland Exp $");
+#endif
+#endif /* not lint */
+
+#include "rogue.h"
+
+/*
+ * random.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+static long rntb[32] = {
+ 3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342,
+ 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, 0x7449e56b,
+ 0xbeb1dbb0, 0xab5c5918, 0x946554fd, 0x8c2e680f, 0xeb3d799f,
+ 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d,
+ 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc,
+ 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e,
+ 0x8999220b, 0x27fb47b9
+};
+
+static long *fptr = &rntb[4];
+static long *rptr = &rntb[1];
+static long *state = &rntb[1];
+static int rand_type = 3;
+static int rand_deg = 31;
+static int rand_sep = 3;
+static long *end_ptr = &rntb[32];
+
+static long rrandom(void);
+
+void
+srrandom(int x)
+{
+ int i;
+
+ state[0] = x;
+ if (rand_type != 0) {
+ for (i = 1; i < rand_deg; i++) {
+ state[i] = 1103515245 * state[i - 1] + 12345;
+ }
+ fptr = &state[rand_sep];
+ rptr = &state[0];
+ for (i = 0; i < 10 * rand_deg; i++) {
+ (void)rrandom();
+ }
+ }
+}
+
+static long
+rrandom(void)
+{
+ long i;
+
+ if (rand_type == 0) {
+ i = state[0] = (state[0]*1103515245 + 12345) & 0x7fffffff;
+ } else {
+ *fptr += *rptr;
+ i = (*fptr >> 1) & 0x7fffffff;
+ if (++fptr >= end_ptr) {
+ fptr = state;
+ ++rptr;
+ } else {
+ if (++rptr >= end_ptr) {
+ rptr = state;
+ }
+ }
+ }
+ return(i);
+}
+
+int
+get_rand(int x, int y)
+{
+ int r, t;
+ long lr;
+
+ if (x > y) {
+ t = y;
+ y = x;
+ x = t;
+ }
+ lr = rrandom();
+ lr &= 0x00003fffL;
+ r = (int)lr;
+ r = (r % ((y - x) + 1)) + x;
+ return(r);
+}
+
+int
+rand_percent(int percentage)
+{
+ return(get_rand(1, 100) <= percentage);
+}
+
+int
+coin_toss(void)
+{
+ return(((rrandom() & 01) ? 1 : 0));
+}
diff --git a/rogue/ring.c b/rogue/ring.c
new file mode 100644
index 0000000..5cdcf5f
--- /dev/null
+++ b/rogue/ring.c
@@ -0,0 +1,338 @@
+/* $NetBSD: ring.c,v 1.9 2008/01/14 03:50:02 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ring.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: ring.c,v 1.9 2008/01/14 03:50:02 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * ring.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+static const char left_or_right[] = "left or right hand?";
+static const char no_ring[] = "there's no ring on that hand";
+
+short stealthy;
+short r_rings;
+short add_strength;
+short e_rings;
+short regeneration;
+short ring_exp;
+short auto_search;
+boolean r_teleport;
+boolean r_see_invisible;
+boolean sustain_strength;
+boolean maintain_armor;
+
+void
+put_on_ring(void)
+{
+ short ch;
+ char desc[DCOLS];
+ object *ring;
+
+ if (r_rings == 2) {
+ messagef(0, "wearing two rings already");
+ return;
+ }
+ if ((ch = pack_letter("put on what?", RING)) == CANCEL) {
+ return;
+ }
+ if (!(ring = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (!(ring->what_is & RING)) {
+ messagef(0, "that's not a ring");
+ return;
+ }
+ if (ring->in_use_flags & (ON_LEFT_HAND | ON_RIGHT_HAND)) {
+ messagef(0, "that ring is already being worn");
+ return;
+ }
+ if (r_rings == 1) {
+ ch = (rogue.left_ring ? 'r' : 'l');
+ } else {
+ messagef(0, "%s", left_or_right);
+ do {
+ ch = rgetchar();
+ } while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') && (ch != '\n') &&
+ (ch != '\r'));
+ }
+ if ((ch != 'l') && (ch != 'r')) {
+ check_message();
+ return;
+ }
+ if (((ch == 'l') && rogue.left_ring)||((ch == 'r') && rogue.right_ring)) {
+ check_message();
+ messagef(0, "there's already a ring on that hand");
+ return;
+ }
+ if (ch == 'l') {
+ do_put_on(ring, 1);
+ } else {
+ do_put_on(ring, 0);
+ }
+ ring_stats(1);
+ check_message();
+ get_desc(ring, desc, sizeof(desc));
+ messagef(0, "%s", desc);
+ (void)reg_move();
+}
+
+/*
+ * Do not call ring_stats() from within do_put_on(). It will cause
+ * serious problems when do_put_on() is called from read_pack() in restore().
+ */
+
+void
+do_put_on(object *ring, boolean on_left)
+{
+ if (on_left) {
+ ring->in_use_flags |= ON_LEFT_HAND;
+ rogue.left_ring = ring;
+ } else {
+ ring->in_use_flags |= ON_RIGHT_HAND;
+ rogue.right_ring = ring;
+ }
+}
+
+void
+remove_ring(void)
+{
+ boolean left = 0, right = 0;
+ short ch;
+ char buf[DCOLS];
+ object *ring;
+
+ ring = NULL;
+ if (r_rings == 0) {
+ inv_rings();
+ } else if (rogue.left_ring && !rogue.right_ring) {
+ left = 1;
+ } else if (!rogue.left_ring && rogue.right_ring) {
+ right = 1;
+ } else {
+ messagef(0, "%s", left_or_right);
+ do {
+ ch = rgetchar();
+ } while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') &&
+ (ch != '\n') && (ch != '\r'));
+ left = (ch == 'l');
+ right = (ch == 'r');
+ check_message();
+ }
+ if (left || right) {
+ if (left) {
+ if (rogue.left_ring) {
+ ring = rogue.left_ring;
+ } else {
+ messagef(0, "%s", no_ring);
+ }
+ } else {
+ if (rogue.right_ring) {
+ ring = rogue.right_ring;
+ } else {
+ messagef(0, "%s", no_ring);
+ }
+ }
+ if (ring->is_cursed) {
+ messagef(0, "%s", curse_message);
+ } else {
+ un_put_on(ring);
+ get_desc(ring, buf, sizeof(buf));
+ messagef(0, "removed %s", buf);
+ (void)reg_move();
+ }
+ }
+}
+
+void
+un_put_on(object *ring)
+{
+ if (ring && (ring->in_use_flags & ON_LEFT_HAND)) {
+ ring->in_use_flags &= (~ON_LEFT_HAND);
+ rogue.left_ring = NULL;
+ } else if (ring && (ring->in_use_flags & ON_RIGHT_HAND)) {
+ ring->in_use_flags &= (~ON_RIGHT_HAND);
+ rogue.right_ring = NULL;
+ }
+ ring_stats(1);
+}
+
+void
+gr_ring(object *ring, boolean assign_wk)
+{
+ ring->what_is = RING;
+ if (assign_wk) {
+ ring->which_kind = get_rand(0, (RINGS - 1));
+ }
+ ring->class = 0;
+
+ switch(ring->which_kind) {
+ /*
+ case STEALTH:
+ break;
+ case SLOW_DIGEST:
+ break;
+ case REGENERATION:
+ break;
+ case R_SEE_INVISIBLE:
+ break;
+ case SUSTAIN_STRENGTH:
+ break;
+ case R_MAINTAIN_ARMOR:
+ break;
+ case SEARCHING:
+ break;
+ */
+ case R_TELEPORT:
+ ring->is_cursed = 1;
+ break;
+ case ADD_STRENGTH:
+ case DEXTERITY:
+ while ((ring->class = (get_rand(0, 4) - 2)) == 0)
+ ;
+ ring->is_cursed = (ring->class < 0);
+ break;
+ case ADORNMENT:
+ ring->is_cursed = coin_toss();
+ break;
+ }
+}
+
+void
+inv_rings(void)
+{
+ char buf[DCOLS];
+
+ if (r_rings == 0) {
+ messagef(0, "not wearing any rings");
+ } else {
+ if (rogue.left_ring) {
+ get_desc(rogue.left_ring, buf, sizeof(buf));
+ messagef(0, "%s", buf);
+ }
+ if (rogue.right_ring) {
+ get_desc(rogue.right_ring, buf, sizeof(buf));
+ messagef(0, "%s", buf);
+ }
+ }
+ if (wizard) {
+ messagef(0, "ste %d, r_r %d, e_r %d, r_t %d, s_s %d, a_s %d, reg %d, r_e %d, s_i %d, m_a %d, aus %d",
+ stealthy, r_rings, e_rings, r_teleport, sustain_strength,
+ add_strength, regeneration, ring_exp, r_see_invisible,
+ maintain_armor, auto_search);
+ }
+}
+
+void
+ring_stats(boolean pr)
+{
+ short i;
+ object *ring;
+
+ stealthy = 0;
+ r_rings = 0;
+ e_rings = 0;
+ r_teleport = 0;
+ sustain_strength = 0;
+ add_strength = 0;
+ regeneration = 0;
+ ring_exp = 0;
+ r_see_invisible = 0;
+ maintain_armor = 0;
+ auto_search = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (!(ring = ((i == 0) ? rogue.left_ring : rogue.right_ring))) {
+ continue;
+ }
+ r_rings++;
+ e_rings++;
+ switch(ring->which_kind) {
+ case STEALTH:
+ stealthy++;
+ break;
+ case R_TELEPORT:
+ r_teleport = 1;
+ break;
+ case REGENERATION:
+ regeneration++;
+ break;
+ case SLOW_DIGEST:
+ e_rings -= 2;
+ break;
+ case ADD_STRENGTH:
+ add_strength += ring->class;
+ break;
+ case SUSTAIN_STRENGTH:
+ sustain_strength = 1;
+ break;
+ case DEXTERITY:
+ ring_exp += ring->class;
+ break;
+ case ADORNMENT:
+ break;
+ case R_SEE_INVISIBLE:
+ r_see_invisible = 1;
+ break;
+ case MAINTAIN_ARMOR:
+ maintain_armor = 1;
+ break;
+ case SEARCHING:
+ auto_search += 2;
+ break;
+ }
+ }
+ if (pr) {
+ print_stats(STAT_STRENGTH);
+ relight();
+ }
+}
diff --git a/rogue/rogue.6 b/rogue/rogue.6
new file mode 100644
index 0000000..4598c7b
--- /dev/null
+++ b/rogue/rogue.6
@@ -0,0 +1,107 @@
+.\" $NetBSD: rogue.6,v 1.11 2005/09/15 02:09:41 wiz Exp $
+.\"
+.\" 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.
+.\"
+.\" @(#)rogue.6 8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt ROGUE 6
+.Os
+.Sh NAME
+.Nm rogue
+.Nd exploring The Dungeons of Doom
+.Sh SYNOPSIS
+.Nm
+.Op Fl s
+.Op Ar save_file
+.\" .Op Fl r
+.\" .Op Fl d
+.Sh DESCRIPTION
+.Nm
+is a computer fantasy game with a new twist.
+It is CRT oriented and the object of the game is to survive the attacks of
+various monsters and get a lot of gold, rather than the puzzle solving
+orientation of most computer fantasy games.
+.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, they 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 (by
+some miracle) manage to win,
+.Nm
+will give you a list of the top-ten scorers.
+The scoring is based entirely upon how much gold you get.
+There is a 10% penalty for getting yourself killed.
+.Pp
+If
+.Ar save_file
+is specified,
+rogue will be restored from the specified saved game file.
+.Pp
+The
+.Fl s
+option will print out the list of scores.
+.Pp
+For more detailed directions, read the document
+.Rs
+.%T A Guide to the Dungeons of Doom
+.Re
+.Sh FILES
+.Bl -tag -width /var/games/rogue.scores -compact
+.It Pa /var/games/rogue.scores
+Score file
+.It Pa ~/rogue.save
+Default save file
+.El
+.Sh SEE ALSO
+.Rs
+.%A Michael C. Toy
+.%A Kenneth C. R. C. Arnold
+.%T A guide to the Dungeons of Doom
+.Re
+.Sh AUTHORS
+.An Timothy Stoehr
+.An Michael C. Toy
+.An Kenneth C. R. C. Arnold
+.An Glenn Wichman
+.Sh BUGS
+Probably infinite, although none are known.
+However, that Ice Monsters sometimes transfix you permanently is
+.Em not
+a bug.
+It's a feature.
diff --git a/rogue/rogue.h b/rogue/rogue.h
new file mode 100644
index 0000000..8ce9411
--- /dev/null
+++ b/rogue/rogue.h
@@ -0,0 +1,702 @@
+/* $NetBSD: rogue.h,v 1.24 2013/08/11 03:44:27 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ *
+ * @(#)rogue.h 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * rogue.h
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) This notice shall not be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ */
+
+#define boolean char
+
+#define NOTHING ((unsigned short) 0)
+#define OBJECT ((unsigned short) 01)
+#define MONSTER ((unsigned short) 02)
+#define STAIRS ((unsigned short) 04)
+#define HORWALL ((unsigned short) 010)
+#define VERTWALL ((unsigned short) 020)
+#define DOOR ((unsigned short) 040)
+#define FLOOR ((unsigned short) 0100)
+#define TUNNEL ((unsigned short) 0200)
+#define TRAP ((unsigned short) 0400)
+#define HIDDEN ((unsigned short) 01000)
+
+#define ARMOR ((unsigned short) 01)
+#define WEAPON ((unsigned short) 02)
+#define SCROL ((unsigned short) 04)
+#define POTION ((unsigned short) 010)
+#define GOLD ((unsigned short) 020)
+#define FOOD ((unsigned short) 040)
+#define WAND ((unsigned short) 0100)
+#define RING ((unsigned short) 0200)
+#define AMULET ((unsigned short) 0400)
+#define ALL_OBJECTS ((unsigned short) 0777)
+
+#define LEATHER 0
+#define RINGMAIL 1
+#define SCALE 2
+#define CHAIN 3
+#define BANDED 4
+#define SPLINT 5
+#define PLATE 6
+#define ARMORS 7
+
+#define BOW 0
+#define DART 1
+#define ARROW 2
+#define DAGGER 3
+#define SHURIKEN 4
+#define MACE 5
+#define LONG_SWORD 6
+#define TWO_HANDED_SWORD 7
+#define WEAPONS 8
+
+#define MAX_PACK_COUNT 24
+
+#define PROTECT_ARMOR 0
+#define HOLD_MONSTER 1
+#define ENCH_WEAPON 2
+#define ENCH_ARMOR 3
+#define IDENTIFY 4
+#define TELEPORT 5
+#define SLEEP 6
+#define SCARE_MONSTER 7
+#define REMOVE_CURSE 8
+#define CREATE_MONSTER 9
+#define AGGRAVATE_MONSTER 10
+#define MAGIC_MAPPING 11
+#define CON_MON 12
+#define SCROLS 13
+
+#define INCREASE_STRENGTH 0
+#define RESTORE_STRENGTH 1
+#define HEALING 2
+#define EXTRA_HEALING 3
+#define POISON 4
+#define RAISE_LEVEL 5
+#define BLINDNESS 6
+#define HALLUCINATION 7
+#define DETECT_MONSTER 8
+#define DETECT_OBJECTS 9
+#define CONFUSION 10
+#define LEVITATION 11
+#define HASTE_SELF 12
+#define SEE_INVISIBLE 13
+#define POTIONS 14
+
+#define TELE_AWAY 0
+#define SLOW_MONSTER 1
+#define INVISIBILITY 2
+#define POLYMORPH 3
+#define HASTE_MONSTER 4
+#define MAGIC_MISSILE 5
+#define CANCELLATION 6
+#define DO_NOTHING 7
+#define DRAIN_LIFE 8
+#define COLD 9
+#define FIRE 10
+#define WANDS 11
+
+#define STEALTH 0
+#define R_TELEPORT 1
+#define REGENERATION 2
+#define SLOW_DIGEST 3
+#define ADD_STRENGTH 4
+#define SUSTAIN_STRENGTH 5
+#define DEXTERITY 6
+#define ADORNMENT 7
+#define R_SEE_INVISIBLE 8
+#define MAINTAIN_ARMOR 9
+#define SEARCHING 10
+#define RINGS 11
+
+#define RATION 0
+#define FRUIT 1
+
+#define NOT_USED ((unsigned short) 0)
+#define BEING_WIELDED ((unsigned short) 01)
+#define BEING_WORN ((unsigned short) 02)
+#define ON_LEFT_HAND ((unsigned short) 04)
+#define ON_RIGHT_HAND ((unsigned short) 010)
+#define ON_EITHER_HAND ((unsigned short) 014)
+#define BEING_USED ((unsigned short) 017)
+
+#define NO_TRAP -1
+#define TRAP_DOOR 0
+#define BEAR_TRAP 1
+#define TELE_TRAP 2
+#define DART_TRAP 3
+#define SLEEPING_GAS_TRAP 4
+#define RUST_TRAP 5
+#define TRAPS 6
+
+#define STEALTH_FACTOR 3
+#define R_TELE_PERCENT 8
+
+#define UNIDENTIFIED ((unsigned short) 00) /* MUST BE ZERO! */
+#define IDENTIFIED ((unsigned short) 01)
+#define CALLED ((unsigned short) 02)
+
+#define DROWS 24
+#define DCOLS 80
+#define NMESSAGES 5
+#define MAX_TITLE_LENGTH 30
+#define MAXSYLLABLES 40
+#define MAX_METAL 14
+#define WAND_MATERIALS 30
+#define GEMS 14
+
+#define GOLD_PERCENT 46
+
+#define MAX_OPT_LEN 40
+
+#define MAX_ID_TITLE_LEN 64
+struct id {
+ short value;
+ char title[MAX_ID_TITLE_LEN];
+ const char *real;
+ unsigned short id_status;
+};
+
+/* The following #defines provide more meaningful names for some of the
+ * struct object fields that are used for monsters. This, since each monster
+ * and object (scrolls, potions, etc) are represented by a struct object.
+ * Ideally, this should be handled by some kind of union structure.
+ */
+
+#define m_damage damage
+#define hp_to_kill quantity
+#define m_char ichar
+#define first_level is_protected
+#define last_level is_cursed
+#define m_hit_chance class
+#define stationary_damage identified
+#define drop_percent which_kind
+#define trail_char d_enchant
+#define slowed_toggle quiver
+#define moves_confused hit_enchant
+#define nap_length picked_up
+#define disguise what_is
+#define next_monster next_object
+
+struct obj { /* comment is monster meaning */
+ unsigned long m_flags; /* monster flags */
+ const char *damage; /* damage it does */
+ short quantity; /* hit points to kill */
+ short ichar; /* 'A' is for aquator */
+ short kill_exp; /* exp for killing it */
+ short is_protected; /* level starts */
+ short is_cursed; /* level ends */
+ short class; /* chance of hitting you */
+ short identified; /* 'F' damage, 1,2,3... */
+ unsigned short which_kind; /* item carry/drop % */
+ short o_row, o_col, o; /* o is how many times stuck at o_row, o_col */
+ short row, col; /* current row, col */
+ short d_enchant; /* room char when detect_monster */
+ short quiver; /* monster slowed toggle */
+ short trow, tcol; /* target row, col */
+ short hit_enchant; /* how many moves is confused */
+ unsigned short what_is; /* imitator's charactor (?!%: */
+ short picked_up; /* sleep from wand of sleep */
+ unsigned short in_use_flags;
+ struct obj *next_object; /* next monster */
+};
+
+typedef struct obj object;
+
+#define INIT_AW NULL
+#define INIT_RINGS NULL
+#define INIT_HP 12
+#define INIT_STR 16
+#define INIT_EXPLEVEL 1
+#define INIT_EXP 0
+#define INIT_PACK {0,NULL,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL}
+#define INIT_GOLD 0
+#define INIT_CHAR '@'
+#define INIT_MOVES 1250
+
+struct fightr {
+ object *armor;
+ object *weapon;
+ object *left_ring, *right_ring;
+ short hp_current;
+ short hp_max;
+ short str_current;
+ short str_max;
+ object pack;
+ long gold;
+ short exp;
+ long exp_points;
+ short row, col;
+ short fchar;
+ short moves_left;
+};
+
+typedef struct fightr fighter;
+
+struct dr {
+ short oth_room;
+ short oth_row,
+ oth_col;
+ short door_row,
+ door_col;
+};
+
+typedef struct dr door;
+
+struct rm {
+ short bottom_row, right_col, left_col, top_row;
+ door doors[4];
+ unsigned short is_room;
+};
+
+typedef struct rm room;
+
+#define MAXROOMS 9
+#define BIG_ROOM 10
+
+#define NO_ROOM (-1)
+
+#define PASSAGE (-3) /* cur_room value */
+
+#define AMULET_LEVEL 26
+
+#define R_NOTHING ((unsigned short) 01)
+#define R_ROOM ((unsigned short) 02)
+#define R_MAZE ((unsigned short) 04)
+#define R_DEADEND ((unsigned short) 010)
+#define R_CROSS ((unsigned short) 020)
+
+#define MAX_EXP_LEVEL 21
+#define MAX_EXP 10000001L
+#define MAX_GOLD 999999
+#define MAX_ARMOR 99
+#define MAX_HP 999
+#define MAX_STRENGTH 99
+#define LAST_DUNGEON 99
+
+#define STAT_LEVEL 01
+#define STAT_GOLD 02
+#define STAT_HP 04
+#define STAT_STRENGTH 010
+#define STAT_ARMOR 020
+#define STAT_EXP 040
+#define STAT_HUNGER 0100
+#define STAT_LABEL 0200
+#define STAT_ALL 0377
+
+#define PARTY_TIME 10 /* one party somewhere in each 10 level span */
+
+#define MAX_TRAPS 10 /* maximum traps per level */
+
+#define HIDE_PERCENT 12
+
+struct tr {
+ short trap_type;
+ short trap_row, trap_col;
+};
+
+typedef struct tr trap;
+
+extern fighter rogue;
+extern room rooms[];
+extern trap traps[];
+extern unsigned short dungeon[DROWS][DCOLS];
+extern object level_objects;
+
+extern struct id id_scrolls[];
+extern struct id id_potions[];
+extern struct id id_wands[];
+extern struct id id_rings[];
+extern struct id id_weapons[];
+extern struct id id_armors[];
+
+extern object level_monsters;
+
+#define MONSTERS 26
+
+#define HASTED 01L
+#define SLOWED 02L
+#define INVISIBLE 04L
+#define ASLEEP 010L
+#define WAKENS 020L
+#define WANDERS 040L
+#define FLIES 0100L
+#define FLITS 0200L
+#define CAN_FLIT 0400L /* can, but usually doesn't, flit */
+#define CONFUSED 01000L
+#define RUSTS 02000L
+#define HOLDS 04000L
+#define FREEZES 010000L
+#define STEALS_GOLD 020000L
+#define STEALS_ITEM 040000L
+#define STINGS 0100000L
+#define DRAINS_LIFE 0200000L
+#define DROPS_LEVEL 0400000L
+#define SEEKS_GOLD 01000000L
+#define FREEZING_ROGUE 02000000L
+#define RUST_VANISHED 04000000L
+#define CONFUSES 010000000L
+#define IMITATES 020000000L
+#define FLAMES 040000000L
+#define STATIONARY 0100000000L /* damage will be 1,2,3,... */
+#define NAPPING 0200000000L /* can't wake up for a while */
+#define ALREADY_MOVED 0400000000L
+
+#define SPECIAL_HIT (RUSTS|HOLDS|FREEZES|STEALS_GOLD|STEALS_ITEM|STINGS|DRAINS_LIFE|DROPS_LEVEL)
+
+#define WAKE_PERCENT 45
+#define FLIT_PERCENT 40
+#define PARTY_WAKE_PERCENT 75
+
+#define HYPOTHERMIA 1
+#define STARVATION 2
+#define POISON_DART 3
+#define QUIT 4
+#define WIN 5
+#define KFIRE 6
+
+#define UPWARD 0
+#define UPRIGHT 1
+#define RIGHT 2
+#define DOWNRIGHT 3
+#define DOWN 4
+#define DOWNLEFT 5
+#define LEFT 6
+#define UPLEFT 7
+#define DIRS 8
+
+#define ROW1 7
+#define ROW2 15
+
+#define COL1 26
+#define COL2 52
+
+#define MOVED 0
+#define MOVE_FAILED -1
+#define STOPPED_ON_SOMETHING -2
+#define CANCEL '\033'
+#define LIST '*'
+
+#define HUNGRY 300
+#define WEAK 150
+#define FAINT 20
+#define STARVE 0
+
+#define MIN_ROW 1
+
+struct rogue_time {
+ short year; /* >= 1987 */
+ short month; /* 1 - 12 */
+ short day; /* 1 - 31 */
+ short hour; /* 0 - 23 */
+ short minute; /* 0 - 59 */
+ short second; /* 0 - 59 */
+};
+
+#include <curses.h>
+
+/*
+ * external routine declarations.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+object *alloc_object(void);
+object *get_letter_object(int);
+object *gr_monster(object *, int);
+object *gr_object(void);
+char *md_getenv(const char *);
+const char *
+ md_gln(void);
+void *md_malloc(size_t);
+const char *mon_name(const object *);
+const char *name_of(const object *);
+object *object_at(object *, short, short);
+object *pick_up(int, int, short *);
+void add_exp(int, boolean);
+void add_traps(void);
+void aggravate(void);
+void bounce(short, short, short, short, short);
+void byebye(int);
+void c_object_for_wizard(void);
+void call_it(void);
+boolean can_move(int, int, int, int);
+void check_gold_seeker(object *);
+boolean check_imitator(object *);
+void check_message(void);
+int check_up(void);
+void clean_up(const char *) __dead;
+void clear_level(void);
+void cnfs(void);
+int coin_toss(void);
+void cough_up(object *);
+void create_monster(void);
+void darken_room(short);
+void do_put_on(object *, boolean);
+void do_shell(void);
+void do_wear(object *);
+void do_wield(object *);
+void dr_course(object *, boolean, short, short);
+void draw_magic_map(void);
+void drop(void);
+int drop_check(void);
+void eat(void);
+void edit_opts(void);
+void error_save(int) __dead;
+void fight(boolean);
+boolean flame_broil(object *);
+void free_object(object *);
+void free_stuff(object *);
+int get_armor_class(const object *);
+int get_damage(const char *, boolean);
+void get_desc(const object *, char *, size_t);
+void get_dir_rc(short, short *, short *, short);
+char get_dungeon_char(short, short);
+void get_food(object *, boolean);
+int get_hit_chance(const object *);
+int get_input_line(const char *, const char *, char *, size_t, const char *, boolean, boolean);
+char get_mask_char(unsigned short);
+int get_number(const char *);
+int get_rand(int, int);
+short get_room_number(int, int);
+void get_wand_and_ring_materials(void);
+int get_weapon_damage(const object *);
+char gmc(object *);
+char gmc_row_col(int, int);
+char gr_obj_char(void);
+void gr_ring(object *, boolean);
+short gr_room(void);
+void gr_row_col(short *, short *, unsigned short);
+void hallucinate(void);
+boolean has_amulet(void);
+int hp_raise(void);
+void id_com(void);
+void id_trap(void);
+void id_type(void);
+boolean imitating(short, short);
+int init(int, char **);
+void insert_score(char [][82], char [][30], const char *, short, short, const object *, int);
+void inv_armor_weapon(boolean);
+void inv_rings(void);
+void inventory(const object *, unsigned short);
+boolean is_all_connected(void);
+boolean is_digit(int);
+boolean is_direction(short, short *);
+boolean is_passable(int, int);
+boolean is_vowel(short);
+void kick_into_pack(void);
+void killed_by(const object *, short) __dead;
+long lget_number(const char *);
+void light_passage(int, int);
+void light_up_room(int);
+boolean m_confuse(object *);
+void make_level(void);
+void make_scroll_titles(void);
+boolean md_df(const char *);
+void md_exit(int) __dead;
+void md_gct(struct rogue_time *);
+int md_get_file_id(const char *);
+void md_gfmt(const char *, struct rogue_time *);
+int md_gseed(void);
+void md_heed_signals(void);
+void md_ignore_signals(void);
+int md_link_count(const char *);
+void md_lock(boolean);
+void md_shell(const char *);
+void md_sleep(int);
+void md_slurp(void);
+/*void message(const char *, boolean);*/
+void messagef(boolean, const char *, ...) __printflike(2, 3);
+void mix_colors(void);
+int mon_can_go(const object *, short, short);
+int mon_damage(object *, short);
+void mon_hit(object *);
+boolean mon_sees(const object *, int, int);
+void move_mon_to(object *, short, short);
+void move_onto(void);
+void multiple_move_rogue(short);
+void mv_1_monster(object *, short, short);
+void mv_aquatars(void);
+void mv_mons(void);
+int name_cmp(char *, const char *);
+void nickize(char *, const char *, const char *);
+int one_move_rogue(short, short);
+void onintr(int);
+short pack_count(const object *);
+short pack_letter(const char *, unsigned short);
+void pad(const char *, short);
+void party_monsters(int, int);
+short party_objects(int);
+void place_at(object *, int, int);
+void play_level(void);
+void print_stats(int);
+void put_amulet(void);
+void put_mons(void);
+void put_objects(void);
+void put_on_ring(void);
+void put_player(short);
+void put_scores(const object *, short) __dead;
+void put_stairs(void);
+void quaff(void);
+void quit(boolean);
+int r_index(const char *, int, boolean);
+void rand_around(short, short *, short *);
+int rand_percent(int);
+void read_scroll(void);
+boolean reg_move(void);
+void relight(void);
+void remessage(short);
+void remove_ring(void);
+void rest(int);
+void restore(const char *);
+int rgetchar(void);
+void ring_stats(boolean);
+int rogue_can_see(int, int);
+void rogue_damage(short, object *, short);
+void rogue_hit(object *, boolean);
+void rust(object *);
+void s_con_mon(object *);
+void save_game(void);
+void save_into_file(const char *);
+void search(short, boolean);
+boolean seek_gold(object *);
+void show_average_hp(void);
+void show_monsters(void);
+void show_objects(void);
+void show_traps(void);
+void single_inv(short);
+void sound_bell(void);
+void special_hit(object *);
+void srrandom(int);
+void start_window(void);
+void stop_window(void);
+void take_a_nap(void);
+void take_from_pack(object *, object *);
+void take_off(void);
+void tele(void);
+void throw(void);
+void trap_player(short, short);
+void un_put_on(object *);
+void unblind(void);
+void unconfuse(void);
+void unhallucinate(void);
+void unwear(object *);
+void unwield(object *);
+void vanish(object *, short, object *);
+void wait_for_ack(void);
+void wake_room(short, boolean, short, short);
+void wake_up(object *);
+void wanderer(void);
+void wear(void);
+void wield(void);
+void win(void) __dead;
+void wizardize(void);
+long xxx(boolean);
+void xxxx(char *, short);
+void zapp(void);
+object *add_to_pack(object *, object *, int);
+struct id *get_id_table(const object *);
+
+extern boolean ask_quit;
+extern boolean being_held;
+extern boolean cant_int;
+extern boolean con_mon;
+extern boolean detect_monster;
+extern boolean did_int;
+extern boolean interrupted;
+extern boolean is_wood[];
+extern boolean jump;
+extern boolean maintain_armor;
+extern boolean mon_disappeared;
+extern boolean msg_cleared;
+extern boolean no_skull;
+extern boolean passgo;
+extern boolean r_see_invisible;
+extern boolean r_teleport;
+extern boolean save_is_interactive;
+extern boolean score_only;
+extern boolean see_invisible;
+extern boolean sustain_strength;
+extern boolean trap_door;
+extern boolean wizard;
+#define HIT_MESSAGE_SIZE 80
+extern char hit_message[HIT_MESSAGE_SIZE];
+#define HUNGER_STR_LEN 8
+extern char hunger_str[HUNGER_STR_LEN];
+extern char login_name[MAX_OPT_LEN];
+extern const char *byebye_string;
+extern const char curse_message[];
+extern const char *error_file;
+extern char *fruit;
+extern const char *const m_names[];
+extern const char *more;
+extern const char *new_level_message;
+extern char *nick_name;
+extern const char *press_space;
+extern char *save_file;
+extern const char you_can_move_again[];
+extern const long level_points[];
+extern short add_strength;
+extern short auto_search;
+extern short bear_trap;
+extern short blind;
+extern short confused;
+extern short cur_level;
+extern short cur_room;
+extern short e_rings;
+extern short extra_hp;
+extern short foods;
+extern short halluc;
+extern short haste_self;
+extern short less_hp;
+extern short levitate;
+extern short m_moves;
+extern short max_level;
+extern short party_room;
+extern short r_rings;
+extern short regeneration;
+extern short ring_exp;
+extern short stealthy;
+extern gid_t gid;
+extern gid_t egid;
diff --git a/rogue/room.c b/rogue/room.c
new file mode 100644
index 0000000..0071489
--- /dev/null
+++ b/rogue/room.c
@@ -0,0 +1,671 @@
+/* $NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)room.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * room.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+room rooms[MAXROOMS];
+
+static boolean rooms_visited[MAXROOMS];
+
+#define NOPTS 7
+static const struct option {
+ const char *prompt;
+ boolean is_bool;
+ char **strval;
+ boolean *bval;
+} options[NOPTS] = {
+ {
+ "Show position only at end of run (\"jump\"): ",
+ 1, NULL, &jump
+ },
+ {
+ "Follow turnings in passageways (\"passgo\"): ",
+ 1, NULL, &passgo
+ },
+ {
+ "Don't print skull when killed (\"noskull\" or \"notombstone\"): ",
+ 1, NULL, &no_skull
+ },
+ {
+ "Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ",
+ 1, NULL, &ask_quit
+ },
+ {
+ "Name (\"name\"): ",
+ 0, &nick_name, NULL
+ },
+ {
+ "Fruit (\"fruit\"): ",
+ 0, &fruit, NULL
+ },
+ {
+ "Save file (\"file\"): ",
+ 0, &save_file, NULL
+ }
+};
+
+static boolean get_oth_room(short, short *, short *);
+static void opt_erase(int);
+static void opt_go(int);
+static void opt_show(int);
+static void visit_rooms(int);
+
+void
+light_up_room(int rn)
+{
+ short i, j;
+
+ if (!blind) {
+ for (i = rooms[rn].top_row;
+ i <= rooms[rn].bottom_row; i++) {
+ for (j = rooms[rn].left_col;
+ j <= rooms[rn].right_col; j++) {
+ if (dungeon[i][j] & MONSTER) {
+ object *monster;
+
+ if ((monster = object_at(
+ &level_monsters, i, j)) != NULL) {
+ dungeon[monster->row][monster->col] &= (~MONSTER);
+ monster->trail_char =
+ get_dungeon_char(monster->row, monster->col);
+ dungeon[monster->row][monster->col] |= MONSTER;
+ }
+ }
+ mvaddch(i, j, get_dungeon_char(i, j));
+ }
+ }
+ mvaddch(rogue.row, rogue.col, rogue.fchar);
+ }
+}
+
+void
+light_passage(int row, int col)
+{
+ short i, j, i_end, j_end;
+
+ if (blind) {
+ return;
+ }
+ i_end = (row < (DROWS-2)) ? 1 : 0;
+ j_end = (col < (DCOLS-1)) ? 1 : 0;
+
+ for (i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
+ for (j = ((col > 0) ? -1 : 0); j <= j_end; j++) {
+ if (can_move(row, col, row+i, col+j)) {
+ mvaddch(row+i, col+j, get_dungeon_char(row+i, col+j));
+ }
+ }
+ }
+}
+
+void
+darken_room(short rn)
+{
+ short i, j;
+
+ for (i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) {
+ for (j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) {
+ if (blind) {
+ mvaddch(i, j, ' ');
+ } else {
+ if (!(dungeon[i][j] & (OBJECT | STAIRS)) &&
+ !(detect_monster && (dungeon[i][j] & MONSTER))) {
+ if (!imitating(i, j)) {
+ mvaddch(i, j, ' ');
+ }
+ if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) {
+ mvaddch(i, j, '^');
+ }
+ }
+ }
+ }
+ }
+}
+
+char
+get_dungeon_char(short row, short col)
+{
+ unsigned short mask = dungeon[row][col];
+
+ if (mask & MONSTER) {
+ return(gmc_row_col(row, col));
+ }
+ if (mask & OBJECT) {
+ object *obj;
+
+ obj = object_at(&level_objects, row, col);
+ return(get_mask_char(obj->what_is));
+ }
+ if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) {
+ if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) {
+ return(((mask & STAIRS) ? '%' : '#'));
+ }
+ if (mask & HORWALL) {
+ return('-');
+ }
+ if (mask & VERTWALL) {
+ return('|');
+ }
+ if (mask & FLOOR) {
+ if (mask & TRAP) {
+ if (!(dungeon[row][col] & HIDDEN)) {
+ return('^');
+ }
+ }
+ return('.');
+ }
+ if (mask & DOOR) {
+ if (mask & HIDDEN) {
+ if (((col > 0) && (dungeon[row][col-1] & HORWALL)) ||
+ ((col < (DCOLS-1)) && (dungeon[row][col+1] & HORWALL))) {
+ return('-');
+ } else {
+ return('|');
+ }
+ } else {
+ return('+');
+ }
+ }
+ }
+ return(' ');
+}
+
+char
+get_mask_char(unsigned short mask)
+{
+ switch(mask) {
+ case SCROL:
+ return('?');
+ case POTION:
+ return('!');
+ case GOLD:
+ return('*');
+ case FOOD:
+ return(':');
+ case WAND:
+ return('/');
+ case ARMOR:
+ return(']');
+ case WEAPON:
+ return(')');
+ case RING:
+ return('=');
+ case AMULET:
+ return(',');
+ default:
+ return('~'); /* unknown, something is wrong */
+ }
+}
+
+void
+gr_row_col(short *row, short *col, unsigned short mask)
+{
+ short rn;
+ short r, c;
+
+ do {
+ r = get_rand(MIN_ROW, DROWS-2);
+ c = get_rand(0, DCOLS-1);
+ rn = get_room_number(r, c);
+ } while ((rn == NO_ROOM) ||
+ (!(dungeon[r][c] & mask)) ||
+ (dungeon[r][c] & (~mask)) ||
+ (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) ||
+ ((r == rogue.row) && (c == rogue.col)));
+
+ *row = r;
+ *col = c;
+}
+
+short
+gr_room(void)
+{
+ short i;
+
+ do {
+ i = get_rand(0, MAXROOMS-1);
+ } while (!(rooms[i].is_room & (R_ROOM | R_MAZE)));
+
+ return(i);
+}
+
+short
+party_objects(int rn)
+{
+ short i, j, nf = 0;
+ object *obj;
+ short n, N, row, col;
+ boolean found;
+
+ row = col = 0;
+ N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) *
+ ((rooms[rn].right_col - rooms[rn].left_col) - 1);
+ n = get_rand(5, 10);
+ if (n > N) {
+ n = N - 2;
+ }
+ for (i = 0; i < n; i++) {
+ for (j = found = 0; ((!found) && (j < 250)); j++) {
+ row = get_rand(rooms[rn].top_row+1,
+ rooms[rn].bottom_row-1);
+ col = get_rand(rooms[rn].left_col+1,
+ rooms[rn].right_col-1);
+ if ((dungeon[row][col] == FLOOR) || (dungeon[row][col] == TUNNEL)) {
+ found = 1;
+ }
+ }
+ if (found) {
+ obj = gr_object();
+ place_at(obj, row, col);
+ nf++;
+ }
+ }
+ return(nf);
+}
+
+short
+get_room_number(int row, int col)
+{
+ short i;
+
+ for (i = 0; i < MAXROOMS; i++) {
+ if ((row >= rooms[i].top_row) && (row <= rooms[i].bottom_row) &&
+ (col >= rooms[i].left_col) && (col <= rooms[i].right_col)) {
+ return(i);
+ }
+ }
+ return(NO_ROOM);
+}
+
+boolean
+is_all_connected(void)
+{
+ short i, starting_room;
+
+ starting_room = 0;
+ for (i = 0; i < MAXROOMS; i++) {
+ rooms_visited[i] = 0;
+ if (rooms[i].is_room & (R_ROOM | R_MAZE)) {
+ starting_room = i;
+ }
+ }
+
+ visit_rooms(starting_room);
+
+ for (i = 0; i < MAXROOMS; i++) {
+ if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && (!rooms_visited[i])) {
+ return(0);
+ }
+ }
+ return(1);
+}
+
+static void
+visit_rooms(int rn)
+{
+ short i;
+ short oth_rn;
+
+ rooms_visited[rn] = 1;
+
+ for (i = 0; i < 4; i++) {
+ oth_rn = rooms[rn].doors[i].oth_room;
+ if ((oth_rn >= 0) && (!rooms_visited[oth_rn])) {
+ visit_rooms(oth_rn);
+ }
+ }
+}
+
+void
+draw_magic_map(void)
+{
+ short i, j, ch, och;
+ unsigned short mask = (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS |
+ MONSTER);
+ unsigned short s;
+
+ for (i = 0; i < DROWS; i++) {
+ for (j = 0; j < DCOLS; j++) {
+ s = dungeon[i][j];
+ if (s & mask) {
+ if (((ch = mvinch(i, j)) == ' ') ||
+ ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) {
+ och = ch;
+ dungeon[i][j] &= (~HIDDEN);
+ if (s & HORWALL) {
+ ch = '-';
+ } else if (s & VERTWALL) {
+ ch = '|';
+ } else if (s & DOOR) {
+ ch = '+';
+ } else if (s & TRAP) {
+ ch = '^';
+ } else if (s & STAIRS) {
+ ch = '%';
+ } else if (s & TUNNEL) {
+ ch = '#';
+ } else {
+ continue;
+ }
+ if ((!(s & MONSTER)) || (och == ' ')) {
+ addch(ch);
+ }
+ if (s & MONSTER) {
+ object *monster;
+
+ if ((monster = object_at(
+ &level_monsters, i, j))
+ != NULL) {
+ monster->trail_char =
+ ch;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+dr_course(object *monster, boolean entering, short row, short col)
+{
+ short i, j, k, rn;
+ short r, rr;
+
+ monster->row = row;
+ monster->col = col;
+
+ if (mon_sees(monster, rogue.row, rogue.col)) {
+ monster->trow = NO_ROOM;
+ return;
+ }
+ rn = get_room_number(row, col);
+
+ if (entering) { /* entering room */
+ /* look for door to some other room */
+ r = get_rand(0, MAXROOMS-1);
+ for (i = 0; i < MAXROOMS; i++) {
+ rr = (r + i) % MAXROOMS;
+ if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || (rr == rn)) {
+ continue;
+ }
+ for (k = 0; k < 4; k++) {
+ if (rooms[rr].doors[k].oth_room == rn) {
+ monster->trow = rooms[rr].doors[k].oth_row;
+ monster->tcol = rooms[rr].doors[k].oth_col;
+ if ((monster->trow == row) &&
+ (monster->tcol == col)) {
+ continue;
+ }
+ return;
+ }
+ }
+ }
+ /* look for door to dead end */
+ if (rn == NO_ROOM)
+ clean_up("dr_course: monster not in room");
+ for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) {
+ for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) {
+ if ((i != monster->row) && (j != monster->col) &&
+ (dungeon[i][j] & DOOR)) {
+ monster->trow = i;
+ monster->tcol = j;
+ return;
+ }
+ }
+ }
+ /* return monster to room that he came from */
+ for (i = 0; i < MAXROOMS; i++) {
+ for (j = 0; j < 4; j++) {
+ if (rooms[i].doors[j].oth_room == rn) {
+ for (k = 0; k < 4; k++) {
+ if (rooms[rn].doors[k].oth_room == i) {
+ monster->trow = rooms[rn].doors[k].oth_row;
+ monster->tcol = rooms[rn].doors[k].oth_col;
+ return;
+ }
+ }
+ }
+ }
+ }
+ /* no place to send monster */
+ monster->trow = NO_ROOM;
+ } else { /* exiting room */
+ if (rn == NO_ROOM || !get_oth_room(rn, &row, &col)) {
+ monster->trow = NO_ROOM;
+ } else {
+ monster->trow = row;
+ monster->tcol = col;
+ }
+ }
+}
+
+static boolean
+get_oth_room(short rn, short *row, short *col)
+{
+ short d = -1;
+
+ if (*row == rooms[rn].top_row) {
+ d = UPWARD/2;
+ } else if (*row == rooms[rn].bottom_row) {
+ d = DOWN/2;
+ } else if (*col == rooms[rn].left_col) {
+ d = LEFT/2;
+ } else if (*col == rooms[rn].right_col) {
+ d = RIGHT/2;
+ }
+ if ((d != -1) && (rooms[rn].doors[d].oth_room >= 0)) {
+ *row = rooms[rn].doors[d].oth_row;
+ *col = rooms[rn].doors[d].oth_col;
+ return(1);
+ }
+ return(0);
+}
+
+void
+edit_opts(void)
+{
+ char save[NOPTS+1][DCOLS];
+ short i, j;
+ short ch;
+ boolean done = 0;
+ char buf[MAX_OPT_LEN + 2];
+
+ for (i = 0; i < NOPTS+1; i++) {
+ for (j = 0; j < DCOLS; j++) {
+ save[i][j] = mvinch(i, j);
+ }
+ if (i < NOPTS) {
+ opt_show(i);
+ }
+ }
+ opt_go(0);
+ i = 0;
+
+ while (!done) {
+ refresh();
+ ch = rgetchar();
+CH:
+ switch(ch) {
+ case '\033':
+ done = 1;
+ break;
+ case '\012':
+ case '\015':
+ if (i == (NOPTS - 1)) {
+ mvaddstr(NOPTS, 0, press_space);
+ refresh();
+ wait_for_ack();
+ done = 1;
+ } else {
+ i++;
+ opt_go(i);
+ }
+ break;
+ case '-':
+ if (i > 0) {
+ opt_go(--i);
+ } else {
+ sound_bell();
+ }
+ break;
+ case 't':
+ case 'T':
+ case 'f':
+ case 'F':
+ if (options[i].is_bool) {
+ *(options[i].bval) = (((ch == 't') || (ch == 'T')) ? 1 : 0);
+ opt_show(i);
+ opt_go(++i);
+ break;
+ }
+ default:
+ if (options[i].is_bool) {
+ sound_bell();
+ break;
+ }
+ j = 0;
+ if ((ch == '\010') || ((ch >= ' ') && (ch <= '~'))) {
+ opt_erase(i);
+ do {
+ if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) {
+ buf[j++] = ch;
+ buf[j] = '\0';
+ addch(ch);
+ } else if ((ch == '\010') && (j > 0)) {
+ buf[--j] = '\0';
+ move(i, j + strlen(options[i].prompt));
+ addch(' ');
+ move(i, j + strlen(options[i].prompt));
+ }
+ refresh();
+ ch = rgetchar();
+ } while ((ch != '\012') && (ch != '\015') && (ch != '\033'));
+ if (j != 0) {
+ /*
+ * We rely on the option string being
+ * allocated to hold MAX_OPT_LEN+2
+ * bytes. This is arranged in init.c.
+ */
+ (void)strcpy(*(options[i].strval), buf);
+ }
+ opt_show(i);
+ goto CH;
+ } else {
+ sound_bell();
+ }
+ break;
+ }
+ }
+
+ for (i = 0; i < NOPTS+1; i++) {
+ move(i, 0);
+ for (j = 0; j < DCOLS; j++) {
+ addch(save[i][j]);
+ }
+ }
+}
+
+static void
+opt_show(int i)
+{
+ const char *s;
+ const struct option *opt = &options[i];
+
+ opt_erase(i);
+
+ if (opt->is_bool) {
+ s = *(opt->bval) ? "True" : "False";
+ } else {
+ s = *(opt->strval);
+ }
+ addstr(s);
+}
+
+static void
+opt_erase(int i)
+{
+ const struct option *opt = &options[i];
+
+ mvaddstr(i, 0, opt->prompt);
+ clrtoeol();
+}
+
+static void
+opt_go(int i)
+{
+ move(i, strlen(options[i].prompt));
+}
+
+void
+do_shell(void)
+{
+#ifdef UNIX
+ const char *sh;
+
+ md_ignore_signals();
+ if (!(sh = md_getenv("SHELL"))) {
+ sh = "/bin/sh";
+ }
+ move(LINES-1, 0);
+ refresh();
+ stop_window();
+ printf("\nCreating new shell...\n");
+ md_shell(sh);
+ start_window();
+ wrefresh(curscr);
+ md_heed_signals();
+#endif
+}
diff --git a/rogue/save.c b/rogue/save.c
new file mode 100644
index 0000000..4a7190a
--- /dev/null
+++ b/rogue/save.c
@@ -0,0 +1,427 @@
+/* $NetBSD: save.c,v 1.13 2008/01/14 03:50:02 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)save.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: save.c,v 1.13 2008/01/14 03:50:02 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * save.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include <stdio.h>
+#include "rogue.h"
+
+static boolean has_been_touched(const struct rogue_time *,
+ const struct rogue_time *);
+static void r_read(FILE *, void *, size_t);
+static void r_write(FILE *, const void *, size_t);
+static void read_pack(object *, FILE *, boolean);
+static void read_string(char *, FILE *, size_t);
+static void rw_dungeon(FILE *, boolean);
+static void rw_id(struct id *, FILE *, int, boolean);
+static void rw_rooms(FILE *, boolean);
+static void write_pack(const object *, FILE *);
+static void write_string(char *, FILE *);
+
+static short write_failed = 0;
+
+char *save_file = NULL;
+
+void
+save_game(void)
+{
+ char fname[64];
+
+ if (!get_input_line("file name?", save_file, fname, sizeof(fname),
+ "game not saved", 0, 1)) {
+ return;
+ }
+ check_message();
+ messagef(0, "%s", fname);
+ save_into_file(fname);
+}
+
+void
+save_into_file(const char *sfile)
+{
+ FILE *fp;
+ int file_id;
+ char *name_buffer;
+ size_t len;
+ char *hptr;
+ struct rogue_time rt_buf;
+
+ if (sfile[0] == '~') {
+ if ((hptr = md_getenv("HOME")) != NULL) {
+ len = strlen(hptr) + strlen(sfile);
+ name_buffer = md_malloc(len);
+ if (name_buffer == NULL) {
+ messagef(0,
+ "out of memory for save file name");
+ sfile = error_file;
+ } else {
+ (void)strcpy(name_buffer, hptr);
+ (void)strcat(name_buffer, sfile+1);
+ sfile = name_buffer;
+ }
+ /*
+ * Note: name_buffer gets leaked. But it's small,
+ * and in the common case we're about to exit.
+ */
+ }
+ }
+ if (((fp = fopen(sfile, "w")) == NULL) ||
+ ((file_id = md_get_file_id(sfile)) == -1)) {
+ if (fp)
+ fclose(fp);
+ messagef(0, "problem accessing the save file");
+ return;
+ }
+ md_ignore_signals();
+ write_failed = 0;
+ (void)xxx(1);
+ r_write(fp, &detect_monster, sizeof(detect_monster));
+ r_write(fp, &cur_level, sizeof(cur_level));
+ r_write(fp, &max_level, sizeof(max_level));
+ write_string(hunger_str, fp);
+ write_string(login_name, fp);
+ r_write(fp, &party_room, sizeof(party_room));
+ write_pack(&level_monsters, fp);
+ write_pack(&level_objects, fp);
+ r_write(fp, &file_id, sizeof(file_id));
+ rw_dungeon(fp, 1);
+ r_write(fp, &foods, sizeof(foods));
+ r_write(fp, &rogue, sizeof(fighter));
+ write_pack(&rogue.pack, fp);
+ rw_id(id_potions, fp, POTIONS, 1);
+ rw_id(id_scrolls, fp, SCROLS, 1);
+ rw_id(id_wands, fp, WANDS, 1);
+ rw_id(id_rings, fp, RINGS, 1);
+ r_write(fp, traps, (MAX_TRAPS * sizeof(trap)));
+ r_write(fp, is_wood, (WANDS * sizeof(boolean)));
+ r_write(fp, &cur_room, sizeof(cur_room));
+ rw_rooms(fp, 1);
+ r_write(fp, &being_held, sizeof(being_held));
+ r_write(fp, &bear_trap, sizeof(bear_trap));
+ r_write(fp, &halluc, sizeof(halluc));
+ r_write(fp, &blind, sizeof(blind));
+ r_write(fp, &confused, sizeof(confused));
+ r_write(fp, &levitate, sizeof(levitate));
+ r_write(fp, &haste_self, sizeof(haste_self));
+ r_write(fp, &see_invisible, sizeof(see_invisible));
+ r_write(fp, &detect_monster, sizeof(detect_monster));
+ r_write(fp, &wizard, sizeof(wizard));
+ r_write(fp, &score_only, sizeof(score_only));
+ r_write(fp, &m_moves, sizeof(m_moves));
+ md_gct(&rt_buf);
+ rt_buf.second += 10; /* allow for some processing time */
+ r_write(fp, &rt_buf, sizeof(rt_buf));
+ fclose(fp);
+
+ if (write_failed) {
+ (void)md_df(sfile); /* delete file */
+ } else {
+ clean_up("");
+ }
+}
+
+void
+restore(const char *fname)
+{
+ FILE *fp;
+ struct rogue_time saved_time, mod_time;
+ char buf[4];
+ char tbuf[MAX_OPT_LEN];
+ int new_file_id, saved_file_id;
+
+ fp = NULL;
+ if (((new_file_id = md_get_file_id(fname)) == -1) ||
+ ((fp = fopen(fname, "r")) == NULL)) {
+ clean_up("cannot open file");
+ }
+ if (md_link_count(fname) > 1) {
+ clean_up("file has link");
+ }
+ (void)xxx(1);
+ r_read(fp, &detect_monster, sizeof(detect_monster));
+ r_read(fp, &cur_level, sizeof(cur_level));
+ r_read(fp, &max_level, sizeof(max_level));
+ read_string(hunger_str, fp, sizeof hunger_str);
+
+ (void)strlcpy(tbuf, login_name, sizeof tbuf);
+ read_string(login_name, fp, sizeof login_name);
+ if (strcmp(tbuf, login_name)) {
+ clean_up("you're not the original player");
+ }
+
+ r_read(fp, &party_room, sizeof(party_room));
+ read_pack(&level_monsters, fp, 0);
+ read_pack(&level_objects, fp, 0);
+ r_read(fp, &saved_file_id, sizeof(saved_file_id));
+ if (new_file_id != saved_file_id) {
+ clean_up("sorry, saved game is not in the same file");
+ }
+ rw_dungeon(fp, 0);
+ r_read(fp, &foods, sizeof(foods));
+ r_read(fp, &rogue, sizeof(fighter));
+ read_pack(&rogue.pack, fp, 1);
+ rw_id(id_potions, fp, POTIONS, 0);
+ rw_id(id_scrolls, fp, SCROLS, 0);
+ rw_id(id_wands, fp, WANDS, 0);
+ rw_id(id_rings, fp, RINGS, 0);
+ r_read(fp, traps, (MAX_TRAPS * sizeof(trap)));
+ r_read(fp, is_wood, (WANDS * sizeof(boolean)));
+ r_read(fp, &cur_room, sizeof(cur_room));
+ rw_rooms(fp, 0);
+ r_read(fp, &being_held, sizeof(being_held));
+ r_read(fp, &bear_trap, sizeof(bear_trap));
+ r_read(fp, &halluc, sizeof(halluc));
+ r_read(fp, &blind, sizeof(blind));
+ r_read(fp, &confused, sizeof(confused));
+ r_read(fp, &levitate, sizeof(levitate));
+ r_read(fp, &haste_self, sizeof(haste_self));
+ r_read(fp, &see_invisible, sizeof(see_invisible));
+ r_read(fp, &detect_monster, sizeof(detect_monster));
+ r_read(fp, &wizard, sizeof(wizard));
+ r_read(fp, &score_only, sizeof(score_only));
+ r_read(fp, &m_moves, sizeof(m_moves));
+ r_read(fp, &saved_time, sizeof(saved_time));
+
+ if (fread(buf, 1, 1, fp) > 0) {
+ clear();
+ clean_up("extra characters in file");
+ }
+
+ md_gfmt(fname, &mod_time); /* get file modification time */
+
+ if (has_been_touched(&saved_time, &mod_time)) {
+ clear();
+ clean_up("sorry, file has been touched");
+ }
+ if ((!wizard) && !md_df(fname)) {
+ clean_up("cannot delete file");
+ }
+ msg_cleared = 0;
+ ring_stats(0);
+ fclose(fp);
+}
+
+static void
+write_pack(const object *pack, FILE *fp)
+{
+ object t;
+
+ while ((pack = pack->next_object) != NULL) {
+ r_write(fp, pack, sizeof(object));
+ }
+ t.ichar = t.what_is = 0;
+ r_write(fp, &t, sizeof(object));
+}
+
+static void
+read_pack(object *pack, FILE *fp, boolean is_rogue)
+{
+ object read_obj, *new_obj;
+
+ for (;;) {
+ r_read(fp, &read_obj, sizeof(object));
+ if (read_obj.ichar == 0) {
+ pack->next_object = NULL;
+ break;
+ }
+ new_obj = alloc_object();
+ *new_obj = read_obj;
+ if (is_rogue) {
+ if (new_obj->in_use_flags & BEING_WORN) {
+ do_wear(new_obj);
+ } else if (new_obj->in_use_flags & BEING_WIELDED) {
+ do_wield(new_obj);
+ } else if (new_obj->in_use_flags & (ON_EITHER_HAND)) {
+ do_put_on(new_obj,
+ ((new_obj->in_use_flags & ON_LEFT_HAND) ? 1 : 0));
+ }
+ }
+ pack->next_object = new_obj;
+ pack = new_obj;
+ }
+}
+
+static void
+rw_dungeon(FILE *fp, boolean rw)
+{
+ short i, j;
+ char buf[DCOLS];
+
+ for (i = 0; i < DROWS; i++) {
+ if (rw) {
+ r_write(fp, dungeon[i], (DCOLS * sizeof(dungeon[0][0])));
+ for (j = 0; j < DCOLS; j++) {
+ buf[j] = mvinch(i, j);
+ }
+ r_write(fp, buf, DCOLS);
+ } else {
+ r_read(fp, dungeon[i], (DCOLS * sizeof(dungeon[0][0])));
+ r_read(fp, buf, DCOLS);
+ for (j = 0; j < DCOLS; j++) {
+ mvaddch(i, j, buf[j]);
+ }
+ }
+ }
+}
+
+static void
+rw_id(struct id id_table[], FILE *fp, int n, boolean wr)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (wr) {
+ r_write(fp, &id_table[i].value, sizeof(short));
+ r_write(fp, &id_table[i].id_status,
+ sizeof(unsigned short));
+ write_string(id_table[i].title, fp);
+ } else {
+ r_read(fp, &id_table[i].value, sizeof(short));
+ r_read(fp, &id_table[i].id_status,
+ sizeof(unsigned short));
+ read_string(id_table[i].title, fp, MAX_ID_TITLE_LEN);
+ }
+ }
+}
+
+static void
+write_string(char *s, FILE *fp)
+{
+ short n;
+
+ n = strlen(s) + 1;
+ xxxx(s, n);
+ r_write(fp, &n, sizeof(short));
+ r_write(fp, s, n);
+}
+
+static void
+read_string(char *s, FILE *fp, size_t len)
+{
+ short n;
+
+ r_read(fp, &n, sizeof(short));
+ if (n<=0 || (size_t)(unsigned short)n > len) {
+ clean_up("read_string: corrupt game file");
+ }
+ r_read(fp, s, n);
+ xxxx(s, n);
+ /* ensure null termination */
+ s[n-1] = 0;
+}
+
+static void
+rw_rooms(FILE *fp, boolean rw)
+{
+ short i;
+
+ for (i = 0; i < MAXROOMS; i++) {
+ rw ? r_write(fp, (rooms + i), sizeof(room)) :
+ r_read(fp, (rooms + i), sizeof(room));
+ }
+}
+
+static void
+r_read(FILE *fp, void *buf, size_t n)
+{
+ if (fread(buf, 1, n, fp) != n) {
+ clean_up("fread() failed, don't know why");
+ }
+}
+
+static void
+r_write(FILE *fp, const void *buf, size_t n)
+{
+ if (!write_failed) {
+ if (fwrite(buf, 1, n, fp) != n) {
+ messagef(0, "write() failed, don't know why");
+ sound_bell();
+ write_failed = 1;
+ }
+ }
+}
+
+static boolean
+has_been_touched(const struct rogue_time *saved_time,
+ const struct rogue_time *mod_time)
+{
+ if (saved_time->year < mod_time->year) {
+ return(1);
+ } else if (saved_time->year > mod_time->year) {
+ return(0);
+ }
+ if (saved_time->month < mod_time->month) {
+ return(1);
+ } else if (saved_time->month > mod_time->month) {
+ return(0);
+ }
+ if (saved_time->day < mod_time->day) {
+ return(1);
+ } else if (saved_time->day > mod_time->day) {
+ return(0);
+ }
+ if (saved_time->hour < mod_time->hour) {
+ return(1);
+ } else if (saved_time->hour > mod_time->hour) {
+ return(0);
+ }
+ if (saved_time->minute < mod_time->minute) {
+ return(1);
+ } else if (saved_time->minute > mod_time->minute) {
+ return(0);
+ }
+ if (saved_time->second < mod_time->second) {
+ return(1);
+ }
+ return(0);
+}
diff --git a/rogue/score.c b/rogue/score.c
new file mode 100644
index 0000000..4bcf4e3
--- /dev/null
+++ b/rogue/score.c
@@ -0,0 +1,674 @@
+/* $NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)score.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * score.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include <stdio.h>
+#include "rogue.h"
+#include "pathnames.h"
+
+static void center(short, const char *);
+static int get_value(const object *);
+static void id_all(void);
+static void sell_pack(void);
+static void sf_error(void) __dead;
+
+void
+killed_by(const object *monster, short other)
+{
+ const char *mechanism = "killed by something unknown (?)";
+ char mechanism_buf[128];
+ const char *article;
+ char message_buf[128];
+
+ md_ignore_signals();
+
+ if (other != QUIT) {
+ rogue.gold = ((rogue.gold * 9) / 10);
+ }
+
+ if (other) {
+ switch(other) {
+ case HYPOTHERMIA:
+ mechanism = "died of hypothermia";
+ break;
+ case STARVATION:
+ mechanism = "died of starvation";
+ break;
+ case POISON_DART:
+ mechanism = "killed by a dart";
+ break;
+ case QUIT:
+ mechanism = "quit";
+ break;
+ case KFIRE:
+ mechanism = "killed by fire";
+ break;
+ }
+ } else {
+ if (is_vowel(m_names[monster->m_char - 'A'][0])) {
+ article = "an";
+ } else {
+ article = "a";
+ }
+ snprintf(mechanism_buf, sizeof(mechanism_buf),
+ "Killed by %s %s",
+ article, m_names[monster->m_char - 'A']);
+ mechanism = mechanism_buf;
+ }
+ snprintf(message_buf, sizeof(message_buf),
+ "%s with %ld gold", mechanism, rogue.gold);
+
+ if ((!other) && (!no_skull)) {
+ clear();
+ mvaddstr(4, 32, "__---------__");
+ mvaddstr(5, 30, "_~ ~_");
+ mvaddstr(6, 29, "/ \\");
+ mvaddstr(7, 28, "~ ~");
+ mvaddstr(8, 27, "/ \\");
+ mvaddstr(9, 27, "| XXXX XXXX |");
+ mvaddstr(10, 27, "| XXXX XXXX |");
+ mvaddstr(11, 27, "| XXX XXX |");
+ mvaddstr(12, 28, "\\ @ /");
+ mvaddstr(13, 29, "--\\ @@@ /--");
+ mvaddstr(14, 30, "| | @@@ | |");
+ mvaddstr(15, 30, "| | | |");
+ mvaddstr(16, 30, "| vvVvvvvvvvVvv |");
+ mvaddstr(17, 30, "| ^^^^^^^^^^^ |");
+ mvaddstr(18, 31, "\\_ _/");
+ mvaddstr(19, 33, "~---------~");
+ center(21, nick_name);
+ center(22, message_buf);
+ } else {
+ messagef(0, "%s", message_buf);
+ }
+ messagef(0, "%s", ""); /* gcc objects to just "" */
+ put_scores(monster, other);
+}
+
+void
+win(void)
+{
+ unwield(rogue.weapon); /* disarm and relax */
+ unwear(rogue.armor);
+ un_put_on(rogue.left_ring);
+ un_put_on(rogue.right_ring);
+
+ clear();
+ mvaddstr(10, 11, "@ @ @@@ @ @ @ @ @ @@@ @ @ @");
+ mvaddstr(11, 11, " @ @ @ @ @ @ @ @ @ @ @ @@ @ @");
+ mvaddstr(12, 11, " @ @ @ @ @ @ @ @ @ @ @ @ @ @");
+ mvaddstr(13, 11, " @ @ @ @ @ @ @ @ @ @ @ @@");
+ mvaddstr(14, 11, " @ @@@ @@@ @@ @@ @@@ @ @ @");
+ mvaddstr(17, 11, "Congratulations, you have been admitted to the");
+ mvaddstr(18, 11, "Fighters' Guild. You return home, sell all your");
+ mvaddstr(19, 11, "treasures at great profit and retire into comfort.");
+ messagef(0, "%s", ""); /* gcc objects to just "" */
+ messagef(0, "%s", ""); /* gcc objects to just "" */
+ id_all();
+ sell_pack();
+ put_scores(NULL, WIN);
+}
+
+void
+quit(boolean from_intrpt)
+{
+ char buf[DCOLS];
+ short i, orow, ocol;
+ boolean mc;
+
+ orow = ocol = 0;
+ mc = FALSE;
+ md_ignore_signals();
+
+ if (from_intrpt) {
+ orow = rogue.row;
+ ocol = rogue.col;
+
+ mc = msg_cleared;
+
+ for (i = 0; i < DCOLS; i++) {
+ buf[i] = mvinch(0, i);
+ }
+ }
+ check_message();
+ messagef(1, "really quit?");
+ if (rgetchar() != 'y') {
+ md_heed_signals();
+ check_message();
+ if (from_intrpt) {
+ for (i = 0; i < DCOLS; i++) {
+ mvaddch(0, i, buf[i]);
+ }
+ msg_cleared = mc;
+ move(orow, ocol);
+ refresh();
+ }
+ return;
+ }
+ if (from_intrpt) {
+ clean_up(byebye_string);
+ }
+ check_message();
+ killed_by(NULL, QUIT);
+}
+
+/*
+ * The score file on disk is up to ten entries of the form
+ * score block [80 bytes]
+ * nickname block [30 bytes]
+ *
+ * The score block is to be parsed as follows:
+ * bytes 0-1 Rank (" 1" to "10")
+ * bytes 2-4 space padding
+ * bytes 5-15 Score/gold
+ * byte 15 up to a ':' Login name
+ * past the ':' Death mechanism
+ *
+ * The nickname block is an alternate name to be printed in place of the
+ * login name. Both blocks are supposed to contain a null-terminator.
+ */
+
+struct score_entry {
+ long gold;
+ char username[80];
+ char death[80];
+ char nickname[30];
+};
+
+#define NUM_SCORE_ENTRIES 10
+
+static void make_score(struct score_entry *, const object *, int);
+
+static
+void
+pad_spaces(char *str, size_t len)
+{
+ size_t x;
+ for (x=strlen(str); x<len-1; x++) {
+ str[x] = ' ';
+ }
+ str[len-1] = 0;
+}
+
+static
+void
+unpad_spaces(char *str)
+{
+ size_t x;
+ for (x=strlen(str); x>0 && str[x-1]==' '; x--);
+ str[x] = 0;
+}
+
+static
+int
+read_score_entry(struct score_entry *se, FILE *fp)
+{
+ char score_block[80];
+ char nickname_block[30];
+ size_t n, x;
+
+ n = fread(score_block, 1, sizeof(score_block), fp);
+ if (n==0) {
+ /* EOF */
+ return 0;
+ }
+ if (n != sizeof(score_block)) {
+ sf_error();
+ }
+
+ n = fread(nickname_block, 1, sizeof(nickname_block), fp);
+ if (n != sizeof(nickname_block)) {
+ sf_error();
+ }
+
+ xxxx(score_block, sizeof(score_block));
+ xxxx(nickname_block, sizeof(nickname_block));
+
+ /* Ensure null termination */
+ score_block[sizeof(score_block)-1] = 0;
+ nickname_block[sizeof(nickname_block)-1] = 0;
+
+ /* If there are other nulls in the score block, file is corrupt */
+ if (strlen(score_block)!=sizeof(score_block)-1) {
+ sf_error();
+ }
+ /* but this is NOT true of the nickname block */
+
+ /* quash trailing spaces */
+ unpad_spaces(score_block);
+ unpad_spaces(nickname_block);
+
+ for (x=5; score_block[x] == ' '; x++);
+ se->gold = lget_number(score_block+x);
+
+ for (x=15; score_block[x] != 0 && score_block[x] != ':'; x++);
+ if (score_block[x] == 0) {
+ sf_error();
+ }
+ score_block[x++] = 0;
+ strlcpy(se->username, score_block+15, sizeof(se->username));
+
+ strlcpy(se->death, score_block+x, sizeof(se->death));
+ strlcpy(se->nickname, nickname_block, sizeof(se->nickname));
+
+ return 1;
+}
+
+static
+void
+write_score_entry(const struct score_entry *se, int rank, FILE *fp)
+{
+ char score_block[80];
+ char nickname_block[30];
+
+ /* avoid writing crap to score file */
+ memset(score_block, 0, sizeof(score_block));
+ memset(nickname_block, 0, sizeof(nickname_block));
+
+ snprintf(score_block, sizeof(score_block),
+ "%2d %6ld %s: %s",
+ rank+1, se->gold, se->username, se->death);
+ strlcpy(nickname_block, se->nickname, sizeof(nickname_block));
+
+ /* pad blocks out with spaces */
+ pad_spaces(score_block, sizeof(score_block));
+ /*pad_spaces(nickname_block, sizeof(nickname_block)); -- wrong! */
+
+ xxxx(score_block, sizeof(score_block));
+ xxxx(nickname_block, sizeof(nickname_block));
+
+ fwrite(score_block, 1, sizeof(score_block), fp);
+ fwrite(nickname_block, 1, sizeof(nickname_block), fp);
+}
+
+void
+put_scores(const object *monster, short other)
+{
+ short i, rank=-1, found_player = -1, numscores = 0;
+ struct score_entry scores[NUM_SCORE_ENTRIES];
+ const char *name;
+ FILE *fp;
+ boolean dopause = score_only;
+
+ md_lock(1);
+
+ setegid(egid);
+ if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL &&
+ (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) {
+ setegid(gid);
+ messagef(0, "cannot read/write/create score file");
+ sf_error();
+ }
+ setegid(gid);
+ rewind(fp);
+ (void)xxx(1);
+
+ for (numscores = 0; numscores < NUM_SCORE_ENTRIES; numscores++) {
+ if (read_score_entry(&scores[numscores], fp) == 0) {
+ break;
+ }
+ }
+
+ /* Search the score list. */
+ for (i=0; i<numscores; i++) {
+ if (!strcmp(scores[i].username, login_name)) {
+ /* found our score */
+ if (rogue.gold < scores[i].gold) {
+ /* we didn't do as well as last time */
+ score_only = 1;
+ } else {
+ /* we did better; mark entry for removal */
+ found_player = i;
+ }
+ break;
+ }
+ }
+
+ /* Remove a superseded entry, if any. */
+ if (found_player != -1) {
+ numscores--;
+ for (i = found_player; i < numscores; i++) {
+ scores[i] = scores[i+1];
+ }
+ }
+
+ /* If we're going to insert ourselves, do it now */
+ if (!score_only) {
+
+ /* if we aren't better than anyone, add at end. */
+ rank = numscores;
+
+ /* Otherwise, find our slot. */
+ for (i = 0; i < numscores; i++) {
+ if (rogue.gold >= scores[i].gold) {
+ rank = i;
+ break;
+ }
+ }
+
+ if (rank < NUM_SCORE_ENTRIES) {
+ /* Open up a slot */
+ for (i = numscores; i > rank; i--) {
+ scores[i] = scores[i-1];
+ }
+ numscores++;
+
+ /* Put our info in the slot */
+ make_score(&scores[rank], monster, other);
+ }
+
+ /* Now rewrite the score file */
+
+ md_ignore_signals();
+ rewind(fp);
+ (void)xxx(1);
+
+ for (i = 0; i < numscores; i++) {
+ write_score_entry(&scores[i], i, fp);
+ }
+ }
+ md_lock(0);
+ fclose(fp);
+
+ /* Display the scores */
+
+ clear();
+ mvaddstr(3, 30, "Top Ten Rogueists");
+ mvaddstr(8, 0, "Rank Score Name");
+
+ for (i = 0; i < numscores; i++) {
+ if (i == rank) {
+ standout();
+ }
+
+ if (scores[i].nickname[0]) {
+ name = scores[i].nickname;
+ } else {
+ name = scores[i].username;
+ }
+
+ mvprintw(i+10, 0, "%2d %6ld %s: %s",
+ i+1, scores[i].gold, name, scores[i].death);
+
+ if (i == rank) {
+ standend();
+ }
+ }
+ refresh();
+ messagef(0, "%s", ""); /* gcc objects to just "" */
+ if (dopause) {
+ messagef(0, "%s", "");
+ }
+ clean_up("");
+}
+
+static
+void
+make_score(struct score_entry *se, const object *monster, int other)
+{
+ const char *death = "bolts from the blue (?)";
+ const char *hasamulet;
+ char deathbuf[80];
+
+ se->gold = rogue.gold;
+ strlcpy(se->username, login_name, sizeof(se->username));
+
+ if (other) {
+ switch(other) {
+ case HYPOTHERMIA:
+ death = "died of hypothermia";
+ break;
+ case STARVATION:
+ death = "died of starvation";
+ break;
+ case POISON_DART:
+ death = "killed by a dart";
+ break;
+ case QUIT:
+ death = "quit";
+ break;
+ case WIN:
+ death = "a total winner";
+ break;
+ case KFIRE:
+ death = "killed by fire";
+ break;
+ }
+ } else {
+ const char *mn, *article;
+
+ mn = m_names[monster->m_char - 'A'];
+ if (is_vowel(mn[0])) {
+ article = "an";
+ } else {
+ article = "a";
+ }
+
+ snprintf(deathbuf, sizeof(deathbuf),
+ "killed by %s %s", article, mn);
+ death = deathbuf;
+ }
+
+ if (other != WIN && has_amulet()) {
+ hasamulet = " with amulet";
+ } else {
+ hasamulet = "";
+ }
+
+ snprintf(se->death, sizeof(se->death), "%s on level %d%s",
+ death, max_level, hasamulet);
+
+ strlcpy(se->nickname, nick_name, sizeof(se->nickname));
+}
+
+boolean
+is_vowel(short ch)
+{
+ return( (ch == 'a') ||
+ (ch == 'e') ||
+ (ch == 'i') ||
+ (ch == 'o') ||
+ (ch == 'u') );
+}
+
+static void
+sell_pack(void)
+{
+ object *obj;
+ short row = 2, val;
+ char buf[DCOLS];
+
+ obj = rogue.pack.next_object;
+
+ clear();
+ mvaddstr(1, 0, "Value Item");
+
+ while (obj) {
+ if (obj->what_is != FOOD) {
+ obj->identified = 1;
+ val = get_value(obj);
+ rogue.gold += val;
+
+ if (row < DROWS) {
+ get_desc(obj, buf, sizeof(buf));
+ mvprintw(row++, 0, "%5d %s", val, buf);
+ }
+ }
+ obj = obj->next_object;
+ }
+ refresh();
+ if (rogue.gold > MAX_GOLD) {
+ rogue.gold = MAX_GOLD;
+ }
+ messagef(0, "%s", ""); /* gcc objects to just "" */
+}
+
+static int
+get_value(const object *obj)
+{
+ short wc;
+ int val;
+
+ val = 0;
+ wc = obj->which_kind;
+
+ switch(obj->what_is) {
+ case WEAPON:
+ val = id_weapons[wc].value;
+ if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) ||
+ (wc == DART)) {
+ val *= obj->quantity;
+ }
+ val += (obj->d_enchant * 85);
+ val += (obj->hit_enchant * 85);
+ break;
+ case ARMOR:
+ val = id_armors[wc].value;
+ val += (obj->d_enchant * 75);
+ if (obj->is_protected) {
+ val += 200;
+ }
+ break;
+ case WAND:
+ val = id_wands[wc].value * (obj->class + 1);
+ break;
+ case SCROL:
+ val = id_scrolls[wc].value * obj->quantity;
+ break;
+ case POTION:
+ val = id_potions[wc].value * obj->quantity;
+ break;
+ case AMULET:
+ val = 5000;
+ break;
+ case RING:
+ val = id_rings[wc].value * (obj->class + 1);
+ break;
+ }
+ if (val <= 0) {
+ val = 10;
+ }
+ return(val);
+}
+
+static void
+id_all(void)
+{
+ short i;
+
+ for (i = 0; i < SCROLS; i++) {
+ id_scrolls[i].id_status = IDENTIFIED;
+ }
+ for (i = 0; i < WEAPONS; i++) {
+ id_weapons[i].id_status = IDENTIFIED;
+ }
+ for (i = 0; i < ARMORS; i++) {
+ id_armors[i].id_status = IDENTIFIED;
+ }
+ for (i = 0; i < WANDS; i++) {
+ id_wands[i].id_status = IDENTIFIED;
+ }
+ for (i = 0; i < POTIONS; i++) {
+ id_potions[i].id_status = IDENTIFIED;
+ }
+}
+
+void
+xxxx(char *buf, short n)
+{
+ short i;
+ unsigned char c;
+
+ for (i = 0; i < n; i++) {
+
+ /* It does not matter if accuracy is lost during this assignment */
+ c = (unsigned char)xxx(0);
+
+ buf[i] ^= c;
+ }
+}
+
+long
+xxx(boolean st)
+{
+ static long f, s;
+ long r;
+
+ if (st) {
+ f = 37;
+ s = 7;
+ return(0L);
+ }
+ r = ((f * s) + 9337) % 8887;
+ f = s;
+ s = r;
+ return(r);
+}
+
+static void
+center(short row, const char *buf)
+{
+ short margin;
+
+ margin = ((DCOLS - strlen(buf)) / 2);
+ mvaddstr(row, margin, buf);
+}
+
+static void
+sf_error(void)
+{
+ md_lock(0);
+ messagef(1, "%s", ""); /* gcc objects to just "" */
+ clean_up("sorry, score file is out of order");
+}
diff --git a/rogue/spec_hit.c b/rogue/spec_hit.c
new file mode 100644
index 0000000..69e314c
--- /dev/null
+++ b/rogue/spec_hit.c
@@ -0,0 +1,536 @@
+/* $NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)spec_hit.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * special_hit.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+static void disappear(object *);
+static void drain_life(void);
+static void drop_level(void);
+static void freeze(object *);
+static int get_dir(short, short, short, short);
+static boolean gold_at(short, short);
+static void steal_gold(object *);
+static void steal_item(object *);
+static void sting(object *);
+static boolean try_to_cough(short, short, object *);
+
+short less_hp = 0;
+boolean being_held;
+
+void
+special_hit(object *monster)
+{
+ if ((monster->m_flags & CONFUSED) && rand_percent(66)) {
+ return;
+ }
+ if (monster->m_flags & RUSTS) {
+ rust(monster);
+ }
+ if ((monster->m_flags & HOLDS) && !levitate) {
+ being_held = 1;
+ }
+ if (monster->m_flags & FREEZES) {
+ freeze(monster);
+ }
+ if (monster->m_flags & STINGS) {
+ sting(monster);
+ }
+ if (monster->m_flags & DRAINS_LIFE) {
+ drain_life();
+ }
+ if (monster->m_flags & DROPS_LEVEL) {
+ drop_level();
+ }
+ if (monster->m_flags & STEALS_GOLD) {
+ steal_gold(monster);
+ } else if (monster->m_flags & STEALS_ITEM) {
+ steal_item(monster);
+ }
+}
+
+void
+rust(object *monster)
+{
+ if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) ||
+ (rogue.armor->which_kind == LEATHER)) {
+ return;
+ }
+ if ((rogue.armor->is_protected) || maintain_armor) {
+ if (monster && (!(monster->m_flags & RUST_VANISHED))) {
+ messagef(0, "the rust vanishes instantly");
+ monster->m_flags |= RUST_VANISHED;
+ }
+ } else {
+ rogue.armor->d_enchant--;
+ messagef(0, "your armor weakens");
+ print_stats(STAT_ARMOR);
+ }
+}
+
+void
+freeze(object *monster)
+{
+ short freeze_percent = 99;
+ short i, n;
+
+ if (rand_percent(12)) {
+ return;
+ }
+ freeze_percent -= (rogue.str_current+(rogue.str_current / 2));
+ freeze_percent -= ((rogue.exp + ring_exp) * 4);
+ freeze_percent -= (get_armor_class(rogue.armor) * 5);
+ freeze_percent -= (rogue.hp_max / 3);
+
+ if (freeze_percent > 10) {
+ monster->m_flags |= FREEZING_ROGUE;
+ messagef(1, "you are frozen");
+
+ n = get_rand(4, 8);
+ for (i = 0; i < n; i++) {
+ mv_mons();
+ }
+ if (rand_percent(freeze_percent)) {
+ for (i = 0; i < 50; i++) {
+ mv_mons();
+ }
+ killed_by(NULL, HYPOTHERMIA);
+ }
+ messagef(1, "%s", you_can_move_again);
+ monster->m_flags &= (~FREEZING_ROGUE);
+ }
+}
+
+void
+steal_gold(object *monster)
+{
+ int amount;
+
+ if ((rogue.gold <= 0) || rand_percent(10)) {
+ return;
+ }
+
+ amount = get_rand((cur_level * 10), (cur_level * 30));
+
+ if (amount > rogue.gold) {
+ amount = rogue.gold;
+ }
+ rogue.gold -= amount;
+ messagef(0, "your purse feels lighter");
+ print_stats(STAT_GOLD);
+ disappear(monster);
+}
+
+void
+steal_item(object *monster)
+{
+ object *obj;
+ short i, n, t = 0;
+ char desc[80];
+ boolean has_something = 0;
+
+ if (rand_percent(15)) {
+ return;
+ }
+ obj = rogue.pack.next_object;
+
+ if (!obj) {
+ goto DSPR;
+ }
+ while (obj) {
+ if (!(obj->in_use_flags & BEING_USED)) {
+ has_something = 1;
+ break;
+ }
+ obj = obj->next_object;
+ }
+ if (!has_something) {
+ goto DSPR;
+ }
+ n = get_rand(0, MAX_PACK_COUNT);
+ obj = rogue.pack.next_object;
+
+ for (i = 0; i <= n; i++) {
+ obj = obj->next_object;
+ while ((!obj) || (obj->in_use_flags & BEING_USED)) {
+ if (!obj) {
+ obj = rogue.pack.next_object;
+ } else {
+ obj = obj->next_object;
+ }
+ }
+ }
+ if (obj->what_is != WEAPON) {
+ t = obj->quantity;
+ obj->quantity = 1;
+ }
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "she stole %s", desc);
+
+ obj->quantity = ((obj->what_is != WEAPON) ? t : 1);
+
+ vanish(obj, 0, &rogue.pack);
+DSPR:
+ disappear(monster);
+}
+
+static void
+disappear(object *monster)
+{
+ short row, col;
+
+ row = monster->row;
+ col = monster->col;
+
+ dungeon[row][col] &= ~MONSTER;
+ if (rogue_can_see(row, col)) {
+ mvaddch(row, col, get_dungeon_char(row, col));
+ }
+ take_from_pack(monster, &level_monsters);
+ free_object(monster);
+ mon_disappeared = 1;
+}
+
+void
+cough_up(object *monster)
+{
+ object *obj;
+ short row, col, i, n;
+
+ if (cur_level < max_level) {
+ return;
+ }
+
+ if (monster->m_flags & STEALS_GOLD) {
+ obj = alloc_object();
+ obj->what_is = GOLD;
+ obj->quantity = get_rand((cur_level * 15), (cur_level * 30));
+ } else {
+ if (!rand_percent((int)monster->drop_percent)) {
+ return;
+ }
+ obj = gr_object();
+ }
+ row = monster->row;
+ col = monster->col;
+
+ for (n = 0; n <= 5; n++) {
+ for (i = -n; i <= n; i++) {
+ if (try_to_cough(row+n, col+i, obj)) {
+ return;
+ }
+ if (try_to_cough(row-n, col+i, obj)) {
+ return;
+ }
+ }
+ for (i = -n; i <= n; i++) {
+ if (try_to_cough(row+i, col-n, obj)) {
+ return;
+ }
+ if (try_to_cough(row+i, col+n, obj)) {
+ return;
+ }
+ }
+ }
+ free_object(obj);
+}
+
+static boolean
+try_to_cough(short row, short col, object *obj)
+{
+ if ((row < MIN_ROW) ||
+ (row > (DROWS-2)) || (col < 0) || (col>(DCOLS-1))) {
+ return(0);
+ }
+ if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) &&
+ (dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) {
+ place_at(obj, row, col);
+ if (((row != rogue.row) || (col != rogue.col)) &&
+ (!(dungeon[row][col] & MONSTER))) {
+ mvaddch(row, col, get_dungeon_char(row, col));
+ }
+ return(1);
+ }
+ return(0);
+}
+
+boolean
+seek_gold(object *monster)
+{
+ short i, j, rn, s;
+
+ if ((rn = get_room_number(monster->row, monster->col)) < 0) {
+ return(0);
+ }
+ for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) {
+ for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) {
+ if ((gold_at(i, j)) && !(dungeon[i][j] & MONSTER)) {
+ monster->m_flags |= CAN_FLIT;
+ s = mon_can_go(monster, i, j);
+ monster->m_flags &= (~CAN_FLIT);
+ if (s) {
+ move_mon_to(monster, i, j);
+ monster->m_flags |= ASLEEP;
+ monster->m_flags &= (~(WAKENS | SEEKS_GOLD));
+ return(1);
+ }
+ monster->m_flags &= (~SEEKS_GOLD);
+ monster->m_flags |= CAN_FLIT;
+ mv_1_monster(monster, i, j);
+ monster->m_flags &= (~CAN_FLIT);
+ monster->m_flags |= SEEKS_GOLD;
+ return(1);
+ }
+ }
+ }
+ return(0);
+}
+
+static boolean
+gold_at(short row, short col)
+{
+ if (dungeon[row][col] & OBJECT) {
+ object *obj;
+
+ if ((obj = object_at(&level_objects, row, col)) &&
+ (obj->what_is == GOLD)) {
+ return(1);
+ }
+ }
+ return(0);
+}
+
+void
+check_gold_seeker(object *monster)
+{
+ monster->m_flags &= (~SEEKS_GOLD);
+}
+
+boolean
+check_imitator(object *monster)
+{
+ if (monster->m_flags & IMITATES) {
+ wake_up(monster);
+ if (!blind) {
+ mvaddch(monster->row, monster->col,
+ get_dungeon_char(monster->row, monster->col));
+ check_message();
+ messagef(1, "wait, that's a %s!", mon_name(monster));
+ }
+ return(1);
+ }
+ return(0);
+}
+
+boolean
+imitating(short row, short col)
+{
+ if (dungeon[row][col] & MONSTER) {
+ object *monster;
+
+ if ((monster = object_at(&level_monsters, row, col)) != NULL) {
+ if (monster->m_flags & IMITATES) {
+ return(1);
+ }
+ }
+ }
+ return(0);
+}
+
+static void
+sting(object *monster)
+{
+ short sting_chance = 35;
+
+ if ((rogue.str_current <= 3) || sustain_strength) {
+ return;
+ }
+ sting_chance += (6 * (6 - get_armor_class(rogue.armor)));
+
+ if ((rogue.exp + ring_exp) > 8) {
+ sting_chance -= (6 * ((rogue.exp + ring_exp) - 8));
+ }
+ if (rand_percent(sting_chance)) {
+ messagef(0, "the %s's bite has weakened you",
+ mon_name(monster));
+ rogue.str_current--;
+ print_stats(STAT_STRENGTH);
+ }
+}
+
+static void
+drop_level(void)
+{
+ int hp;
+
+ if (rand_percent(80) || (rogue.exp <= 5)) {
+ return;
+ }
+ rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29);
+ rogue.exp -= 2;
+ hp = hp_raise();
+ if ((rogue.hp_current -= hp) <= 0) {
+ rogue.hp_current = 1;
+ }
+ if ((rogue.hp_max -= hp) <= 0) {
+ rogue.hp_max = 1;
+ }
+ add_exp(1, 0);
+}
+
+void
+drain_life(void)
+{
+ short n;
+
+ if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) {
+ return;
+ }
+ n = get_rand(1, 3); /* 1 Hp, 2 Str, 3 both */
+
+ if ((n != 2) || (!sustain_strength)) {
+ messagef(0, "you feel weaker");
+ }
+ if (n != 2) {
+ rogue.hp_max--;
+ rogue.hp_current--;
+ less_hp++;
+ }
+ if (n != 1) {
+ if ((rogue.str_current > 3) && (!sustain_strength)) {
+ rogue.str_current--;
+ if (coin_toss()) {
+ rogue.str_max--;
+ }
+ }
+ }
+ print_stats((STAT_STRENGTH | STAT_HP));
+}
+
+boolean
+m_confuse(object *monster)
+{
+ if (!rogue_can_see(monster->row, monster->col)) {
+ return(0);
+ }
+ if (rand_percent(45)) {
+ monster->m_flags &= (~CONFUSES); /* will not confuse the rogue */
+ return(0);
+ }
+ if (rand_percent(55)) {
+ monster->m_flags &= (~CONFUSES);
+ messagef(1, "the gaze of the %s has confused you",
+ mon_name(monster));
+ cnfs();
+ return(1);
+ }
+ return(0);
+}
+
+boolean
+flame_broil(object *monster)
+{
+ short row, col, dir;
+
+ if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) {
+ return(0);
+ }
+ row = rogue.row - monster->row;
+ col = rogue.col - monster->col;
+ if (row < 0) {
+ row = -row;
+ }
+ if (col < 0) {
+ col = -col;
+ }
+ if (((row != 0) && (col != 0) && (row != col)) ||
+ ((row > 7) || (col > 7))) {
+ return(0);
+ }
+ dir = get_dir(monster->row, monster->col, row, col);
+ bounce(FIRE, dir, monster->row, monster->col, 0);
+
+ return(1);
+}
+
+static int
+get_dir(short srow, short scol, short drow, short dcol)
+{
+ if (srow == drow) {
+ if (scol < dcol) {
+ return(RIGHT);
+ } else {
+ return(LEFT);
+ }
+ }
+ if (scol == dcol) {
+ if (srow < drow) {
+ return(DOWN);
+ } else {
+ return(UPWARD);
+ }
+ }
+ if ((srow > drow) && (scol > dcol)) {
+ return(UPLEFT);
+ }
+ if ((srow < drow) && (scol < dcol)) {
+ return(DOWNRIGHT);
+ }
+ if ((srow < drow) && (scol > dcol)) {
+ return(DOWNLEFT);
+ }
+ /*if ((srow > drow) && (scol < dcol)) {*/
+ return(UPRIGHT);
+ /*}*/
+}
diff --git a/rogue/throw.c b/rogue/throw.c
new file mode 100644
index 0000000..93c88c5
--- /dev/null
+++ b/rogue/throw.c
@@ -0,0 +1,324 @@
+/* $NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)throw.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * throw.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+static void flop_weapon(object *, short, short);
+static object *get_thrown_at_monster(object *, short, short *, short *);
+static boolean throw_at_monster(object *, object *);
+
+void
+throw(void)
+{
+ short wch, d;
+ boolean first_miss = 1;
+ object *weapon;
+ short dir, row, col;
+ object *monster;
+
+ while (!is_direction(dir = rgetchar(), &d)) {
+ sound_bell();
+ if (first_miss) {
+ messagef(0, "direction? ");
+ first_miss = 0;
+ }
+ }
+ check_message();
+ if (dir == CANCEL) {
+ return;
+ }
+ if ((wch = pack_letter("throw what?", WEAPON)) == CANCEL) {
+ return;
+ }
+ check_message();
+
+ if (!(weapon = get_letter_object(wch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if ((weapon->in_use_flags & BEING_USED) && weapon->is_cursed) {
+ messagef(0, "%s", curse_message);
+ return;
+ }
+ row = rogue.row; col = rogue.col;
+
+ if ((weapon->in_use_flags & BEING_WIELDED) && (weapon->quantity <= 1)) {
+ unwield(rogue.weapon);
+ } else if (weapon->in_use_flags & BEING_WORN) {
+ mv_aquatars();
+ unwear(rogue.armor);
+ print_stats(STAT_ARMOR);
+ } else if (weapon->in_use_flags & ON_EITHER_HAND) {
+ un_put_on(weapon);
+ }
+ monster = get_thrown_at_monster(weapon, d, &row, &col);
+ mvaddch(rogue.row, rogue.col, rogue.fchar);
+ refresh();
+
+ if (rogue_can_see(row, col) && ((row != rogue.row) || (col != rogue.col))){
+ mvaddch(row, col, get_dungeon_char(row, col));
+ }
+ if (monster) {
+ wake_up(monster);
+ check_gold_seeker(monster);
+
+ if (!throw_at_monster(monster, weapon)) {
+ flop_weapon(weapon, row, col);
+ }
+ } else {
+ flop_weapon(weapon, row, col);
+ }
+ vanish(weapon, 1, &rogue.pack);
+}
+
+boolean
+throw_at_monster(object *monster, object *weapon)
+{
+ short damage, hit_chance;
+ short t;
+
+ hit_chance = get_hit_chance(weapon);
+ damage = get_weapon_damage(weapon);
+ if ((weapon->which_kind == ARROW) &&
+ (rogue.weapon && (rogue.weapon->which_kind == BOW))) {
+ damage += get_weapon_damage(rogue.weapon);
+ damage = ((damage * 2) / 3);
+ hit_chance += (hit_chance / 3);
+ } else if ((weapon->in_use_flags & BEING_WIELDED) &&
+ ((weapon->which_kind == DAGGER) ||
+ (weapon->which_kind == SHURIKEN) ||
+ (weapon->which_kind == DART))) {
+ damage = ((damage * 3) / 2);
+ hit_chance += (hit_chance / 3);
+ }
+ t = weapon->quantity;
+ weapon->quantity = 1;
+ snprintf(hit_message, HIT_MESSAGE_SIZE, "the %s", name_of(weapon));
+ weapon->quantity = t;
+
+ if (!rand_percent(hit_chance)) {
+ (void)strlcat(hit_message, "misses ", HIT_MESSAGE_SIZE);
+ return(0);
+ }
+ s_con_mon(monster);
+ (void)strlcat(hit_message, "hit ", HIT_MESSAGE_SIZE);
+ (void)mon_damage(monster, damage);
+ return(1);
+}
+
+object *
+get_thrown_at_monster(object *obj, short dir, short *row, short *col)
+{
+ short orow, ocol;
+ short i, ch;
+
+ orow = *row; ocol = *col;
+
+ ch = get_mask_char(obj->what_is);
+
+ for (i = 0; i < 24; i++) {
+ get_dir_rc(dir, row, col, 0);
+ if ( (((*col <= 0) || (*col >= DCOLS-1)) ||
+ (dungeon[*row][*col] == NOTHING)) ||
+ ((dungeon[*row][*col] & (HORWALL | VERTWALL | HIDDEN)) &&
+ (!(dungeon[*row][*col] & TRAP)))) {
+ *row = orow;
+ *col = ocol;
+ return(0);
+ }
+ if ((i != 0) && rogue_can_see(orow, ocol)) {
+ mvaddch(orow, ocol, get_dungeon_char(orow, ocol));
+ }
+ if (rogue_can_see(*row, *col)) {
+ if (!(dungeon[*row][*col] & MONSTER)) {
+ mvaddch(*row, *col, ch);
+ }
+ refresh();
+ }
+ orow = *row; ocol = *col;
+ if (dungeon[*row][*col] & MONSTER) {
+ if (!imitating(*row, *col)) {
+ return(object_at(&level_monsters, *row, *col));
+ }
+ }
+ if (dungeon[*row][*col] & TUNNEL) {
+ i += 2;
+ }
+ }
+ return(0);
+}
+
+void
+flop_weapon(object *weapon, short row, short col)
+{
+ object *new_weapon, *monster;
+ short i = 0;
+ boolean found = 0;
+ short mch, dch;
+ unsigned short mon;
+
+ if ((row < 0) || (row >= DROWS) || (col < 0) || (col >= DCOLS))
+ clean_up("flop_weapon: weapon landed outside of dungeon");
+
+ while ((i < 9) && dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER)) {
+ rand_around(i++, &row, &col);
+ if ((row > (DROWS-2)) || (row < MIN_ROW) ||
+ (col > (DCOLS-1)) || (col < 0) || (!dungeon[row][col]) ||
+ (dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER))) {
+ continue;
+ }
+ found = 1;
+ break;
+ }
+
+ if (found || (i == 0)) {
+ new_weapon = alloc_object();
+ *new_weapon = *weapon;
+ new_weapon->in_use_flags = NOT_USED;
+ new_weapon->quantity = 1;
+ new_weapon->ichar = 'L';
+ place_at(new_weapon, row, col);
+ if (rogue_can_see(row, col) &&
+ ((row != rogue.row) || (col != rogue.col))) {
+ mon = dungeon[row][col] & MONSTER;
+ dungeon[row][col] &= (~MONSTER);
+ dch = get_dungeon_char(row, col);
+ if (mon) {
+ mch = mvinch(row, col);
+ if ((monster = object_at(&level_monsters,
+ row, col)) != NULL) {
+ monster->trail_char = dch;
+ }
+ if ((mch < 'A') || (mch > 'Z')) {
+ mvaddch(row, col, dch);
+ }
+ } else {
+ mvaddch(row, col, dch);
+ }
+ dungeon[row][col] |= mon;
+ }
+ } else {
+ short t;
+
+ t = weapon->quantity;
+ weapon->quantity = 1;
+ messagef(0, "the %svanishes as it hits the ground",
+ name_of(weapon));
+ weapon->quantity = t;
+ }
+}
+
+void
+rand_around(short i, short *r, short *c)
+{
+ static char pos[] = "\010\007\001\003\004\005\002\006\0";
+ static short row, col;
+ short j;
+
+ if (i == 0) {
+ short x, y, o, t;
+
+ row = *r;
+ col = *c;
+
+ o = get_rand(1, 8);
+
+ for (j = 0; j < 5; j++) {
+ x = get_rand(0, 8);
+ y = (x + o) % 9;
+ t = pos[x];
+ pos[x] = pos[y];
+ pos[y] = t;
+ }
+ }
+ switch((short)pos[i]) {
+ case 0:
+ *r = row + 1;
+ *c = col + 1;
+ break;
+ case 1:
+ *r = row + 1;
+ *c = col - 1;
+ break;
+ case 2:
+ *r = row - 1;
+ *c = col + 1;
+ break;
+ case 3:
+ *r = row - 1;
+ *c = col - 1;
+ break;
+ case 4:
+ *r = row;
+ *c = col + 1;
+ break;
+ case 5:
+ *r = row + 1;
+ *c = col;
+ break;
+ case 6:
+ *r = row;
+ *c = col;
+ break;
+ case 7:
+ *r = row - 1;
+ *c = col;
+ break;
+ case 8:
+ *r = row;
+ *c = col - 1;
+ break;
+ }
+}
diff --git a/rogue/trap.c b/rogue/trap.c
new file mode 100644
index 0000000..ae3c918
--- /dev/null
+++ b/rogue/trap.c
@@ -0,0 +1,282 @@
+/* $NetBSD: trap.c,v 1.10 2009/08/12 08:44:45 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)trap.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: trap.c,v 1.10 2009/08/12 08:44:45 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * trap.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+trap traps[MAX_TRAPS];
+boolean trap_door = 0;
+short bear_trap = 0;
+
+static const char *const trap_strings[TRAPS * 2] = {
+ "trap door",
+ "you fell down a trap",
+ "bear trap",
+ "you are caught in a bear trap",
+ "teleport trap",
+ "teleport",
+ "poison dart trap",
+ "a small dart just hit you in the shoulder",
+ "sleeping gas trap",
+ "a strange white mist envelops you and you fall asleep",
+ "rust trap",
+ "a gush of water hits you on the head"
+};
+
+static short
+trap_at(int row, int col)
+{
+ short i;
+
+ for (i = 0; ((i < MAX_TRAPS) && (traps[i].trap_type != NO_TRAP)); i++) {
+ if ((traps[i].trap_row == row) && (traps[i].trap_col == col)) {
+ return(traps[i].trap_type);
+ }
+ }
+ return(NO_TRAP);
+}
+
+void
+trap_player(short row, short col)
+{
+ short t;
+
+ if ((t = trap_at(row, col)) == NO_TRAP) {
+ return;
+ }
+ dungeon[row][col] &= (~HIDDEN);
+ if (rand_percent(rogue.exp + ring_exp)) {
+ messagef(1, "the trap failed");
+ return;
+ }
+ switch(t) {
+ case TRAP_DOOR:
+ trap_door = 1;
+ new_level_message = trap_strings[(t*2)+1];
+ break;
+ case BEAR_TRAP:
+ messagef(1, "%s", trap_strings[(t*2)+1]);
+ bear_trap = get_rand(4, 7);
+ break;
+ case TELE_TRAP:
+ mvaddch(rogue.row, rogue.col, '^');
+ tele();
+ break;
+ case DART_TRAP:
+ messagef(1, "%s", trap_strings[(t*2)+1]);
+ rogue.hp_current -= get_damage("1d6", 1);
+ if (rogue.hp_current <= 0) {
+ rogue.hp_current = 0;
+ }
+ if ((!sustain_strength) && rand_percent(40) &&
+ (rogue.str_current >= 3)) {
+ rogue.str_current--;
+ }
+ print_stats(STAT_HP | STAT_STRENGTH);
+ if (rogue.hp_current <= 0) {
+ killed_by((object *)0, POISON_DART);
+ }
+ break;
+ case SLEEPING_GAS_TRAP:
+ messagef(1, "%s", trap_strings[(t*2)+1]);
+ take_a_nap();
+ break;
+ case RUST_TRAP:
+ messagef(1, "%s", trap_strings[(t*2)+1]);
+ rust(NULL);
+ break;
+ }
+}
+
+void
+add_traps(void)
+{
+ short i, n, tries = 0;
+ short row, col;
+
+ if (cur_level <= 2) {
+ n = 0;
+ } else if (cur_level <= 7) {
+ n = get_rand(0, 2);
+ } else if (cur_level <= 11) {
+ n = get_rand(1, 2);
+ } else if (cur_level <= 16) {
+ n = get_rand(2, 3);
+ } else if (cur_level <= 21) {
+ n = get_rand(2, 4);
+ } else if (cur_level <= (AMULET_LEVEL + 2)) {
+ n = get_rand(3, 5);
+ } else {
+ n = get_rand(5, MAX_TRAPS);
+ }
+ for (i = 0; i < n; i++) {
+ traps[i].trap_type = get_rand(0, (TRAPS - 1));
+
+ if ((i == 0) && (party_room != NO_ROOM)) {
+ do {
+ row = get_rand((rooms[party_room].top_row+1),
+ (rooms[party_room].bottom_row-1));
+ col = get_rand((rooms[party_room].left_col+1),
+ (rooms[party_room].right_col-1));
+ tries++;
+ } while (((dungeon[row][col] & (OBJECT|STAIRS|TRAP|TUNNEL)) ||
+ (dungeon[row][col] == NOTHING)) && (tries < 15));
+ if (tries >= 15) {
+ gr_row_col(&row, &col, (FLOOR | MONSTER));
+ }
+ } else {
+ gr_row_col(&row, &col, (FLOOR | MONSTER));
+ }
+ traps[i].trap_row = row;
+ traps[i].trap_col = col;
+ dungeon[row][col] |= (TRAP | HIDDEN);
+ }
+}
+
+void
+id_trap(void)
+{
+ short dir, row, col, d, t;
+
+ messagef(0, "direction? ");
+
+ while (!is_direction(dir = rgetchar(), &d)) {
+ sound_bell();
+ }
+ check_message();
+
+ if (dir == CANCEL) {
+ return;
+ }
+ row = rogue.row;
+ col = rogue.col;
+
+ get_dir_rc(d, &row, &col, 0);
+
+ if ((dungeon[row][col] & TRAP) && (!(dungeon[row][col] & HIDDEN))) {
+ t = trap_at(row, col);
+ messagef(0, "%s", trap_strings[t*2]);
+ } else {
+ messagef(0, "no trap there");
+ }
+}
+
+void
+show_traps(void)
+{
+ short i, j;
+
+ for (i = 0; i < DROWS; i++) {
+ for (j = 0; j < DCOLS; j++) {
+ if (dungeon[i][j] & TRAP) {
+ mvaddch(i, j, '^');
+ }
+ }
+ }
+}
+
+void
+search(short n, boolean is_auto)
+{
+ short s, i, j, row, col, t;
+ short shown = 0, found = 0;
+ static boolean reg_search;
+
+ for (i = -1; i <= 1; i++) {
+ for (j = -1; j <= 1; j++) {
+ row = rogue.row + i;
+ col = rogue.col + j;
+ if ((row < MIN_ROW) || (row >= (DROWS-1)) ||
+ (col < 0) || (col >= DCOLS)) {
+ continue;
+ }
+ if (dungeon[row][col] & HIDDEN) {
+ found++;
+ }
+ }
+ }
+ for (s = 0; s < n; s++) {
+ for (i = -1; i <= 1; i++) {
+ for (j = -1; j <= 1; j++) {
+ row = rogue.row + i;
+ col = rogue.col + j ;
+ if ((row < MIN_ROW) || (row >= (DROWS-1)) ||
+ (col < 0) || (col >= DCOLS)) {
+ continue;
+ }
+ if (dungeon[row][col] & HIDDEN) {
+ if (rand_percent(17 + (rogue.exp + ring_exp))) {
+ dungeon[row][col] &= (~HIDDEN);
+ if ((!blind) && ((row != rogue.row) ||
+ (col != rogue.col))) {
+ mvaddch(row, col, get_dungeon_char(row, col));
+ }
+ shown++;
+ if (dungeon[row][col] & TRAP) {
+ t = trap_at(row, col);
+ messagef(1, "%s",
+ trap_strings[t*2]);
+ }
+ }
+ }
+ if (((shown == found) && (found > 0)) || interrupted) {
+ return;
+ }
+ }
+ }
+ if ((!is_auto) && (reg_search = !reg_search)) {
+ (void)reg_move();
+ }
+ }
+}
diff --git a/rogue/use.c b/rogue/use.c
new file mode 100644
index 0000000..8371a6f
--- /dev/null
+++ b/rogue/use.c
@@ -0,0 +1,625 @@
+/* $NetBSD: use.c,v 1.10 2009/08/12 08:44:45 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)use.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: use.c,v 1.10 2009/08/12 08:44:45 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * use.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+short halluc = 0;
+short blind = 0;
+short confused = 0;
+short levitate = 0;
+short haste_self = 0;
+boolean see_invisible = 0;
+short extra_hp = 0;
+boolean detect_monster = 0;
+boolean con_mon = 0;
+
+static const char strange_feeling[] =
+ "you have a strange feeling for a moment, then it passes";
+
+static const char *get_ench_color(void);
+static void go_blind(void);
+static void hold_monster(void);
+static void idntfy(void);
+static void potion_heal(int);
+static void uncurse_all(void);
+
+void
+quaff(void)
+{
+ short ch;
+ object *obj;
+
+ ch = pack_letter("quaff what?", POTION);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (obj->what_is != POTION) {
+ messagef(0, "you can't drink that");
+ return;
+ }
+ switch(obj->which_kind) {
+ case INCREASE_STRENGTH:
+ messagef(0, "you feel stronger now, what bulging muscles!");
+ rogue.str_current++;
+ if (rogue.str_current > rogue.str_max) {
+ rogue.str_max = rogue.str_current;
+ }
+ break;
+ case RESTORE_STRENGTH:
+ rogue.str_current = rogue.str_max;
+ messagef(0, "this tastes great, you feel warm all over");
+ break;
+ case HEALING:
+ messagef(0, "you begin to feel better");
+ potion_heal(0);
+ break;
+ case EXTRA_HEALING:
+ messagef(0, "you begin to feel much better");
+ potion_heal(1);
+ break;
+ case POISON:
+ if (!sustain_strength) {
+ rogue.str_current -= get_rand(1, 3);
+ if (rogue.str_current < 1) {
+ rogue.str_current = 1;
+ }
+ }
+ messagef(0, "you feel very sick now");
+ if (halluc) {
+ unhallucinate();
+ }
+ break;
+ case RAISE_LEVEL:
+ rogue.exp_points = level_points[rogue.exp - 1];
+ messagef(0, "you suddenly feel much more skillful");
+ add_exp(1, 1);
+ break;
+ case BLINDNESS:
+ go_blind();
+ break;
+ case HALLUCINATION:
+ messagef(0, "oh wow, everything seems so cosmic");
+ halluc += get_rand(500, 800);
+ break;
+ case DETECT_MONSTER:
+ show_monsters();
+ if (!(level_monsters.next_monster)) {
+ messagef(0, "%s", strange_feeling);
+ }
+ break;
+ case DETECT_OBJECTS:
+ if (level_objects.next_object) {
+ if (!blind) {
+ show_objects();
+ }
+ } else {
+ messagef(0, "%s", strange_feeling);
+ }
+ break;
+ case CONFUSION:
+ messagef(0, (halluc ? "what a trippy feeling" :
+ "you feel confused"));
+ cnfs();
+ break;
+ case LEVITATION:
+ messagef(0, "you start to float in the air");
+ levitate += get_rand(15, 30);
+ being_held = bear_trap = 0;
+ break;
+ case HASTE_SELF:
+ messagef(0, "you feel yourself moving much faster");
+ haste_self += get_rand(11, 21);
+ if (!(haste_self % 2)) {
+ haste_self++;
+ }
+ break;
+ case SEE_INVISIBLE:
+ messagef(0, "hmm, this potion tastes like %sjuice",
+ fruit);
+ if (blind) {
+ unblind();
+ }
+ see_invisible = 1;
+ relight();
+ break;
+ }
+ print_stats((STAT_STRENGTH | STAT_HP));
+ if (id_potions[obj->which_kind].id_status != CALLED) {
+ id_potions[obj->which_kind].id_status = IDENTIFIED;
+ }
+ vanish(obj, 1, &rogue.pack);
+}
+
+void
+read_scroll(void)
+{
+ short ch;
+ object *obj;
+
+ ch = pack_letter("read what?", SCROL);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (obj->what_is != SCROL) {
+ messagef(0, "you can't read that");
+ return;
+ }
+ switch(obj->which_kind) {
+ case SCARE_MONSTER:
+ messagef(0, "you hear a maniacal laughter in the distance");
+ break;
+ case HOLD_MONSTER:
+ hold_monster();
+ break;
+ case ENCH_WEAPON:
+ if (rogue.weapon) {
+ if (rogue.weapon->what_is == WEAPON) {
+ messagef(0, "your %sglow%s %sfor a moment",
+ name_of(rogue.weapon),
+ ((rogue.weapon->quantity <= 1) ? "s" : ""),
+ get_ench_color());
+ if (coin_toss()) {
+ rogue.weapon->hit_enchant++;
+ } else {
+ rogue.weapon->d_enchant++;
+ }
+ }
+ rogue.weapon->is_cursed = 0;
+ } else {
+ messagef(0, "your hands tingle");
+ }
+ break;
+ case ENCH_ARMOR:
+ if (rogue.armor) {
+ messagef(0, "your armor glows %sfor a moment",
+ get_ench_color());
+ rogue.armor->d_enchant++;
+ rogue.armor->is_cursed = 0;
+ print_stats(STAT_ARMOR);
+ } else {
+ messagef(0, "your skin crawls");
+ }
+ break;
+ case IDENTIFY:
+ messagef(0, "this is a scroll of identify");
+ obj->identified = 1;
+ id_scrolls[obj->which_kind].id_status = IDENTIFIED;
+ idntfy();
+ break;
+ case TELEPORT:
+ tele();
+ break;
+ case SLEEP:
+ messagef(0, "you fall asleep");
+ take_a_nap();
+ break;
+ case PROTECT_ARMOR:
+ if (rogue.armor) {
+ messagef(0, "your armor is covered by a shimmering gold shield");
+ rogue.armor->is_protected = 1;
+ rogue.armor->is_cursed = 0;
+ } else {
+ messagef(0, "your acne seems to have disappeared");
+ }
+ break;
+ case REMOVE_CURSE:
+ messagef(0, (!halluc) ?
+ "you feel as though someone is watching over you" :
+ "you feel in touch with the universal oneness");
+ uncurse_all();
+ break;
+ case CREATE_MONSTER:
+ create_monster();
+ break;
+ case AGGRAVATE_MONSTER:
+ aggravate();
+ break;
+ case MAGIC_MAPPING:
+ messagef(0, "this scroll seems to have a map on it");
+ draw_magic_map();
+ break;
+ case CON_MON:
+ con_mon = 1;
+ messagef(0, "your hands glow %sfor a moment",
+ get_ench_color());
+ break;
+ }
+ if (id_scrolls[obj->which_kind].id_status != CALLED) {
+ id_scrolls[obj->which_kind].id_status = IDENTIFIED;
+ }
+ vanish(obj, (obj->which_kind != SLEEP), &rogue.pack);
+}
+
+/* vanish() does NOT handle a quiver of weapons with more than one
+ * arrow (or whatever) in the quiver. It will only decrement the count.
+ */
+
+void
+vanish(object *obj, short rm, object *pack)
+{
+ if (obj->quantity > 1) {
+ obj->quantity--;
+ } else {
+ if (obj->in_use_flags & BEING_WIELDED) {
+ unwield(obj);
+ } else if (obj->in_use_flags & BEING_WORN) {
+ unwear(obj);
+ } else if (obj->in_use_flags & ON_EITHER_HAND) {
+ un_put_on(obj);
+ }
+ take_from_pack(obj, pack);
+ free_object(obj);
+ }
+ if (rm) {
+ (void)reg_move();
+ }
+}
+
+static void
+potion_heal(int extra)
+{
+ float ratio;
+ short add;
+
+ rogue.hp_current += rogue.exp;
+
+ ratio = ((float)rogue.hp_current) / rogue.hp_max;
+
+ if (ratio >= 1.00) {
+ rogue.hp_max += (extra ? 2 : 1);
+ extra_hp += (extra ? 2 : 1);
+ rogue.hp_current = rogue.hp_max;
+ } else if (ratio >= 0.90) {
+ rogue.hp_max += (extra ? 1 : 0);
+ extra_hp += (extra ? 1 : 0);
+ rogue.hp_current = rogue.hp_max;
+ } else {
+ if (ratio < 0.33) {
+ ratio = 0.33;
+ }
+ if (extra) {
+ ratio += ratio;
+ }
+ add = (short)(ratio * (rogue.hp_max - rogue.hp_current));
+ rogue.hp_current += add;
+ if (rogue.hp_current > rogue.hp_max) {
+ rogue.hp_current = rogue.hp_max;
+ }
+ }
+ if (blind) {
+ unblind();
+ }
+ if (confused && extra) {
+ unconfuse();
+ } else if (confused) {
+ confused = (confused / 2) + 1;
+ }
+ if (halluc && extra) {
+ unhallucinate();
+ } else if (halluc) {
+ halluc = (halluc / 2) + 1;
+ }
+}
+
+static void
+idntfy(void)
+{
+ short ch;
+ object *obj;
+ struct id *id_table;
+ char desc[DCOLS];
+AGAIN:
+ ch = pack_letter("what would you like to identify?", ALL_OBJECTS);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item, try again");
+ messagef(0, "%s", ""); /* gcc objects to just "" */
+ check_message();
+ goto AGAIN;
+ }
+ obj->identified = 1;
+ if (obj->what_is & (SCROL | POTION | WEAPON | ARMOR | WAND | RING)) {
+ id_table = get_id_table(obj);
+ id_table[obj->which_kind].id_status = IDENTIFIED;
+ }
+ get_desc(obj, desc, sizeof(desc));
+ messagef(0, "%s", desc);
+}
+
+void
+eat(void)
+{
+ short ch;
+ short moves;
+ object *obj;
+
+ ch = pack_letter("eat what?", FOOD);
+
+ if (ch == CANCEL) {
+ return;
+ }
+ if (!(obj = get_letter_object(ch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (obj->what_is != FOOD) {
+ messagef(0, "you can't eat that");
+ return;
+ }
+ if ((obj->which_kind == FRUIT) || rand_percent(60)) {
+ moves = get_rand(950, 1150);
+ if (obj->which_kind == RATION) {
+ messagef(0, "yum, that tasted good");
+ } else {
+ messagef(0, "my, that was a yummy %s", fruit);
+ }
+ } else {
+ moves = get_rand(750, 950);
+ messagef(0, "yuk, that food tasted awful");
+ add_exp(2, 1);
+ }
+ rogue.moves_left /= 3;
+ rogue.moves_left += moves;
+ hunger_str[0] = 0;
+ print_stats(STAT_HUNGER);
+
+ vanish(obj, 1, &rogue.pack);
+}
+
+static void
+hold_monster(void)
+{
+ short i, j;
+ short mcount = 0;
+ object *monster;
+ short row, col;
+
+ for (i = -2; i <= 2; i++) {
+ for (j = -2; j <= 2; j++) {
+ row = rogue.row + i;
+ col = rogue.col + j;
+ if ((row < MIN_ROW) || (row > (DROWS-2)) || (col < 0) ||
+ (col > (DCOLS-1))) {
+ continue;
+ }
+ if (dungeon[row][col] & MONSTER) {
+ monster = object_at(&level_monsters, row, col);
+ monster->m_flags |= ASLEEP;
+ monster->m_flags &= (~WAKENS);
+ mcount++;
+ }
+ }
+ }
+ if (mcount == 0) {
+ messagef(0, "you feel a strange sense of loss");
+ } else if (mcount == 1) {
+ messagef(0, "the monster freezes");
+ } else {
+ messagef(0, "the monsters around you freeze");
+ }
+}
+
+void
+tele(void)
+{
+ mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
+
+ if (cur_room >= 0) {
+ darken_room(cur_room);
+ }
+ put_player(get_room_number(rogue.row, rogue.col));
+ being_held = 0;
+ bear_trap = 0;
+}
+
+void
+hallucinate(void)
+{
+ object *obj, *monster;
+ short ch;
+
+ if (blind) return;
+
+ obj = level_objects.next_object;
+
+ while (obj) {
+ ch = mvinch(obj->row, obj->col);
+ if (((ch < 'A') || (ch > 'Z')) &&
+ ((obj->row != rogue.row) || (obj->col != rogue.col)))
+ if ((ch != ' ') && (ch != '.') && (ch != '#') && (ch != '+')) {
+ addch(gr_obj_char());
+ }
+ obj = obj->next_object;
+ }
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ ch = mvinch(monster->row, monster->col);
+ if ((ch >= 'A') && (ch <= 'Z')) {
+ addch(get_rand('A', 'Z'));
+ }
+ monster = monster->next_monster;
+ }
+}
+
+void
+unhallucinate(void)
+{
+ halluc = 0;
+ relight();
+ messagef(1, "everything looks SO boring now");
+}
+
+void
+unblind(void)
+{
+ blind = 0;
+ messagef(1, "the veil of darkness lifts");
+ relight();
+ if (halluc) {
+ hallucinate();
+ }
+ if (detect_monster) {
+ show_monsters();
+ }
+}
+
+void
+relight(void)
+{
+ if (cur_room == PASSAGE) {
+ light_passage(rogue.row, rogue.col);
+ } else {
+ light_up_room(cur_room);
+ }
+ mvaddch(rogue.row, rogue.col, rogue.fchar);
+}
+
+void
+take_a_nap(void)
+{
+ short i;
+
+ i = get_rand(2, 5);
+ md_sleep(1);
+
+ while (i--) {
+ mv_mons();
+ }
+ md_sleep(1);
+ messagef(0, "%s", you_can_move_again);
+}
+
+static void
+go_blind(void)
+{
+ short i, j;
+
+ if (!blind) {
+ messagef(0, "a cloak of darkness falls around you");
+ }
+ blind += get_rand(500, 800);
+
+ if (detect_monster) {
+ object *monster;
+
+ monster = level_monsters.next_monster;
+
+ while (monster) {
+ mvaddch(monster->row, monster->col, monster->trail_char);
+ monster = monster->next_monster;
+ }
+ }
+ if (cur_room >= 0) {
+ for (i = rooms[cur_room].top_row + 1;
+ i < rooms[cur_room].bottom_row; i++) {
+ for (j = rooms[cur_room].left_col + 1;
+ j < rooms[cur_room].right_col; j++) {
+ mvaddch(i, j, ' ');
+ }
+ }
+ }
+ mvaddch(rogue.row, rogue.col, rogue.fchar);
+}
+
+static const char *
+get_ench_color(void)
+{
+ if (halluc) {
+ return(id_potions[get_rand(0, POTIONS-1)].title);
+ } else if (con_mon) {
+ return("red ");
+ }
+ return("blue ");
+}
+
+void
+cnfs(void)
+{
+ confused += get_rand(12, 22);
+}
+
+void
+unconfuse(void)
+{
+ confused = 0;
+ messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused"));
+}
+
+static void
+uncurse_all(void)
+{
+ object *obj;
+
+ obj = rogue.pack.next_object;
+
+ while (obj) {
+ obj->is_cursed = 0;
+ obj = obj->next_object;
+ }
+}
diff --git a/rogue/zap.c b/rogue/zap.c
new file mode 100644
index 0000000..3e9134f
--- /dev/null
+++ b/rogue/zap.c
@@ -0,0 +1,407 @@
+/* $NetBSD: zap.c,v 1.9 2008/01/14 03:50:03 dholland Exp $ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Timothy C. Stoehr.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)zap.c 8.1 (Berkeley) 5/31/93";
+#else
+__RCSID("$NetBSD: zap.c,v 1.9 2008/01/14 03:50:03 dholland Exp $");
+#endif
+#endif /* not lint */
+
+/*
+ * zap.c
+ *
+ * This source herein may be modified and/or distributed by anybody who
+ * so desires, with the following restrictions:
+ * 1.) No portion of this notice shall be removed.
+ * 2.) Credit shall not be taken for the creation of this source.
+ * 3.) This code is not to be traded, sold, or used for personal
+ * gain or profit.
+ *
+ */
+
+#include "rogue.h"
+
+static object *get_zapped_monster(short, short *, short *);
+static void tele_away(object *);
+static void wdrain_life(object *);
+static void zap_monster(object *, unsigned short);
+
+boolean wizard = 0;
+
+void
+zapp(void)
+{
+ short wch;
+ boolean first_miss = 1;
+ object *wand;
+ short dir, d, row, col;
+ object *monster;
+
+ while (!is_direction(dir = rgetchar(), &d)) {
+ sound_bell();
+ if (first_miss) {
+ messagef(0, "direction? ");
+ first_miss = 0;
+ }
+ }
+ check_message();
+ if (dir == CANCEL) {
+ return;
+ }
+ if ((wch = pack_letter("zap with what?", WAND)) == CANCEL) {
+ return;
+ }
+ check_message();
+
+ if (!(wand = get_letter_object(wch))) {
+ messagef(0, "no such item.");
+ return;
+ }
+ if (wand->what_is != WAND) {
+ messagef(0, "you can't zap with that");
+ return;
+ }
+ if (wand->class <= 0) {
+ messagef(0, "nothing happens");
+ } else {
+ wand->class--;
+ row = rogue.row; col = rogue.col;
+ if ((wand->which_kind == COLD) || (wand->which_kind == FIRE)) {
+ bounce((short)wand->which_kind, d, row, col, 0);
+ } else {
+ monster = get_zapped_monster(d, &row, &col);
+ if (wand->which_kind == DRAIN_LIFE) {
+ wdrain_life(monster);
+ } else if (monster) {
+ wake_up(monster);
+ s_con_mon(monster);
+ zap_monster(monster, wand->which_kind);
+ relight();
+ }
+ }
+ }
+ (void)reg_move();
+}
+
+static object *
+get_zapped_monster(short dir, short *row, short *col)
+{
+ short orow, ocol;
+
+ for (;;) {
+ orow = *row; ocol = *col;
+ get_dir_rc(dir, row, col, 0);
+ if (((*row == orow) && (*col == ocol)) ||
+ (dungeon[*row][*col] & (HORWALL | VERTWALL)) ||
+ (dungeon[*row][*col] == NOTHING)) {
+ return(0);
+ }
+ if (dungeon[*row][*col] & MONSTER) {
+ if (!imitating(*row, *col)) {
+ return(object_at(&level_monsters, *row, *col));
+ }
+ }
+ }
+}
+
+static void
+zap_monster(object *monster, unsigned short kind)
+{
+ short row, col;
+ object *nm;
+ short tc;
+
+ row = monster->row;
+ col = monster->col;
+
+ switch(kind) {
+ case SLOW_MONSTER:
+ if (monster->m_flags & HASTED) {
+ monster->m_flags &= (~HASTED);
+ } else {
+ monster->slowed_toggle = 0;
+ monster->m_flags |= SLOWED;
+ }
+ break;
+ case HASTE_MONSTER:
+ if (monster->m_flags & SLOWED) {
+ monster->m_flags &= (~SLOWED);
+ } else {
+ monster->m_flags |= HASTED;
+ }
+ break;
+ case TELE_AWAY:
+ tele_away(monster);
+ break;
+ case INVISIBILITY:
+ monster->m_flags |= INVISIBLE;
+ break;
+ case POLYMORPH:
+ if (monster->m_flags & HOLDS) {
+ being_held = 0;
+ }
+ nm = monster->next_monster;
+ tc = monster->trail_char;
+ (void)gr_monster(monster, get_rand(0, MONSTERS-1));
+ monster->row = row;
+ monster->col = col;
+ monster->next_monster = nm;
+ monster->trail_char = tc;
+ if (!(monster->m_flags & IMITATES)) {
+ wake_up(monster);
+ }
+ break;
+ case MAGIC_MISSILE:
+ rogue_hit(monster, 1);
+ break;
+ case CANCELLATION:
+ if (monster->m_flags & HOLDS) {
+ being_held = 0;
+ }
+ if (monster->m_flags & STEALS_ITEM) {
+ monster->drop_percent = 0;
+ }
+ monster->m_flags &= (~(FLIES | FLITS | SPECIAL_HIT | INVISIBLE |
+ FLAMES | IMITATES | CONFUSES | SEEKS_GOLD | HOLDS));
+ break;
+ case DO_NOTHING:
+ messagef(0, "nothing happens");
+ break;
+ }
+}
+
+static void
+tele_away(object *monster)
+{
+ short row, col;
+
+ if (monster->m_flags & HOLDS) {
+ being_held = 0;
+ }
+ gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT));
+ mvaddch(monster->row, monster->col, monster->trail_char);
+ dungeon[monster->row][monster->col] &= ~MONSTER;
+ monster->row = row; monster->col = col;
+ dungeon[row][col] |= MONSTER;
+ monster->trail_char = mvinch(row, col);
+ if (detect_monster || rogue_can_see(row, col)) {
+ mvaddch(row, col, gmc(monster));
+ }
+}
+
+void
+wizardize(void)
+{
+ char buf[100];
+
+ if (wizard) {
+ wizard = 0;
+ messagef(0, "not wizard anymore");
+ } else {
+ if (get_input_line("wizard's password:", "", buf, sizeof(buf),
+ "", 0, 0)) {
+ (void)xxx(1);
+ xxxx(buf, strlen(buf));
+ if (!strncmp(buf, "\247\104\126\272\115\243\027", 7)) {
+ wizard = 1;
+ score_only = 1;
+ messagef(0, "Welcome, mighty wizard!");
+ } else {
+ messagef(0, "sorry");
+ }
+ }
+ }
+}
+
+static void
+wdrain_life(object *monster)
+{
+ short hp;
+ object *lmon, *nm;
+
+ hp = rogue.hp_current / 3;
+ rogue.hp_current = (rogue.hp_current + 1) / 2;
+
+ if (cur_room >= 0) {
+ lmon = level_monsters.next_monster;
+ while (lmon) {
+ nm = lmon->next_monster;
+ if (get_room_number(lmon->row, lmon->col) == cur_room) {
+ wake_up(lmon);
+ (void)mon_damage(lmon, hp);
+ }
+ lmon = nm;
+ }
+ } else {
+ if (monster) {
+ wake_up(monster);
+ (void)mon_damage(monster, hp);
+ }
+ }
+ print_stats(STAT_HP);
+ relight();
+}
+
+void
+bounce(short ball, short dir, short row, short col, short r)
+{
+ short orow, ocol;
+ const char *s;
+ short i, ch, new_dir = -1, damage;
+ static short btime;
+
+ if (++r == 1) {
+ btime = get_rand(3, 6);
+ } else if (r > btime) {
+ return;
+ }
+
+ if (ball == FIRE) {
+ s = "fire";
+ } else {
+ s = "ice";
+ }
+ if (r > 1) {
+ messagef(0, "the %s bounces", s);
+ }
+ orow = row;
+ ocol = col;
+ do {
+ ch = mvinch(orow, ocol);
+ standout();
+ mvaddch(orow, ocol, ch);
+ get_dir_rc(dir, &orow, &ocol, 1);
+ } while (!( (ocol <= 0) ||
+ (ocol >= DCOLS-1) ||
+ (dungeon[orow][ocol] == NOTHING) ||
+ (dungeon[orow][ocol] & MONSTER) ||
+ (dungeon[orow][ocol] & (HORWALL | VERTWALL)) ||
+ ((orow == rogue.row) && (ocol == rogue.col))));
+ standend();
+ refresh();
+ do {
+ orow = row;
+ ocol = col;
+ ch = mvinch(row, col);
+ mvaddch(row, col, ch);
+ get_dir_rc(dir, &row, &col, 1);
+ } while (!( (col <= 0) ||
+ (col >= DCOLS-1) ||
+ (dungeon[row][col] == NOTHING) ||
+ (dungeon[row][col] & MONSTER) ||
+ (dungeon[row][col] & (HORWALL | VERTWALL)) ||
+ ((row == rogue.row) && (col == rogue.col))));
+
+ if (dungeon[row][col] & MONSTER) {
+ object *monster;
+
+ monster = object_at(&level_monsters, row, col);
+
+ wake_up(monster);
+ if (rand_percent(33)) {
+ messagef(0, "the %s misses the %s", s,
+ mon_name(monster));
+ goto ND;
+ }
+ if (ball == FIRE) {
+ if (!(monster->m_flags & RUSTS)) {
+ if (monster->m_flags & FREEZES) {
+ damage = monster->hp_to_kill;
+ } else if (monster->m_flags & FLAMES) {
+ damage = (monster->hp_to_kill / 10) + 1;
+ } else {
+ damage = get_rand((rogue.hp_current / 3), rogue.hp_max);
+ }
+ } else {
+ damage = (monster->hp_to_kill / 2) + 1;
+ }
+ messagef(0, "the %s hits the %s", s,
+ mon_name(monster));
+ (void)mon_damage(monster, damage);
+ } else {
+ damage = -1;
+ if (!(monster->m_flags & FREEZES)) {
+ if (rand_percent(33)) {
+ messagef(0, "the monster is frozen");
+ monster->m_flags |= (ASLEEP | NAPPING);
+ monster->nap_length = get_rand(3, 6);
+ } else {
+ damage = rogue.hp_current / 4;
+ }
+ } else {
+ damage = -2;
+ }
+ if (damage != -1) {
+ messagef(0, "the %s hits the %s", s,
+ mon_name(monster));
+ (void)mon_damage(monster, damage);
+ }
+ }
+ } else if ((row == rogue.row) && (col == rogue.col)) {
+ if (rand_percent(10 + (3 * get_armor_class(rogue.armor)))) {
+ messagef(0, "the %s misses", s);
+ goto ND;
+ } else {
+ damage = get_rand(3, (3 * rogue.exp));
+ if (ball == FIRE) {
+ damage = (damage * 3) / 2;
+ damage -= get_armor_class(rogue.armor);
+ }
+ rogue_damage(damage, NULL,
+ ((ball == FIRE) ? KFIRE : HYPOTHERMIA));
+ messagef(0, "the %s hits", s);
+ }
+ } else {
+ short nrow, ncol;
+
+ND: for (i = 0; i < 10; i++) {
+ dir = get_rand(0, DIRS-1);
+ nrow = orow;
+ ncol = ocol;
+ get_dir_rc(dir, &nrow, &ncol, 1);
+ if (((ncol >= 0) && (ncol <= DCOLS-1)) &&
+ (dungeon[nrow][ncol] != NOTHING) &&
+ (!(dungeon[nrow][ncol] & (VERTWALL | HORWALL)))) {
+ new_dir = dir;
+ break;
+ }
+ }
+ if (new_dir != -1) {
+ bounce(ball, new_dir, orow, ocol, r);
+ }
+ }
+}
diff --git a/tetris/Makefile b/tetris/Makefile
new file mode 100644
index 0000000..0d9c0d6
--- /dev/null
+++ b/tetris/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.7 2010/02/03 15:34:39 roy Exp $
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+
+PROG= tetris
+SRCS= input.c screen.c shapes.c scores.c tetris.c
+MAN= tetris.6
+DPADD= ${LIBTERMINFO}
+# 20150209 bkw: s/terminfo/curses/, add -lbsd
+LDADD=-lcurses -lbsd
+HIDEGAME=hidegame
+SETGIDGAME=yes
+
+.include <bsd.prog.mk>
diff --git a/tetris/input.c b/tetris/input.c
new file mode 100644
index 0000000..e5f8c12
--- /dev/null
+++ b/tetris/input.c
@@ -0,0 +1,162 @@
+/* $NetBSD: input.c,v 1.11 2009/05/25 04:33:53 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)input.c 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Tetris input.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "input.h"
+#include "tetris.h"
+
+/* return true iff the given timeval is positive */
+#define TV_POS(tv) \
+ ((tv)->tv_sec > 0 || ((tv)->tv_sec == 0 && (tv)->tv_usec > 0))
+
+/* subtract timeval `sub' from `res' */
+#define TV_SUB(res, sub) \
+ (res)->tv_sec -= (sub)->tv_sec; \
+ (res)->tv_usec -= (sub)->tv_usec; \
+ if ((res)->tv_usec < 0) { \
+ (res)->tv_usec += 1000000; \
+ (res)->tv_sec--; \
+ }
+
+/*
+ * Do a `read wait': poll for reading from stdin, with timeout *tvp.
+ * On return, modify *tvp to reflect the amount of time spent waiting.
+ * It will be positive only if input appeared before the time ran out;
+ * otherwise it will be zero or perhaps negative.
+ *
+ * If tvp is nil, wait forever, but return if poll is interrupted.
+ *
+ * Return 0 => no input, 1 => can read() from stdin
+ */
+int
+rwait(struct timeval *tvp)
+{
+ struct pollfd set[1];
+ struct timeval starttv, endtv;
+ int timeout;
+#define NILTZ ((struct timezone *)0)
+
+ if (tvp) {
+ (void) gettimeofday(&starttv, NILTZ);
+ endtv = *tvp;
+ timeout = tvp->tv_sec * 1000 + tvp->tv_usec / 1000;
+ } else
+ timeout = INFTIM;
+again:
+ set[0].fd = STDIN_FILENO;
+ set[0].events = POLLIN;
+ switch (poll(set, 1, timeout)) {
+
+ case -1:
+ if (tvp == 0)
+ return (-1);
+ if (errno == EINTR)
+ goto again;
+ stop("poll failed, help");
+ /* NOTREACHED */
+
+ case 0: /* timed out */
+ if (tvp) {
+ tvp->tv_sec = 0;
+ tvp->tv_usec = 0;
+ }
+ return (0);
+ }
+ if (tvp) {
+ /* since there is input, we may not have timed out */
+ (void) gettimeofday(&endtv, NILTZ);
+ TV_SUB(&endtv, &starttv);
+ TV_SUB(tvp, &endtv); /* adjust *tvp by elapsed time */
+ }
+ return (1);
+}
+
+/*
+ * `sleep' for the current turn time.
+ * Eat any input that might be available.
+ */
+void
+tsleep(void)
+{
+ struct timeval tv;
+ char c;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = fallrate;
+ while (TV_POS(&tv))
+ if (rwait(&tv) && read(0, &c, 1) != 1)
+ break;
+}
+
+/*
+ * getchar with timeout.
+ */
+int
+tgetchar(void)
+{
+ static struct timeval timeleft;
+ char c;
+
+ /*
+ * Reset timeleft to fallrate whenever it is not positive.
+ * In any case, wait to see if there is any input. If so,
+ * take it, and update timeleft so that the next call to
+ * tgetchar() will not wait as long. If there is no input,
+ * make timeleft zero or negative, and return -1.
+ *
+ * Most of the hard work is done by rwait().
+ */
+ if (!TV_POS(&timeleft)) {
+ faster(); /* go faster */
+ timeleft.tv_sec = 0;
+ timeleft.tv_usec = fallrate;
+ }
+ if (!rwait(&timeleft))
+ return (-1);
+ if (read(0, &c, 1) != 1)
+ stop("end of file, help");
+ return ((int)(unsigned char)c);
+}
diff --git a/tetris/input.h b/tetris/input.h
new file mode 100644
index 0000000..856c66a
--- /dev/null
+++ b/tetris/input.h
@@ -0,0 +1,39 @@
+/* $NetBSD: input.h,v 1.5 2004/01/27 20:30:30 jsm Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)input.h 8.1 (Berkeley) 5/31/93
+ */
+
+int rwait(struct timeval *);
+int tgetchar(void);
+void tsleep(void);
diff --git a/tetris/pathnames.h b/tetris/pathnames.h
new file mode 100644
index 0000000..f96e3cc
--- /dev/null
+++ b/tetris/pathnames.h
@@ -0,0 +1,37 @@
+/* $NetBSD: pathnames.h,v 1.3 2003/08/07 09:37:48 agc Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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_SCOREFILE "/var/games/tetris.scores"
diff --git a/tetris/scores.c b/tetris/scores.c
new file mode 100644
index 0000000..eca0ce3
--- /dev/null
+++ b/tetris/scores.c
@@ -0,0 +1,960 @@
+/* $NetBSD: scores.c,v 1.22 2014/03/22 19:05:30 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)scores.c 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Score code for Tetris, by Darren Provine (kilroy@gboro.glassboro.edu)
+ * modified 22 January 1992, to limit the number of entries any one
+ * person has.
+ *
+ * Major whacks since then.
+ */
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/file.h> /* 20150211 bkw: for flock() */
+#include <time.h>
+#include <term.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "screen.h"
+#include "scores.h"
+#include "tetris.h"
+
+/*
+ * Allow updating the high scores unless we're built as part of /rescue.
+ */
+#ifndef RESCUEDIR
+#define ALLOW_SCORE_UPDATES
+#endif
+
+/*
+ * Within this code, we can hang onto one extra "high score", leaving
+ * room for our current score (whether or not it is high).
+ *
+ * We also sometimes keep tabs on the "highest" score on each level.
+ * As long as the scores are kept sorted, this is simply the first one at
+ * that level.
+ */
+#define NUMSPOTS (MAXHISCORES + 1)
+#define NLEVELS (MAXLEVEL + 1)
+
+static time_t now;
+static int nscores;
+static int gotscores;
+static struct highscore scores[NUMSPOTS];
+
+static int checkscores(struct highscore *, int);
+static int cmpscores(const void *, const void *);
+static void getscores(int *);
+static void printem(int, int, struct highscore *, int, const char *);
+static char *thisuser(void);
+
+/* contents chosen to be a highly illegal username */
+static const char hsh_magic_val[HSH_MAGIC_SIZE] = "//:\0\0://";
+
+#define HSH_ENDIAN_NATIVE 0x12345678
+#define HSH_ENDIAN_OPP 0x78563412
+
+/* current file format version */
+#define HSH_VERSION 1
+
+/* codes for scorefile_probe return */
+#define SCOREFILE_ERROR (-1)
+#define SCOREFILE_CURRENT 0 /* 40-byte */
+#define SCOREFILE_CURRENT_OPP 1 /* 40-byte, opposite-endian */
+#define SCOREFILE_599 2 /* 36-byte */
+#define SCOREFILE_599_OPP 3 /* 36-byte, opposite-endian */
+#define SCOREFILE_50 4 /* 32-byte */
+#define SCOREFILE_50_OPP 5 /* 32-byte, opposite-endian */
+
+/*
+ * Check (or guess) what kind of score file contents we have.
+ */
+static int
+scorefile_probe(int sd)
+{
+ struct stat st;
+ int t1, t2, t3, tx;
+ ssize_t result;
+ uint32_t numbers[3], offset56, offset60, offset64;
+
+ if (fstat(sd, &st) < 0) {
+ warn("Score file %s: fstat", _PATH_SCOREFILE);
+ return -1;
+ }
+
+ t1 = st.st_size % sizeof(struct highscore_ondisk) == 0;
+ t2 = st.st_size % sizeof(struct highscore_ondisk_599) == 0;
+ t3 = st.st_size % sizeof(struct highscore_ondisk_50) == 0;
+ tx = t1 + t2 + t3;
+ if (tx == 1) {
+ /* Size matches exact number of one kind of records */
+ if (t1) {
+ return SCOREFILE_CURRENT;
+ } else if (t2) {
+ return SCOREFILE_599;
+ } else {
+ return SCOREFILE_50;
+ }
+ } else if (tx == 0) {
+ /* Size matches nothing, pick most likely as default */
+ goto wildguess;
+ }
+
+ /*
+ * File size is multiple of more than one structure size.
+ * (For example, 288 bytes could be 9*hso50 or 8*hso599.)
+ * Read the file and see if we can figure out what's going
+ * on. This is the layout of the first two records:
+ *
+ * offset hso / current hso_599 hso_50
+ * (40-byte) (36-byte) (32-byte)
+ *
+ * 0 name #0 name #0 name #0
+ * 4 : : :
+ * 8 : : :
+ * 12 : : :
+ * 16 : : :
+ * 20 score #0 score #0 score #0
+ * 24 level #0 level #0 level #0
+ * 28 (pad) time #0 time #0
+ * 32 time #0 name #1
+ * 36 name #1 :
+ * 40 name #1 : :
+ * 44 : : :
+ * 48 : : :
+ * 52 : : score #1
+ * 56 : score #1 level #1
+ * 60 score #1 level #1 time #1
+ * 64 level #1 time #1 name #2
+ * 68 (pad) : :
+ * 72 time #1 name #2 :
+ * 76 : : :
+ * 80 --- end ---
+ *
+ * There are a number of things we could check here, but the
+ * most effective test is based on the following restrictions:
+ *
+ * - The level must be between 1 and 9 (inclusive)
+ * - All times must be after 1985 and are before 2038,
+ * so the high word must be 0 and the low word may not be
+ * a small value.
+ * - Integer values of 0 or 1-9 cannot be the beginning of
+ * a login name string.
+ * - Values of 1-9 are probably not a score.
+ *
+ * So we read the three words at offsets 56, 60, and 64, and
+ * poke at the values to try to figure things...
+ */
+
+ if (lseek(sd, 56, SEEK_SET) < 0) {
+ warn("Score file %s: lseek", _PATH_SCOREFILE);
+ return -1;
+ }
+ result = read(sd, &numbers, sizeof(numbers));
+ if (result < 0) {
+ warn("Score file %s: read", _PATH_SCOREFILE);
+ return -1;
+ }
+ if ((size_t)result != sizeof(numbers)) {
+ /*
+ * The smallest file whose size divides by more than
+ * one of the sizes is substantially larger than 64,
+ * so this should *never* happen.
+ */
+ warnx("Score file %s: Unexpected EOF", _PATH_SCOREFILE);
+ return -1;
+ }
+
+ offset56 = numbers[0];
+ offset60 = numbers[1];
+ offset64 = numbers[2];
+
+ if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) {
+ /* 40-byte structure */
+ return SCOREFILE_CURRENT;
+ } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) {
+ /* 36-byte structure */
+ return SCOREFILE_599;
+ } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) {
+ /* 32-byte structure */
+ return SCOREFILE_50;
+ }
+
+ /* None was a valid level; try opposite endian */
+ offset64 = bswap32(offset64);
+ offset60 = bswap32(offset60);
+ offset56 = bswap32(offset56);
+
+ if (offset64 >= MINLEVEL && offset64 <= MAXLEVEL) {
+ /* 40-byte structure */
+ return SCOREFILE_CURRENT_OPP;
+ } else if (offset60 >= MINLEVEL && offset60 <= MAXLEVEL) {
+ /* 36-byte structure */
+ return SCOREFILE_599_OPP;
+ } else if (offset56 >= MINLEVEL && offset56 <= MAXLEVEL) {
+ /* 32-byte structure */
+ return SCOREFILE_50_OPP;
+ }
+
+ /* That didn't work either, dunno what's going on */
+ wildguess:
+ warnx("Score file %s is likely corrupt", _PATH_SCOREFILE);
+ if (sizeof(void *) == 8 && sizeof(time_t) == 8) {
+ return SCOREFILE_CURRENT;
+ } else if (sizeof(time_t) == 8) {
+ return SCOREFILE_599;
+ } else {
+ return SCOREFILE_50;
+ }
+}
+
+/*
+ * Copy a string safely, making sure it's null-terminated.
+ */
+static void
+readname(char *to, size_t maxto, const char *from, size_t maxfrom)
+{
+ size_t amt;
+
+ amt = maxto < maxfrom ? maxto : maxfrom;
+ memcpy(to, from, amt);
+ to[maxto-1] = '\0';
+}
+
+/*
+ * Copy integers, byte-swapping if desired.
+ */
+static int32_t
+read32(int32_t val, int doflip)
+{
+ if (doflip) {
+ val = bswap32(val);
+ }
+ return val;
+}
+
+static int64_t
+read64(int64_t val, int doflip)
+{
+ if (doflip) {
+ val = bswap64(val);
+ }
+ return val;
+}
+
+/*
+ * Read up to MAXHISCORES scorefile_ondisk entries.
+ */
+static int
+readscores(int sd, int doflip)
+{
+ struct highscore_ondisk buf[MAXHISCORES];
+ ssize_t result;
+ int i;
+
+ result = read(sd, buf, sizeof(buf));
+ if (result < 0) {
+ warn("Score file %s: read", _PATH_SCOREFILE);
+ return -1;
+ }
+ nscores = result / sizeof(buf[0]);
+
+ for (i=0; i<nscores; i++) {
+ readname(scores[i].hs_name, sizeof(scores[i].hs_name),
+ buf[i].hso_name, sizeof(buf[i].hso_name));
+ scores[i].hs_score = read32(buf[i].hso_score, doflip);
+ scores[i].hs_level = read32(buf[i].hso_level, doflip);
+ scores[i].hs_time = read64(buf[i].hso_time, doflip);
+ }
+ return 0;
+}
+
+/*
+ * Read up to MAXHISCORES scorefile_ondisk_599 entries.
+ */
+static int
+readscores599(int sd, int doflip)
+{
+ struct highscore_ondisk_599 buf[MAXHISCORES];
+ ssize_t result;
+ int i;
+
+ result = read(sd, buf, sizeof(buf));
+ if (result < 0) {
+ warn("Score file %s: read", _PATH_SCOREFILE);
+ return -1;
+ }
+ nscores = result / sizeof(buf[0]);
+
+ for (i=0; i<nscores; i++) {
+ readname(scores[i].hs_name, sizeof(scores[i].hs_name),
+ buf[i].hso599_name, sizeof(buf[i].hso599_name));
+ scores[i].hs_score = read32(buf[i].hso599_score, doflip);
+ scores[i].hs_level = read32(buf[i].hso599_level, doflip);
+ /*
+ * Don't bother pasting the time together into a
+ * 64-bit value; just take whichever half is nonzero.
+ */
+ scores[i].hs_time =
+ read32(buf[i].hso599_time[buf[i].hso599_time[0] == 0],
+ doflip);
+ }
+ return 0;
+}
+
+/*
+ * Read up to MAXHISCORES scorefile_ondisk_50 entries.
+ */
+static int
+readscores50(int sd, int doflip)
+{
+ struct highscore_ondisk_50 buf[MAXHISCORES];
+ ssize_t result;
+ int i;
+
+ result = read(sd, buf, sizeof(buf));
+ if (result < 0) {
+ warn("Score file %s: read", _PATH_SCOREFILE);
+ return -1;
+ }
+ nscores = result / sizeof(buf[0]);
+
+ for (i=0; i<nscores; i++) {
+ readname(scores[i].hs_name, sizeof(scores[i].hs_name),
+ buf[i].hso50_name, sizeof(buf[i].hso50_name));
+ scores[i].hs_score = read32(buf[i].hso50_score, doflip);
+ scores[i].hs_level = read32(buf[i].hso50_level, doflip);
+ scores[i].hs_time = read32(buf[i].hso50_time, doflip);
+ }
+ return 0;
+}
+
+/*
+ * Read the score file. Can be called from savescore (before showscores)
+ * or showscores (if savescore will not be called). If the given pointer
+ * is not NULL, sets *fdp to an open file handle that corresponds to a
+ * read/write score file that is locked with LOCK_EX. Otherwise, the
+ * file is locked with LOCK_SH for the read and closed before return.
+ */
+static void
+getscores(int *fdp)
+{
+ struct highscore_header header;
+ int sd, mint, lck;
+ mode_t mask;
+ const char *human;
+ int doflip;
+ int serrno;
+ ssize_t result;
+
+#ifdef ALLOW_SCORE_UPDATES
+ if (fdp != NULL) {
+ mint = O_RDWR | O_CREAT;
+ human = "read/write";
+ lck = LOCK_EX;
+ } else
+#endif
+ {
+ mint = O_RDONLY;
+ human = "reading";
+ lck = LOCK_SH;
+ }
+ setegid(egid);
+ mask = umask(S_IWOTH);
+ sd = open(_PATH_SCOREFILE, mint, 0666);
+ serrno = errno;
+ (void)umask(mask);
+ setegid(gid);
+ if (sd < 0) {
+ /*
+ * If the file simply isn't there because nobody's
+ * played yet, and we aren't going to be trying to
+ * update it, don't warn. Even if we are going to be
+ * trying to write it, don't fail -- we can still show
+ * the player the score they got.
+ */
+ errno = serrno;
+ if (fdp != NULL || errno != ENOENT) {
+ warn("Cannot open %s for %s", _PATH_SCOREFILE, human);
+ }
+ goto fail;
+ }
+
+ /*
+ * Grab a lock.
+ * XXX: failure here should probably be more fatal than this.
+ */
+ if (flock(sd, lck))
+ warn("warning: score file %s cannot be locked",
+ _PATH_SCOREFILE);
+
+ /*
+ * The current format (since -current of 20090525) is
+ *
+ * struct highscore_header
+ * up to MAXHIGHSCORES x struct highscore_ondisk
+ *
+ * Before this, there is no header, and the contents
+ * might be any of three formats:
+ *
+ * highscore_ondisk (64-bit machines with 64-bit time_t)
+ * highscore_ondisk_599 (32-bit machines with 64-bit time_t)
+ * highscore_ondisk_50 (32-bit machines with 32-bit time_t)
+ *
+ * The first two appear in 5.99 between the time_t change and
+ * 20090525, depending on whether the compiler inserts
+ * structure padding before an unaligned 64-bit time_t. The
+ * last appears in 5.0 and earlier.
+ *
+ * Any or all of these might also appear on other OSes where
+ * this code has been ported.
+ *
+ * Since the old file has no header, we will have to guess
+ * which of these formats it has.
+ */
+
+ /*
+ * First, look for a header.
+ */
+ result = read(sd, &header, sizeof(header));
+ if (result < 0) {
+ warn("Score file %s: read", _PATH_SCOREFILE);
+ goto sdfail;
+ }
+ if (result != 0 && (size_t)result != sizeof(header)) {
+ warnx("Score file %s: read: unexpected EOF", _PATH_SCOREFILE);
+ /*
+ * File is hopelessly corrupt, might as well truncate it
+ * and start over with empty scores.
+ */
+ if (lseek(sd, 0, SEEK_SET) < 0) {
+ /* ? */
+ warn("Score file %s: lseek", _PATH_SCOREFILE);
+ goto sdfail;
+ }
+ if (ftruncate(sd, 0) == 0) {
+ result = 0;
+ } else {
+ goto sdfail;
+ }
+ }
+
+ if (result == 0) {
+ /* Empty file; that just means there are no scores. */
+ nscores = 0;
+ } else {
+ /*
+ * Is what we read a header, or the first 16 bytes of
+ * a score entry? hsh_magic_val is chosen to be
+ * something that is extremely unlikely to appear in
+ * hs_name[].
+ */
+ if (!memcmp(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE)) {
+ /* Yes, we have a header. */
+
+ if (header.hsh_endiantag == HSH_ENDIAN_NATIVE) {
+ /* native endian */
+ doflip = 0;
+ } else if (header.hsh_endiantag == HSH_ENDIAN_OPP) {
+ doflip = 1;
+ } else {
+ warnx("Score file %s: Unknown endian tag %u",
+ _PATH_SCOREFILE, header.hsh_endiantag);
+ goto sdfail;
+ }
+
+ if (header.hsh_version != HSH_VERSION) {
+ warnx("Score file %s: Unknown version code %u",
+ _PATH_SCOREFILE, header.hsh_version);
+ goto sdfail;
+ }
+
+ if (readscores(sd, doflip) < 0) {
+ goto sdfail;
+ }
+ } else {
+ /*
+ * Ok, it wasn't a header. Try to figure out what
+ * size records we have.
+ */
+ result = scorefile_probe(sd);
+ if (lseek(sd, 0, SEEK_SET) < 0) {
+ warn("Score file %s: lseek", _PATH_SCOREFILE);
+ goto sdfail;
+ }
+ switch (result) {
+ case SCOREFILE_CURRENT:
+ result = readscores(sd, 0 /* don't flip */);
+ break;
+ case SCOREFILE_CURRENT_OPP:
+ result = readscores(sd, 1 /* do flip */);
+ break;
+ case SCOREFILE_599:
+ result = readscores599(sd, 0 /* don't flip */);
+ break;
+ case SCOREFILE_599_OPP:
+ result = readscores599(sd, 1 /* do flip */);
+ break;
+ case SCOREFILE_50:
+ result = readscores50(sd, 0 /* don't flip */);
+ break;
+ case SCOREFILE_50_OPP:
+ result = readscores50(sd, 1 /* do flip */);
+ break;
+ default:
+ goto sdfail;
+ }
+ if (result < 0) {
+ goto sdfail;
+ }
+ }
+ }
+
+
+ if (fdp)
+ *fdp = sd;
+ else
+ close(sd);
+
+ return;
+
+sdfail:
+ close(sd);
+ fail:
+ if (fdp != NULL) {
+ *fdp = -1;
+ }
+ nscores = 0;
+}
+
+#ifdef ALLOW_SCORE_UPDATES
+/*
+ * Paranoid write wrapper; unlike fwrite() it preserves errno.
+ */
+static int
+dowrite(int sd, const void *vbuf, size_t len)
+{
+ const char *buf = vbuf;
+ ssize_t result;
+ size_t done = 0;
+
+ while (done < len) {
+ result = write(sd, buf+done, len-done);
+ if (result < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ return -1;
+ }
+ done += result;
+ }
+ return 0;
+}
+#endif /* ALLOW_SCORE_UPDATES */
+
+/*
+ * Write the score file out.
+ */
+static void
+putscores(int sd)
+{
+#ifdef ALLOW_SCORE_UPDATES
+ struct highscore_header header;
+ struct highscore_ondisk buf[MAXHISCORES];
+ int i;
+
+ if (sd == -1) {
+ return;
+ }
+
+ memcpy(header.hsh_magic, hsh_magic_val, HSH_MAGIC_SIZE);
+ header.hsh_endiantag = HSH_ENDIAN_NATIVE;
+ header.hsh_version = HSH_VERSION;
+
+ for (i=0; i<nscores; i++) {
+ strncpy(buf[i].hso_name, scores[i].hs_name,
+ sizeof(buf[i].hso_name));
+ buf[i].hso_score = scores[i].hs_score;
+ buf[i].hso_level = scores[i].hs_level;
+ buf[i].hso_pad = 0xbaadf00d;
+ buf[i].hso_time = scores[i].hs_time;
+ }
+
+ if (lseek(sd, 0, SEEK_SET) < 0) {
+ warn("Score file %s: lseek", _PATH_SCOREFILE);
+ goto fail;
+ }
+ if (dowrite(sd, &header, sizeof(header)) < 0 ||
+ dowrite(sd, buf, sizeof(buf[0]) * nscores) < 0) {
+ warn("Score file %s: write", _PATH_SCOREFILE);
+ goto fail;
+ }
+ return;
+ fail:
+ warnx("high scores may be damaged");
+#else
+ (void)sd;
+#endif /* ALLOW_SCORE_UPDATES */
+}
+
+/*
+ * Close the score file.
+ */
+static void
+closescores(int sd)
+{
+ flock(sd, LOCK_UN);
+ close(sd);
+}
+
+/*
+ * Read and update the scores file with the current reults.
+ */
+void
+savescore(int level)
+{
+ struct highscore *sp;
+ int i;
+ int change;
+ int sd;
+ const char *me;
+
+ getscores(&sd);
+ gotscores = 1;
+ (void)time(&now);
+
+ /*
+ * Allow at most one score per person per level -- see if we
+ * can replace an existing score, or (easiest) do nothing.
+ * Otherwise add new score at end (there is always room).
+ */
+ change = 0;
+ me = thisuser();
+ for (i = 0, sp = &scores[0]; i < nscores; i++, sp++) {
+ if (sp->hs_level != level || strcmp(sp->hs_name, me) != 0)
+ continue;
+ if (score > sp->hs_score) {
+ (void)printf("%s bettered %s %d score of %d!\n",
+ "\nYou", "your old level", level,
+ sp->hs_score * sp->hs_level);
+ sp->hs_score = score; /* new score */
+ sp->hs_time = now; /* and time */
+ change = 1;
+ } else if (score == sp->hs_score) {
+ (void)printf("%s tied %s %d high score.\n",
+ "\nYou", "your old level", level);
+ sp->hs_time = now; /* renew it */
+ change = 1; /* gotta rewrite, sigh */
+ } /* else new score < old score: do nothing */
+ break;
+ }
+ if (i >= nscores) {
+ strcpy(sp->hs_name, me);
+ sp->hs_level = level;
+ sp->hs_score = score;
+ sp->hs_time = now;
+ nscores++;
+ change = 1;
+ }
+
+ if (change) {
+ /*
+ * Sort & clean the scores, then rewrite.
+ */
+ nscores = checkscores(scores, nscores);
+ putscores(sd);
+ }
+ closescores(sd);
+}
+
+/*
+ * Get login name, or if that fails, get something suitable.
+ * The result is always trimmed to fit in a score.
+ */
+static char *
+thisuser(void)
+{
+ const char *p;
+ struct passwd *pw;
+ size_t l;
+ static char u[sizeof(scores[0].hs_name)];
+
+ if (u[0])
+ return (u);
+ p = getlogin();
+ if (p == NULL || *p == '\0') {
+ pw = getpwuid(getuid());
+ if (pw != NULL)
+ p = pw->pw_name;
+ else
+ p = " ???";
+ }
+ l = strlen(p);
+ if (l >= sizeof(u))
+ l = sizeof(u) - 1;
+ memcpy(u, p, l);
+ u[l] = '\0';
+ return (u);
+}
+
+/*
+ * Score comparison function for qsort.
+ *
+ * If two scores are equal, the person who had the score first is
+ * listed first in the highscore file.
+ */
+static int
+cmpscores(const void *x, const void *y)
+{
+ const struct highscore *a, *b;
+ long l;
+
+ a = x;
+ b = y;
+ l = (long)b->hs_level * b->hs_score - (long)a->hs_level * a->hs_score;
+ if (l < 0)
+ return (-1);
+ if (l > 0)
+ return (1);
+ if (a->hs_time < b->hs_time)
+ return (-1);
+ if (a->hs_time > b->hs_time)
+ return (1);
+ return (0);
+}
+
+/*
+ * If we've added a score to the file, we need to check the file and ensure
+ * that this player has only a few entries. The number of entries is
+ * controlled by MAXSCORES, and is to ensure that the highscore file is not
+ * monopolised by just a few people. People who no longer have accounts are
+ * only allowed the highest score. Scores older than EXPIRATION seconds are
+ * removed, unless they are someone's personal best.
+ * Caveat: the highest score on each level is always kept.
+ */
+static int
+checkscores(struct highscore *hs, int num)
+{
+ struct highscore *sp;
+ int i, j, k, numnames;
+ int levelfound[NLEVELS];
+ struct peruser {
+ char *name;
+ int times;
+ } count[NUMSPOTS];
+ struct peruser *pu;
+
+ /*
+ * Sort so that highest totals come first.
+ *
+ * levelfound[i] becomes set when the first high score for that
+ * level is encountered. By definition this is the highest score.
+ */
+ qsort((void *)hs, nscores, sizeof(*hs), cmpscores);
+ for (i = MINLEVEL; i < NLEVELS; i++)
+ levelfound[i] = 0;
+ numnames = 0;
+ for (i = 0, sp = hs; i < num;) {
+ /*
+ * This is O(n^2), but do you think we care?
+ */
+ for (j = 0, pu = count; j < numnames; j++, pu++)
+ if (strcmp(sp->hs_name, pu->name) == 0)
+ break;
+ if (j == numnames) {
+ /*
+ * Add new user, set per-user count to 1.
+ */
+ pu->name = sp->hs_name;
+ pu->times = 1;
+ numnames++;
+ } else {
+ /*
+ * Two ways to keep this score:
+ * - Not too many (per user), still has acct, &
+ * score not dated; or
+ * - High score on this level.
+ */
+ if ((pu->times < MAXSCORES &&
+ getpwnam(sp->hs_name) != NULL &&
+ sp->hs_time + EXPIRATION >= now) ||
+ levelfound[sp->hs_level] == 0)
+ pu->times++;
+ else {
+ /*
+ * Delete this score, do not count it,
+ * do not pass go, do not collect $200.
+ */
+ num--;
+ for (k = i; k < num; k++)
+ hs[k] = hs[k + 1];
+ continue;
+ }
+ }
+ if (sp->hs_level < NLEVELS && sp->hs_level >= 0)
+ levelfound[sp->hs_level] = 1;
+ i++, sp++;
+ }
+ return (num > MAXHISCORES ? MAXHISCORES : num);
+}
+
+/*
+ * Show current scores. This must be called after savescore, if
+ * savescore is called at all, for two reasons:
+ * - Showscores munches the time field.
+ * - Even if that were not the case, a new score must be recorded
+ * before it can be shown anyway.
+ */
+void
+showscores(int level)
+{
+ struct highscore *sp;
+ int i, n, c;
+ const char *me;
+ int levelfound[NLEVELS];
+
+ if (!gotscores)
+ getscores(NULL);
+ (void)printf("\n\t\t\t Tetris High Scores\n");
+
+ /*
+ * If level == 0, the person has not played a game but just asked for
+ * the high scores; we do not need to check for printing in highlight
+ * mode. If SOstr is null, we can't do highlighting anyway.
+ */
+ me = level && enter_standout_mode ? thisuser() : NULL;
+
+ /*
+ * Set times to 0 except for high score on each level.
+ */
+ for (i = MINLEVEL; i < NLEVELS; i++)
+ levelfound[i] = 0;
+ for (i = 0, sp = scores; i < nscores; i++, sp++) {
+ if (sp->hs_level < NLEVELS && sp->hs_level >= 0) {
+ if (levelfound[sp->hs_level])
+ sp->hs_time = 0;
+ else {
+ sp->hs_time = 1;
+ levelfound[sp->hs_level] = 1;
+ }
+ }
+ }
+
+ /*
+ * Page each screenful of scores.
+ */
+ for (i = 0, sp = scores; i < nscores; sp += n) {
+ n = 40;
+ if (i + n > nscores)
+ n = nscores - i;
+ printem(level, i + 1, sp, n, me);
+ if ((i += n) < nscores) {
+ (void)printf("\nHit RETURN to continue.");
+ (void)fflush(stdout);
+ while ((c = getchar()) != '\n')
+ if (c == EOF)
+ break;
+ (void)printf("\n");
+ }
+ }
+}
+
+static void
+printem(int level, int offset, struct highscore *hs, int n, const char *me)
+{
+ struct highscore *sp;
+ int nrows, row, col, item, i, highlight;
+ char buf[100];
+#define TITLE "Rank Score Name (points/level)"
+
+ /*
+ * This makes a nice two-column sort with headers, but it's a bit
+ * convoluted...
+ */
+ printf("%s %s\n", TITLE, n > 1 ? TITLE : "");
+
+ highlight = 0;
+ nrows = (n + 1) / 2;
+
+ for (row = 0; row < nrows; row++) {
+ for (col = 0; col < 2; col++) {
+ item = col * nrows + row;
+ if (item >= n) {
+ /*
+ * Can only occur on trailing columns.
+ */
+ (void)putchar('\n');
+ continue;
+ }
+ sp = &hs[item];
+ (void)snprintf(buf, sizeof(buf),
+ "%3d%c %6d %-11s (%6d on %d)",
+ item + offset, sp->hs_time ? '*' : ' ',
+ sp->hs_score * sp->hs_level,
+ sp->hs_name, sp->hs_score, sp->hs_level);
+ /*
+ * Highlight if appropriate. This works because
+ * we only get one score per level.
+ */
+ if (me != NULL &&
+ sp->hs_level == level &&
+ sp->hs_score == score &&
+ strcmp(sp->hs_name, me) == 0) {
+ putpad(enter_standout_mode);
+ highlight = 1;
+ }
+ (void)printf("%s", buf);
+ if (highlight) {
+ putpad(exit_standout_mode);
+ highlight = 0;
+ }
+
+ /* fill in spaces so column 1 lines up */
+ if (col == 0)
+ for (i = 40 - strlen(buf); --i >= 0;)
+ (void)putchar(' ');
+ else /* col == 1 */
+ (void)putchar('\n');
+ }
+ }
+}
diff --git a/tetris/scores.h b/tetris/scores.h
new file mode 100644
index 0000000..5c8db42
--- /dev/null
+++ b/tetris/scores.h
@@ -0,0 +1,89 @@
+/* $NetBSD: scores.h,v 1.5 2009/05/25 08:33:57 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)scores.h 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Tetris scores.
+ */
+
+#include <stdint.h> /* 20150209 bkw */
+
+/* Header for high score file. */
+#define HSH_MAGIC_SIZE 8
+struct highscore_header {
+ char hsh_magic[HSH_MAGIC_SIZE];
+ uint32_t hsh_endiantag;
+ uint32_t hsh_version;
+};
+
+/* Current on-disk high score record. */
+struct highscore_ondisk {
+ char hso_name[20];
+ int32_t hso_score;
+ int32_t hso_level;
+ int32_t hso_pad;
+ int64_t hso_time;
+};
+
+/* 5.99.x after time_t change, on 32-bit machines */
+struct highscore_ondisk_599 {
+ char hso599_name[20];
+ int32_t hso599_score;
+ int32_t hso599_level;
+ int32_t hso599_time[2];
+};
+
+/* 5.0 and earlier on-disk high score record. */
+struct highscore_ondisk_50 {
+ char hso50_name[20];
+ int32_t hso50_score;
+ int32_t hso50_level;
+ int32_t hso50_time;
+};
+
+/* In-memory high score record. */
+struct highscore {
+ char hs_name[20]; /* login name */
+ int hs_score; /* raw score */
+ int hs_level; /* play level */
+ time_t hs_time; /* time at game end */
+};
+
+#define MAXHISCORES 80
+#define MAXSCORES 9 /* maximum high score entries per person */
+#define EXPIRATION (5L * 365 * 24 * 60 * 60)
+
+void savescore(int);
+void showscores(int);
diff --git a/tetris/screen.c b/tetris/screen.c
new file mode 100644
index 0000000..af1b1d6
--- /dev/null
+++ b/tetris/screen.c
@@ -0,0 +1,430 @@
+/* $NetBSD: screen.c,v 1.29 2014/07/13 16:23:55 pgoyette Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)screen.c 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Tetris screen control.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef sigmask
+#define sigmask(s) (1 << ((s) - 1))
+#endif
+
+#include "screen.h"
+#include "tetris.h"
+
+static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
+static int curscore;
+static int isset; /* true => terminal is in game mode */
+static struct termios oldtt;
+static void (*tstp)(int);
+
+static void scr_stop(int);
+static void stopset(int) __dead;
+
+
+/*
+ * Routine used by tputs().
+ */
+int
+put(int c)
+{
+
+ return (putchar(c));
+}
+
+/*
+ * putstr() is for unpadded strings (either as in termcap(5) or
+ * simply literal strings); putpad() is for padded strings with
+ * count=1. (See screen.h for putpad().)
+ */
+#define putstr(s) (void)fputs(s, stdout)
+
+static void
+moveto(int r, int c)
+{
+ char *buf;
+
+ buf = tiparm(cursor_address, r, c);
+ if (buf != NULL)
+ putpad(buf);
+}
+
+static void
+setcolor(int c)
+{
+ char *buf;
+ if (nocolor == 1)
+ return;
+ if (set_a_foreground == NULL)
+ return;
+
+ /* 20150209 bkw: original code:
+ buf = tiparm(set_a_foreground, c == 7 ? 0 : c);
+ ...assumes user has a white terminal background. On
+ Slackware, we have more terminals that default to a
+ black background (the console itself, Konsole, xfce4-terminal),
+ so let's assume that instead: */
+ buf = tiparm(set_a_foreground, c);
+ if (buf != NULL)
+ putpad(buf);
+}
+
+/*
+ * Set up from termcap.
+ */
+void
+scr_init(void)
+{
+
+ setupterm(NULL, 0, NULL);
+ if (clear_screen == NULL)
+ stop("cannot clear screen");
+ if (cursor_address == NULL || cursor_up == NULL)
+ stop("cannot do random cursor positioning");
+}
+
+/* this foolery is needed to modify tty state `atomically' */
+static jmp_buf scr_onstop;
+
+static void
+stopset(int sig)
+{
+ sigset_t set;
+
+ (void) signal(sig, SIG_DFL);
+ (void) kill(getpid(), sig);
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0);
+ longjmp(scr_onstop, 1);
+}
+
+static void
+scr_stop(int sig)
+{
+ sigset_t set;
+
+ scr_end();
+ (void) kill(getpid(), sig);
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0);
+ scr_set();
+ scr_msg(key_msg, 1);
+}
+
+/*
+ * Set up screen mode.
+ */
+void
+scr_set(void)
+{
+ struct winsize ws;
+ struct termios newtt;
+ sigset_t nsigset, osigset;
+ void (*ttou)(int);
+
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGTSTP);
+ sigaddset(&nsigset, SIGTTOU);
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+ if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
+ (void) signal(SIGTSTP, SIG_IGN);
+ if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
+ (void) signal(SIGTTOU, SIG_IGN);
+ /*
+ * At last, we are ready to modify the tty state. If
+ * we stop while at it, stopset() above will longjmp back
+ * to the setjmp here and we will start over.
+ */
+ (void) setjmp(scr_onstop);
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+ Rows = 0, Cols = 0;
+ if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
+ Rows = ws.ws_row;
+ Cols = ws.ws_col;
+ }
+ if (Rows == 0)
+ Rows = lines;
+ if (Cols == 0)
+ Cols = columns;
+ if (Rows < MINROWS || Cols < MINCOLS) {
+ (void) fprintf(stderr,
+ "the screen is too small: must be at least %dx%d, ",
+ MINCOLS, MINROWS);
+ stop(""); /* stop() supplies \n */
+ }
+ if (tcgetattr(0, &oldtt) < 0)
+ stop("tcgetattr() fails");
+ newtt = oldtt;
+ newtt.c_lflag &= ~(ICANON|ECHO);
+ newtt.c_oflag &= ~OXTABS;
+ if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
+ stop("tcsetattr() fails");
+ /* ospeed = cfgetospeed(&newtt); 20150209 bkw */
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+
+ /*
+ * We made it. We are now in screen mode, modulo TIstr
+ * (which we will fix immediately).
+ */
+ if (enter_ca_mode)
+ putstr(enter_ca_mode);
+ if (cursor_invisible)
+ putstr(cursor_invisible);
+ if (tstp != SIG_IGN)
+ (void) signal(SIGTSTP, scr_stop);
+ if (ttou != SIG_IGN)
+ (void) signal(SIGTTOU, ttou);
+
+ isset = 1;
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+ scr_clear();
+}
+
+/*
+ * End screen mode.
+ */
+void
+scr_end(void)
+{
+ sigset_t nsigset, osigset;
+
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGTSTP);
+ sigaddset(&nsigset, SIGTTOU);
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+ /* move cursor to last line */
+ if (cursor_to_ll)
+ putstr(cursor_to_ll);
+ else
+ moveto(Rows - 1, 0);
+ /* exit screen mode */
+ if (exit_ca_mode)
+ putstr(exit_ca_mode);
+ if (cursor_normal)
+ putstr(cursor_normal);
+ (void) fflush(stdout);
+ (void) tcsetattr(0, TCSADRAIN, &oldtt);
+ isset = 0;
+ /* restore signals */
+ (void) signal(SIGTSTP, tstp);
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+}
+
+void
+stop(const char *why)
+{
+
+ if (isset)
+ scr_end();
+ (void) fprintf(stderr, "aborting: %s\n", why);
+ exit(1);
+}
+
+/*
+ * Clear the screen, forgetting the current contents in the process.
+ */
+void
+scr_clear(void)
+{
+
+ putpad(clear_screen);
+ curscore = -1;
+ memset((char *)curscreen, 0, sizeof(curscreen));
+}
+
+#if vax && !__GNUC__
+typedef int regcell; /* pcc is bad at `register char', etc */
+#else
+typedef cell regcell;
+#endif
+
+/*
+ * Update the screen.
+ */
+void
+scr_update(void)
+{
+ cell *bp, *sp;
+ regcell so, cur_so = 0;
+ int i, ccol, j;
+ sigset_t nsigset, osigset;
+ static const struct shape *lastshape;
+
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGTSTP);
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+
+ /* always leave cursor after last displayed point */
+ curscreen[D_LAST * B_COLS - 1] = -1;
+
+ if (score != curscore) {
+ if (cursor_home)
+ putpad(cursor_home);
+ else
+ moveto(0, 0);
+ (void) printf("Score: %d", score);
+ curscore = score;
+ }
+
+ /* draw preview of nextpattern */
+ if (showpreview && (nextshape != lastshape)) {
+ static int r=5, c=2;
+ int tr, tc, t;
+
+ lastshape = nextshape;
+
+ /* clean */
+ putpad(exit_standout_mode);
+ moveto(r-1, c-1); putstr(" ");
+ moveto(r, c-1); putstr(" ");
+ moveto(r+1, c-1); putstr(" ");
+ moveto(r+2, c-1); putstr(" ");
+
+ moveto(r-3, c-2);
+ putstr("Next shape:");
+
+ /* draw */
+ putpad(enter_standout_mode);
+ setcolor(nextshape->color);
+ moveto(r, 2*c);
+ putstr(" ");
+ for(i=0; i<3; i++) {
+ t = c + r*B_COLS;
+ t += nextshape->off[i];
+
+ tr = t / B_COLS;
+ tc = t % B_COLS;
+
+ moveto(tr, 2*tc);
+ putstr(" ");
+ }
+ putpad(exit_standout_mode);
+ }
+
+ bp = &board[D_FIRST * B_COLS];
+ sp = &curscreen[D_FIRST * B_COLS];
+ for (j = D_FIRST; j < D_LAST; j++) {
+ ccol = -1;
+ for (i = 0; i < B_COLS; bp++, sp++, i++) {
+ if (*sp == (so = *bp))
+ continue;
+ *sp = so;
+ if (i != ccol) {
+ if (cur_so && move_standout_mode) {
+ putpad(exit_standout_mode);
+ cur_so = 0;
+ }
+ moveto(RTOD(j), CTOD(i));
+ }
+ if (enter_standout_mode) {
+ if (so != cur_so) {
+ putpad(so ?
+ enter_standout_mode :
+ exit_standout_mode);
+ cur_so = so;
+ }
+ setcolor(so);
+#ifdef DEBUG
+ char buf[3];
+ snprintf(buf, sizeof(buf), "%d%d", so, so);
+ putstr(buf);
+#else
+ putstr(" ");
+#endif
+ } else
+ putstr(so ? "XX" : " ");
+ ccol = i + 1;
+ /*
+ * Look ahead a bit, to avoid extra motion if
+ * we will be redrawing the cell after the next.
+ * Motion probably takes four or more characters,
+ * so we save even if we rewrite two cells
+ * `unnecessarily'. Skip it all, though, if
+ * the next cell is a different color.
+ */
+#define STOP (B_COLS - 3)
+ if (i > STOP || sp[1] != bp[1] || so != bp[1])
+ continue;
+ if (sp[2] != bp[2])
+ sp[1] = -1;
+ else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
+ sp[2] = -1;
+ sp[1] = -1;
+ }
+ }
+ }
+ if (cur_so)
+ putpad(exit_standout_mode);
+ (void) fflush(stdout);
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+}
+
+/*
+ * Write a message (set!=0), or clear the same message (set==0).
+ * (We need its length in case we have to overwrite with blanks.)
+ */
+void
+scr_msg(char *s, int set)
+{
+
+ if (set || clr_eol == NULL) {
+ int l = strlen(s);
+
+ moveto(Rows - 2, ((Cols - l) >> 1) - 1);
+ if (set)
+ putstr(s);
+ else
+ while (--l >= 0)
+ (void) putchar(' ');
+ } else {
+ moveto(Rows - 2, 0);
+ putpad(clr_eol);
+ }
+}
diff --git a/tetris/screen.c.orig b/tetris/screen.c.orig
new file mode 100644
index 0000000..788a279
--- /dev/null
+++ b/tetris/screen.c.orig
@@ -0,0 +1,424 @@
+/* $NetBSD: screen.c,v 1.29 2014/07/13 16:23:55 pgoyette Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)screen.c 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Tetris screen control.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef sigmask
+#define sigmask(s) (1 << ((s) - 1))
+#endif
+
+#include "screen.h"
+#include "tetris.h"
+
+static cell curscreen[B_SIZE]; /* 1 => standout (or otherwise marked) */
+static int curscore;
+static int isset; /* true => terminal is in game mode */
+static struct termios oldtt;
+static void (*tstp)(int);
+
+static void scr_stop(int);
+static void stopset(int) __dead;
+
+
+/*
+ * Routine used by tputs().
+ */
+int
+put(int c)
+{
+
+ return (putchar(c));
+}
+
+/*
+ * putstr() is for unpadded strings (either as in termcap(5) or
+ * simply literal strings); putpad() is for padded strings with
+ * count=1. (See screen.h for putpad().)
+ */
+#define putstr(s) (void)fputs(s, stdout)
+
+static void
+moveto(int r, int c)
+{
+ char *buf;
+
+ buf = tiparm(cursor_address, r, c);
+ if (buf != NULL)
+ putpad(buf);
+}
+
+static void
+setcolor(int c)
+{
+ char *buf;
+ if (nocolor == 1)
+ return;
+ if (set_a_foreground == NULL)
+ return;
+
+ buf = tiparm(set_a_foreground, c == 7 ? 0 : c);
+ if (buf != NULL)
+ putpad(buf);
+}
+
+/*
+ * Set up from termcap.
+ */
+void
+scr_init(void)
+{
+
+ setupterm(NULL, 0, NULL);
+ if (clear_screen == NULL)
+ stop("cannot clear screen");
+ if (cursor_address == NULL || cursor_up == NULL)
+ stop("cannot do random cursor positioning");
+}
+
+/* this foolery is needed to modify tty state `atomically' */
+static jmp_buf scr_onstop;
+
+static void
+stopset(int sig)
+{
+ sigset_t set;
+
+ (void) signal(sig, SIG_DFL);
+ (void) kill(getpid(), sig);
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0);
+ longjmp(scr_onstop, 1);
+}
+
+static void
+scr_stop(int sig)
+{
+ sigset_t set;
+
+ scr_end();
+ (void) kill(getpid(), sig);
+ sigemptyset(&set);
+ sigaddset(&set, sig);
+ (void) sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)0);
+ scr_set();
+ scr_msg(key_msg, 1);
+}
+
+/*
+ * Set up screen mode.
+ */
+void
+scr_set(void)
+{
+ struct winsize ws;
+ struct termios newtt;
+ sigset_t nsigset, osigset;
+ void (*ttou)(int);
+
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGTSTP);
+ sigaddset(&nsigset, SIGTTOU);
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+ if ((tstp = signal(SIGTSTP, stopset)) == SIG_IGN)
+ (void) signal(SIGTSTP, SIG_IGN);
+ if ((ttou = signal(SIGTTOU, stopset)) == SIG_IGN)
+ (void) signal(SIGTTOU, SIG_IGN);
+ /*
+ * At last, we are ready to modify the tty state. If
+ * we stop while at it, stopset() above will longjmp back
+ * to the setjmp here and we will start over.
+ */
+ (void) setjmp(scr_onstop);
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+ Rows = 0, Cols = 0;
+ if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
+ Rows = ws.ws_row;
+ Cols = ws.ws_col;
+ }
+ if (Rows == 0)
+ Rows = lines;
+ if (Cols == 0)
+ Cols = columns;
+ if (Rows < MINROWS || Cols < MINCOLS) {
+ (void) fprintf(stderr,
+ "the screen is too small: must be at least %dx%d, ",
+ MINCOLS, MINROWS);
+ stop(""); /* stop() supplies \n */
+ }
+ if (tcgetattr(0, &oldtt) < 0)
+ stop("tcgetattr() fails");
+ newtt = oldtt;
+ newtt.c_lflag &= ~(ICANON|ECHO);
+ newtt.c_oflag &= ~OXTABS;
+ if (tcsetattr(0, TCSADRAIN, &newtt) < 0)
+ stop("tcsetattr() fails");
+ /* ospeed = cfgetospeed(&newtt); 20150209 bkw */
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+
+ /*
+ * We made it. We are now in screen mode, modulo TIstr
+ * (which we will fix immediately).
+ */
+ if (enter_ca_mode)
+ putstr(enter_ca_mode);
+ if (cursor_invisible)
+ putstr(cursor_invisible);
+ if (tstp != SIG_IGN)
+ (void) signal(SIGTSTP, scr_stop);
+ if (ttou != SIG_IGN)
+ (void) signal(SIGTTOU, ttou);
+
+ isset = 1;
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+ scr_clear();
+}
+
+/*
+ * End screen mode.
+ */
+void
+scr_end(void)
+{
+ sigset_t nsigset, osigset;
+
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGTSTP);
+ sigaddset(&nsigset, SIGTTOU);
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+ /* move cursor to last line */
+ if (cursor_to_ll)
+ putstr(cursor_to_ll);
+ else
+ moveto(Rows - 1, 0);
+ /* exit screen mode */
+ if (exit_ca_mode)
+ putstr(exit_ca_mode);
+ if (cursor_normal)
+ putstr(cursor_normal);
+ (void) fflush(stdout);
+ (void) tcsetattr(0, TCSADRAIN, &oldtt);
+ isset = 0;
+ /* restore signals */
+ (void) signal(SIGTSTP, tstp);
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+}
+
+void
+stop(const char *why)
+{
+
+ if (isset)
+ scr_end();
+ (void) fprintf(stderr, "aborting: %s\n", why);
+ exit(1);
+}
+
+/*
+ * Clear the screen, forgetting the current contents in the process.
+ */
+void
+scr_clear(void)
+{
+
+ putpad(clear_screen);
+ curscore = -1;
+ memset((char *)curscreen, 0, sizeof(curscreen));
+}
+
+#if vax && !__GNUC__
+typedef int regcell; /* pcc is bad at `register char', etc */
+#else
+typedef cell regcell;
+#endif
+
+/*
+ * Update the screen.
+ */
+void
+scr_update(void)
+{
+ cell *bp, *sp;
+ regcell so, cur_so = 0;
+ int i, ccol, j;
+ sigset_t nsigset, osigset;
+ static const struct shape *lastshape;
+
+ sigemptyset(&nsigset);
+ sigaddset(&nsigset, SIGTSTP);
+ (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);
+
+ /* always leave cursor after last displayed point */
+ curscreen[D_LAST * B_COLS - 1] = -1;
+
+ if (score != curscore) {
+ if (cursor_home)
+ putpad(cursor_home);
+ else
+ moveto(0, 0);
+ (void) printf("Score: %d", score);
+ curscore = score;
+ }
+
+ /* draw preview of nextpattern */
+ if (showpreview && (nextshape != lastshape)) {
+ static int r=5, c=2;
+ int tr, tc, t;
+
+ lastshape = nextshape;
+
+ /* clean */
+ putpad(exit_standout_mode);
+ moveto(r-1, c-1); putstr(" ");
+ moveto(r, c-1); putstr(" ");
+ moveto(r+1, c-1); putstr(" ");
+ moveto(r+2, c-1); putstr(" ");
+
+ moveto(r-3, c-2);
+ putstr("Next shape:");
+
+ /* draw */
+ putpad(enter_standout_mode);
+ setcolor(nextshape->color);
+ moveto(r, 2*c);
+ putstr(" ");
+ for(i=0; i<3; i++) {
+ t = c + r*B_COLS;
+ t += nextshape->off[i];
+
+ tr = t / B_COLS;
+ tc = t % B_COLS;
+
+ moveto(tr, 2*tc);
+ putstr(" ");
+ }
+ putpad(exit_standout_mode);
+ }
+
+ bp = &board[D_FIRST * B_COLS];
+ sp = &curscreen[D_FIRST * B_COLS];
+ for (j = D_FIRST; j < D_LAST; j++) {
+ ccol = -1;
+ for (i = 0; i < B_COLS; bp++, sp++, i++) {
+ if (*sp == (so = *bp))
+ continue;
+ *sp = so;
+ if (i != ccol) {
+ if (cur_so && move_standout_mode) {
+ putpad(exit_standout_mode);
+ cur_so = 0;
+ }
+ moveto(RTOD(j), CTOD(i));
+ }
+ if (enter_standout_mode) {
+ if (so != cur_so) {
+ putpad(so ?
+ enter_standout_mode :
+ exit_standout_mode);
+ cur_so = so;
+ }
+ setcolor(so);
+#ifdef DEBUG
+ char buf[3];
+ snprintf(buf, sizeof(buf), "%d%d", so, so);
+ putstr(buf);
+#else
+ putstr(" ");
+#endif
+ } else
+ putstr(so ? "XX" : " ");
+ ccol = i + 1;
+ /*
+ * Look ahead a bit, to avoid extra motion if
+ * we will be redrawing the cell after the next.
+ * Motion probably takes four or more characters,
+ * so we save even if we rewrite two cells
+ * `unnecessarily'. Skip it all, though, if
+ * the next cell is a different color.
+ */
+#define STOP (B_COLS - 3)
+ if (i > STOP || sp[1] != bp[1] || so != bp[1])
+ continue;
+ if (sp[2] != bp[2])
+ sp[1] = -1;
+ else if (i < STOP && so == bp[2] && sp[3] != bp[3]) {
+ sp[2] = -1;
+ sp[1] = -1;
+ }
+ }
+ }
+ if (cur_so)
+ putpad(exit_standout_mode);
+ (void) fflush(stdout);
+ (void) sigprocmask(SIG_SETMASK, &osigset, (sigset_t *)0);
+}
+
+/*
+ * Write a message (set!=0), or clear the same message (set==0).
+ * (We need its length in case we have to overwrite with blanks.)
+ */
+void
+scr_msg(char *s, int set)
+{
+
+ if (set || clr_eol == NULL) {
+ int l = strlen(s);
+
+ moveto(Rows - 2, ((Cols - l) >> 1) - 1);
+ if (set)
+ putstr(s);
+ else
+ while (--l >= 0)
+ (void) putchar(' ');
+ } else {
+ moveto(Rows - 2, 0);
+ putpad(clr_eol);
+ }
+}
diff --git a/tetris/screen.h b/tetris/screen.h
new file mode 100644
index 0000000..c0b3905
--- /dev/null
+++ b/tetris/screen.h
@@ -0,0 +1,54 @@
+/* $NetBSD: screen.h,v 1.9 2009/08/12 08:51:21 dholland Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)screen.h 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Capabilities from TERMCAP (used in the score code).
+ */
+extern char *SEstr; /* end standout mode */
+extern char *SOstr; /* begin standout mode */
+
+/*
+ * putpad() is for padded strings with count=1.
+ */
+#define putpad(s) tputs(s, 1, put)
+
+int put(int); /* just calls putchar; for tputs */
+void scr_clear(void);
+void scr_end(void);
+void scr_init(void);
+void scr_msg(char *, int);
+void scr_set(void);
+void scr_update(void);
diff --git a/tetris/shapes.c b/tetris/shapes.c
new file mode 100644
index 0000000..ef82771
--- /dev/null
+++ b/tetris/shapes.c
@@ -0,0 +1,106 @@
+/* $NetBSD: shapes.c,v 1.9 2014/06/11 16:47:39 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)shapes.c 8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Tetris shapes and related routines.
+ *
+ * Note that the first 7 are `well known'.
+ */
+
+#include <sys/cdefs.h>
+#include "tetris.h"
+
+#define TL -B_COLS-1 /* top left */
+#define TC -B_COLS /* top center */
+#define TR -B_COLS+1 /* top right */
+#define ML -1 /* middle left */
+#define MR 1 /* middle right */
+#define BL B_COLS-1 /* bottom left */
+#define BC B_COLS /* bottom center */
+#define BR B_COLS+1 /* bottom right */
+
+const struct shape shapes[] = {
+ /* 0*/ { 7, 7, { TL, TC, MR, } },
+ /* 1*/ { 1, 8, { TC, TR, ML, } },
+ /* 2*/ { 2, 9, { ML, MR, BC, } },
+ /* 3*/ { 3, 3, { TL, TC, ML, } },
+ /* 4*/ { 4, 12, { ML, BL, MR, } },
+ /* 5*/ { 5, 15, { ML, BR, MR, } },
+ /* 6*/ { 6, 18, { ML, MR, 2 } }, /* sticks out */
+ /* 7*/ { 7, 0, { TC, ML, BL, } },
+ /* 8*/ { 1, 1, { TC, MR, BR, } },
+ /* 9*/ { 2, 10, { TC, MR, BC, } },
+ /*10*/ { 2, 11, { TC, ML, MR, } },
+ /*11*/ { 2, 2, { TC, ML, BC, } },
+ /*12*/ { 4, 13, { TC, BC, BR, } },
+ /*13*/ { 4, 14, { TR, ML, MR, } },
+ /*14*/ { 4, 4, { TL, TC, BC, } },
+ /*15*/ { 5, 16, { TR, TC, BC, } },
+ /*16*/ { 5, 17, { TL, MR, ML, } },
+ /*17*/ { 5, 5, { TC, BC, BL, } },
+ /*18*/ { 6, 6, { TC, BC, 2*B_COLS } } /* sticks out */
+};
+
+/*
+ * Return true iff the given shape fits in the given position,
+ * taking the current board into account.
+ */
+int
+fits_in(const struct shape *shape, int pos)
+{
+ const int *o = shape->off;
+
+ if (board[pos] || board[pos + *o++] || board[pos + *o++] ||
+ board[pos + *o])
+ return 0;
+ return 1;
+}
+
+/*
+ * Write the given shape into the current board, turning it on
+ * if `onoff' is 1, and off if `onoff' is 0.
+ */
+void
+place(const struct shape *shape, int pos, int onoff)
+{
+ const int *o = shape->off;
+ onoff = onoff ? shape->color : 0;
+
+ board[pos] = onoff;
+ board[pos + *o++] = onoff;
+ board[pos + *o++] = onoff;
+ board[pos + *o] = onoff;
+}
diff --git a/tetris/tetris.6 b/tetris/tetris.6
new file mode 100644
index 0000000..b0c6778
--- /dev/null
+++ b/tetris/tetris.6
@@ -0,0 +1,161 @@
+.\" $NetBSD: tetris.6,v 1.14 2014/07/15 16:17:15 wiz Exp $
+.\"
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Nancy L. Tinkham and Darren F. Provine.
+.\"
+.\" 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.
+.\"
+.\" @(#)tetris.6 8.1 (Berkeley) 5/31/93
+.\"
+.Dd July 13, 2014
+.Dt TETRIS 6
+.Os
+.Sh NAME
+.Nm tetris
+.Nd the game of tetris
+.Sh SYNOPSIS
+.Nm
+.Op Fl bps
+.Op Fl k Ar keys
+.Op Fl l Ar level
+.Sh DESCRIPTION
+The
+.Nm
+command runs display-based game which must be played on a CRT terminal.
+The object is to fit the shapes together forming complete rows,
+which then vanish.
+When the shapes fill up to the top, the game ends.
+You can optionally select a level of play, or custom-select control keys.
+.Pp
+The default level of play is 2.
+.Pp
+The default control keys are as follows:
+.Pp
+.Bl -tag -width "xxspacexx" -compact -offset indent
+.It j
+move left
+.It k
+rotate 1/4 turn counterclockwise
+.It l
+move right
+.It Aq space
+drop
+.It p
+pause
+.It q
+quit
+.El
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+By default, shapes are displayed colorfully if the user's CRT supports color.
+The
+.Fl b
+option can be used to restore the traditional black-and-white behavior.
+.It Fl k
+The default control keys can be changed using the
+.Fl k
+option.
+The
+.Ar keys
+argument must have the six keys in order, and, remember to quote any
+space or tab characters from the shell.
+For example:
+.sp
+.Dl "tetris -l 2 -k 'jkl pq'"
+.sp
+will play the default games, i.e. level 2 and with the default
+control keys.
+The current key settings are displayed at the bottom of the screen
+during play.
+.It Fl l
+Select a level of play.
+.It Fl s
+Display the top scores.
+.It Fl p
+Switch on previewing of the shape that will appear next.
+.El
+.Sh PLAY
+At the start of the game, a shape will appear at the top of the screen,
+falling one square at a time.
+The speed at which it falls is determined directly by the level:
+if you select level 2, the blocks will fall twice per second;
+at level 9, they fall 9 times per second.
+(As the game goes on, things speed up,
+no matter what your initial selection.)
+When this shape
+.Dq touches down
+on the bottom of the field, another will appear at the top.
+.Pp
+You can move shapes to the left or right, rotate them counterclockwise,
+or drop them to the bottom by pressing the appropriate keys.
+As you fit them together, completed horizontal rows vanish,
+and any blocks above fall down to fill in.
+When the blocks stack up to the top of the screen, the game is over.
+.Sh SCORING
+You get one point for every block you fit into the stack,
+and one point for every space a block falls when you hit the drop key.
+(Dropping the blocks is therefore a good way to increase your score.)
+Your total score is the product of the level of play
+and your accumulated
+.ie t points\(em200
+.el points -- 200
+points on level 3 gives you a score of 600.
+Each player gets at most one entry on any level,
+for a total of nine scores in the high scores file.
+Players who no longer have accounts are limited to one score.
+Also, scores over 5 years old are expired.
+The exception to these conditions is that the highest score on a given
+level is
+.Em always
+kept,
+so that following generations can pay homage to those who have
+wasted serious amounts of time.
+.Pp
+The score list is produced at the end of the game.
+The printout includes each player's overall ranking,
+name, score, and how many points were scored on what level.
+Scores which are the highest on a given level
+are marked with asterisks
+.Dq * .
+.Sh FILES
+.Bl -tag -width /var/games/tetris.scoresxx
+.It /var/games/tetris.scores
+high score file
+.El
+.Sh AUTHORS
+Adapted from a 1989 International Obfuscated C Code Contest winner by
+Chris Torek and Darren F. Provine.
+.Pp
+Manual adapted from the original entry written by Nancy L. Tinkham and
+Darren F. Provine.
+.Pp
+Code for previewing next shape added by Hubert Feyrer in 1999.
+.Sh BUGS
+The higher levels are unplayable without a fast terminal connection.
diff --git a/tetris/tetris.c b/tetris/tetris.c
new file mode 100644
index 0000000..53a31f2
--- /dev/null
+++ b/tetris/tetris.c
@@ -0,0 +1,338 @@
+/* $NetBSD: tetris.c,v 1.27 2014/07/13 17:38:38 pgoyette Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)tetris.c 8.1 (Berkeley) 5/31/93
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1992, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+/*
+ * Tetris (or however it is spelled).
+ */
+
+#include <sys/time.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "input.h"
+#include "scores.h"
+#include "screen.h"
+#include "tetris.h"
+
+cell board[B_SIZE]; /* 1 => occupied, 0 => empty */
+
+int Rows, Cols; /* current screen size */
+
+static const struct shape *curshape;
+const struct shape *nextshape;
+
+long fallrate; /* less than 1 million; smaller => faster */
+
+int score; /* the obvious thing */
+gid_t gid, egid;
+
+char key_msg[100];
+int showpreview;
+int nocolor;
+
+static void elide(void);
+static void setup_board(void);
+static void onintr(int) __dead;
+static void usage(void) __dead;
+
+/*
+ * Set up the initial board. The bottom display row is completely set,
+ * along with another (hidden) row underneath that. Also, the left and
+ * right edges are set.
+ */
+static void
+setup_board(void)
+{
+ int i;
+ cell *p;
+
+ p = board;
+ for (i = B_SIZE; i; i--)
+ *p++ = (i <= (2 * B_COLS) || (i % B_COLS) < 2) ? 7 : 0;
+}
+
+/*
+ * Elide any full active rows.
+ */
+static void
+elide(void)
+{
+ int i, j, base;
+ cell *p;
+
+ for (i = A_FIRST; i < A_LAST; i++) {
+ base = i * B_COLS + 1;
+ p = &board[base];
+ for (j = B_COLS - 2; *p++ != 0;) {
+ if (--j <= 0) {
+ /* this row is to be elided */
+ memset(&board[base], 0, B_COLS - 2);
+ scr_update();
+ tsleep();
+ while (--base != 0)
+ board[base + B_COLS] = board[base];
+ scr_update();
+ tsleep();
+ break;
+ }
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int pos, c;
+ const char *keys;
+ int level = 2;
+ char key_write[6][10];
+ int ch, i, j;
+ int fd;
+
+ gid = getgid();
+ egid = getegid();
+ setegid(gid);
+
+ fd = open("/dev/null", O_RDONLY);
+ if (fd < 3)
+ exit(1);
+ close(fd);
+
+ keys = "jkl pq";
+
+ while ((ch = getopt(argc, argv, "bk:l:ps")) != -1)
+ switch(ch) {
+ case 'b':
+ nocolor = 1;
+ break;
+ case 'k':
+ if (strlen(keys = optarg) != 6)
+ usage();
+ break;
+ case 'l':
+ level = atoi(optarg);
+ if (level < MINLEVEL || level > MAXLEVEL) {
+ errx(1, "level must be from %d to %d",
+ MINLEVEL, MAXLEVEL);
+ }
+ break;
+ case 'p':
+ showpreview = 1;
+ break;
+ case 's':
+ showscores(0);
+ exit(0);
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ fallrate = 1000000 / level;
+
+ for (i = 0; i <= 5; i++) {
+ for (j = i+1; j <= 5; j++) {
+ if (keys[i] == keys[j]) {
+ errx(1, "duplicate command keys specified.");
+ }
+ }
+ if (keys[i] == ' ')
+ strcpy(key_write[i], "<space>");
+ else {
+ key_write[i][0] = keys[i];
+ key_write[i][1] = '\0';
+ }
+ }
+
+ snprintf(key_msg, sizeof(key_msg),
+"%s - left %s - rotate %s - right %s - drop %s - pause %s - quit",
+ key_write[0], key_write[1], key_write[2], key_write[3],
+ key_write[4], key_write[5]);
+
+ (void)signal(SIGINT, onintr);
+ scr_init();
+ setup_board();
+
+ srandom(getpid());
+ scr_set();
+
+ pos = A_FIRST*B_COLS + (B_COLS/2)-1;
+ nextshape = randshape();
+ curshape = randshape();
+
+ scr_msg(key_msg, 1);
+
+ for (;;) {
+ place(curshape, pos, 1);
+ scr_update();
+ place(curshape, pos, 0);
+ c = tgetchar();
+ if (c < 0) {
+ /*
+ * Timeout. Move down if possible.
+ */
+ if (fits_in(curshape, pos + B_COLS)) {
+ pos += B_COLS;
+ continue;
+ }
+
+ /*
+ * Put up the current shape `permanently',
+ * bump score, and elide any full rows.
+ */
+ place(curshape, pos, 1);
+ score++;
+ elide();
+
+ /*
+ * Choose a new shape. If it does not fit,
+ * the game is over.
+ */
+ curshape = nextshape;
+ nextshape = randshape();
+ pos = A_FIRST*B_COLS + (B_COLS/2)-1;
+ if (!fits_in(curshape, pos))
+ break;
+ continue;
+ }
+
+ /*
+ * Handle command keys.
+ */
+ if (c == keys[5]) {
+ /* quit */
+ break;
+ }
+ if (c == keys[4]) {
+ static char msg[] =
+ "paused - press RETURN to continue";
+
+ place(curshape, pos, 1);
+ do {
+ scr_update();
+ scr_msg(key_msg, 0);
+ scr_msg(msg, 1);
+ (void) fflush(stdout);
+ } while (rwait(NULL) == -1);
+ scr_msg(msg, 0);
+ scr_msg(key_msg, 1);
+ place(curshape, pos, 0);
+ continue;
+ }
+ if (c == keys[0]) {
+ /* move left */
+ if (fits_in(curshape, pos - 1))
+ pos--;
+ continue;
+ }
+ if (c == keys[1]) {
+ /* turn */
+ const struct shape *new = &shapes[curshape->rot];
+
+ if (fits_in(new, pos))
+ curshape = new;
+ continue;
+ }
+ if (c == keys[2]) {
+ /* move right */
+ if (fits_in(curshape, pos + 1))
+ pos++;
+ continue;
+ }
+ if (c == keys[3]) {
+ /* move to bottom */
+ while (fits_in(curshape, pos + B_COLS)) {
+ pos += B_COLS;
+ score++;
+ }
+ continue;
+ }
+ if (c == '\f') {
+ scr_clear();
+ scr_msg(key_msg, 1);
+ }
+ }
+
+ scr_clear();
+ scr_end();
+
+ (void)printf("Your score: %d point%s x level %d = %d\n",
+ score, score == 1 ? "" : "s", level, score * level);
+ savescore(level);
+
+ printf("\nHit RETURN to see high scores, ^C to skip.\n");
+
+ while ((i = getchar()) != '\n')
+ if (i == EOF)
+ break;
+
+ showscores(level);
+
+ exit(0);
+}
+
+static void
+onintr(int signo __attribute__((unused)))
+{
+ scr_clear();
+ scr_end();
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: %s [-ps] [-k keys] [-l level]\n",
+ getprogname());
+ exit(1);
+}
diff --git a/tetris/tetris.h b/tetris/tetris.h
new file mode 100644
index 0000000..559ef76
--- /dev/null
+++ b/tetris/tetris.h
@@ -0,0 +1,175 @@
+/* $NetBSD: tetris.h,v 1.14 2014/07/13 16:23:55 pgoyette Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek and Darren F. Provine.
+ *
+ * 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.
+ *
+ * @(#)tetris.h 8.1 (Berkeley) 5/31/93
+ */
+
+#include <sys/types.h>
+
+/*
+ * Definitions for Tetris.
+ */
+
+/*
+ * The display (`board') is composed of 23 rows of 12 columns of characters
+ * (numbered 0..22 and 0..11), stored in a single array for convenience.
+ * Columns 1 to 10 of rows 1 to 20 are the actual playing area, where
+ * shapes appear. Columns 0 and 11 are always occupied, as are all
+ * columns of rows 21 and 22. Rows 0 and 22 exist as boundary areas
+ * so that regions `outside' the visible area can be examined without
+ * worrying about addressing problems.
+ */
+
+ /* the board */
+#define B_COLS 12
+#define B_ROWS 23
+#define B_SIZE (B_ROWS * B_COLS)
+
+typedef unsigned char cell;
+extern cell board[B_SIZE]; /* 1 => occupied, 0 => empty */
+
+ /* the displayed area (rows) */
+#define D_FIRST 1
+#define D_LAST 22
+
+ /* the active area (rows) */
+#define A_FIRST 1
+#define A_LAST 21
+
+/*
+ * Minimum display size.
+ */
+#define MINROWS 23
+#define MINCOLS 40
+
+extern int Rows, Cols; /* current screen size */
+
+/*
+ * Translations from board coordinates to display coordinates.
+ * As with board coordinates, display coordiates are zero origin.
+ */
+#define RTOD(x) ((x) - 1)
+#define CTOD(x) ((x) * 2 + (((Cols - 2 * B_COLS) >> 1) - 1))
+
+/*
+ * A `shape' is the fundamental thing that makes up the game. There
+ * are 7 basic shapes, each consisting of four `blots':
+ *
+ * X.X X.X X.X
+ * X.X X.X X.X.X X.X X.X.X X.X.X X.X.X.X
+ * X X X
+ *
+ * 0 1 2 3 4 5 6
+ *
+ * Except for 3 and 6, the center of each shape is one of the blots.
+ * This blot is designated (0,0). The other three blots can then be
+ * described as offsets from the center. Shape 3 is the same under
+ * rotation, so its center is effectively irrelevant; it has been chosen
+ * so that it `sticks out' upward and leftward. Except for shape 6,
+ * all the blots are contained in a box going from (-1,-1) to (+1,+1);
+ * shape 6's center `wobbles' as it rotates, so that while it `sticks out'
+ * rightward, its rotation---a vertical line---`sticks out' downward.
+ * The containment box has to include the offset (2,0), making the overall
+ * containment box range from offset (-1,-1) to (+2,+1). (This is why
+ * there is only one row above, but two rows below, the display area.)
+ *
+ * The game works by choosing one of these shapes at random and putting
+ * its center at the middle of the first display row (row 1, column 5).
+ * The shape is moved steadily downward until it collides with something:
+ * either another shape, or the bottom of the board. When the shape can
+ * no longer be moved downwards, it is merged into the current board.
+ * At this time, any completely filled rows are elided, and blots above
+ * these rows move down to make more room. A new random shape is again
+ * introduced at the top of the board, and the whole process repeats.
+ * The game ends when the new shape will not fit at (1,5).
+ *
+ * While the shapes are falling, the user can rotate them counterclockwise
+ * 90 degrees (in addition to moving them left or right), provided that the
+ * rotation puts the blots in empty spaces. The table of shapes is set up
+ * so that each shape contains the index of the new shape obtained by
+ * rotating the current shape. Due to symmetry, each shape has exactly
+ * 1, 2, or 4 rotations total; the first 7 entries in the table represent
+ * the primary shapes, and the remaining 12 represent their various
+ * rotated forms.
+ */
+struct shape {
+ int color;
+ int rot; /* index of rotated version of this shape */
+ int off[3]; /* offsets to other blots if center is at (0,0) */
+};
+
+extern const struct shape shapes[];
+#define randshape() (&shapes[random() % 7])
+
+extern const struct shape *nextshape;
+
+/*
+ * Shapes fall at a rate faster than once per second.
+ *
+ * The initial rate is determined by dividing 1 million microseconds
+ * by the game `level'. (This is at most 1 million, or one second.)
+ * Each time the fall-rate is used, it is decreased a little bit,
+ * depending on its current value, via the `faster' macro below.
+ * The value eventually reaches a limit, and things stop going faster,
+ * but by then the game is utterly impossible.
+ */
+extern long fallrate; /* less than 1 million; smaller => faster */
+#define faster() (fallrate -= fallrate / 3000)
+
+/*
+ * Game level must be between 1 and 9. This controls the initial fall rate
+ * and affects scoring.
+ */
+#define MINLEVEL 1
+#define MAXLEVEL 9
+
+/*
+ * Scoring is as follows:
+ *
+ * When the shape comes to rest, and is integrated into the board,
+ * we score one point. If the shape is high up (at a low-numbered row),
+ * and the user hits the space bar, the shape plummets all the way down,
+ * and we score a point for each row it falls (plus one more as soon as
+ * we find that it is at rest and integrate it---until then, it can
+ * still be moved or rotated).
+ */
+extern int score; /* the obvious thing */
+extern gid_t gid, egid;
+
+extern char key_msg[100];
+extern int showpreview;
+extern int nocolor;
+
+int fits_in(const struct shape *, int);
+void place(const struct shape *, int, int);
+void stop(const char *) __dead;