From 013ac7742311556022304e8b30ca170d48b3a016 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Thu, 7 May 2015 16:32:32 -0400 Subject: initial commit --- Makefile | 70 ++ Makefile.inc | 26 + README | 101 ++ boggle/Makefile | 40 + boggle/README | 66 ++ boggle/boggle/Makefile | 19 + boggle/boggle/bog.c | 714 +++++++++++++ boggle/boggle/bog.h | 83 ++ boggle/boggle/boggle.6 | 127 +++ boggle/boggle/extern.h | 62 ++ boggle/boggle/help.c | 105 ++ boggle/boggle/helpfile | 92 ++ boggle/boggle/mach.c | 665 ++++++++++++ boggle/boggle/prtable.c | 127 +++ boggle/boggle/timer.c | 119 +++ boggle/boggle/word.c | 215 ++++ boggle/mkdict/Makefile | 10 + boggle/mkdict/mkdict.c | 124 +++ boggle/mkindex/Makefile | 10 + boggle/mkindex/mkindex.c | 131 +++ bs/Makefile | 10 + bs/bs.6 | 76 ++ bs/bs.c | 1196 +++++++++++++++++++++ cgram/Makefile | 11 + cgram/cgram.6 | 65 ++ cgram/cgram.c | 344 ++++++ cgram/pathnames.h | 30 + ching/Makefile | 5 + ching/Makefile.inc | 5 + ching/castching/Makefile | 7 + ching/castching/castching.c | 135 +++ ching/ching/Makefile | 13 + ching/ching/ching.6 | 154 +++ ching/ching/ching.sh | 81 ++ ching/ching/hexagrams | 2337 +++++++++++++++++++++++++++++++++++++++++ ching/ching/macros | 126 +++ ching/include/ching.h | 44 + ching/printching/Makefile | 7 + ching/printching/pathnames.h | 38 + ching/printching/printching.c | 326 ++++++ colorbars/Makefile | 9 + colorbars/colorbars.6 | 47 + colorbars/colorbars.c | 124 +++ dab/Makefile | 14 + dab/algor.cc | 310 ++++++ dab/algor.h | 76 ++ dab/board.cc | 253 +++++ dab/board.h | 86 ++ dab/box.cc | 150 +++ dab/box.h | 93 ++ dab/dab.6 | 112 ++ dab/defs.h | 43 + dab/gamescreen.cc | 43 + dab/gamescreen.h | 72 ++ dab/human.cc | 149 +++ dab/human.h | 53 + dab/main.cc | 191 ++++ dab/player.cc | 91 ++ dab/player.h | 70 ++ dab/random.cc | 79 ++ dab/random.h | 66 ++ dab/test.cc | 50 + dab/ttyscrn.cc | 233 ++++ dab/ttyscrn.h | 74 ++ dm/Makefile | 18 + dm/dm.8 | 106 ++ dm/dm.c | 335 ++++++ dm/dm.conf.5 | 114 ++ dm/pathnames.h | 37 + grdc/Makefile | 9 + grdc/grdc.6 | 43 + grdc/grdc.c | 266 +++++ hack/COPYRIGHT | 6 + hack/Makefile | 52 + hack/Makequest | 196 ++++ hack/OWNER | 2 + hack/Original_READ_ME | 61 ++ hack/READ_ME | 92 ++ hack/alloc.c | 38 + hack/config.h | 124 +++ hack/data | 232 ++++ hack/date.h | 2 + hack/def.edog.h | 12 + hack/def.eshk.h | 24 + hack/def.flag.h | 42 + hack/def.func_tab.h | 13 + hack/def.gold.h | 12 + hack/def.mkroom.h | 26 + hack/def.monst.h | 60 ++ hack/def.obj.h | 48 + hack/def.objclass.h | 62 ++ hack/def.objects.h | 290 +++++ hack/def.permonst.h | 27 + hack/def.rm.h | 53 + hack/def.trap.h | 27 + hack/def.wseg.h | 14 + hack/hack.6 | 160 +++ hack/hack.Decl.c | 42 + hack/hack.apply.c | 467 ++++++++ hack/hack.bones.c | 108 ++ hack/hack.c | 904 ++++++++++++++++ hack/hack.cmd.c | 332 ++++++ hack/hack.do.c | 512 +++++++++ hack/hack.do_name.c | 316 ++++++ hack/hack.do_wear.c | 395 +++++++ hack/hack.dog.c | 459 ++++++++ hack/hack.eat.c | 492 +++++++++ hack/hack.end.c | 725 +++++++++++++ hack/hack.engrave.c | 337 ++++++ hack/hack.fight.c | 404 +++++++ hack/hack.fix | 113 ++ hack/hack.h | 707 +++++++++++++ hack/hack.invent.c | 964 +++++++++++++++++ hack/hack.ioctl.c | 47 + hack/hack.lev.c | 276 +++++ hack/hack.main.c | 507 +++++++++ hack/hack.makemon.c | 212 ++++ hack/hack.mfndpos.h | 12 + hack/hack.mhitu.c | 385 +++++++ hack/hack.mklev.c | 795 ++++++++++++++ hack/hack.mkmaze.c | 157 +++ hack/hack.mkobj.c | 160 +++ hack/hack.mkshop.c | 304 ++++++ hack/hack.mon.c | 961 +++++++++++++++++ hack/hack.monst.c | 79 ++ hack/hack.o_init.c | 184 ++++ hack/hack.objnam.c | 584 ++++++++++ hack/hack.options.c | 217 ++++ hack/hack.pager.c | 418 ++++++++ hack/hack.potion.c | 407 +++++++ hack/hack.pri.c | 721 +++++++++++++ hack/hack.read.c | 572 ++++++++++ hack/hack.rip.c | 89 ++ hack/hack.rumors.c | 90 ++ hack/hack.save.c | 240 +++++ hack/hack.search.c | 147 +++ hack/hack.sh | 14 + hack/hack.shk.c | 1085 +++++++++++++++++++ hack/hack.shknam.c | 149 +++ hack/hack.steal.c | 210 ++++ hack/hack.termcap.c | 268 +++++ hack/hack.timeout.c | 72 ++ hack/hack.topl.c | 236 +++++ hack/hack.track.c | 49 + hack/hack.trap.c | 488 +++++++++ hack/hack.tty.c | 313 ++++++ hack/hack.u_init.c | 385 +++++++ hack/hack.unix.c | 421 ++++++++ hack/hack.vault.c | 310 ++++++ hack/hack.version.c | 20 + hack/hack.wield.c | 111 ++ hack/hack.wizard.c | 198 ++++ hack/hack.worm.c | 223 ++++ hack/hack.worn.c | 70 ++ hack/hack.zap.c | 688 ++++++++++++ hack/help | 132 +++ hack/hh | 55 + hack/makedefs.c | 266 +++++ hack/pathnames.h | 35 + hack/rnd.c | 35 + hack/rumors | 505 +++++++++ hals_end/Makefile | 9 + hals_end/hals_end.6 | 66 ++ hals_end/hals_end.c | 151 +++ include/bsdcompat.h | 61 ++ include/sys/tty.h | 1 + larn/COPYRIGHT | 8 + larn/Fixed.Bugs | 218 ++++ larn/Makefile | 83 ++ larn/OWNER | 3 + larn/README | 150 +++ larn/action.c | 305 ++++++ larn/bill.c | 163 +++ larn/config.c | 50 + larn/create.c | 606 +++++++++++ larn/data.c | 664 ++++++++++++ larn/datfiles/larn.help | 140 +++ larn/datfiles/larnmaze | 288 +++++ larn/datfiles/larnopts | 12 + larn/diag.c | 411 ++++++++ larn/display.c | 637 +++++++++++ larn/extern.h | 226 ++++ larn/fortune.c | 94 ++ larn/global.c | 861 +++++++++++++++ larn/header.h | 444 ++++++++ larn/help.c | 129 +++ larn/io.c | 991 +++++++++++++++++ larn/larn.6 | 157 +++ larn/main.c | 1341 +++++++++++++++++++++++ larn/monster.c | 1911 +++++++++++++++++++++++++++++++++ larn/moreobj.c | 296 ++++++ larn/movem.c | 447 ++++++++ larn/nap.c | 23 + larn/object.c | 1336 +++++++++++++++++++++++ larn/pathnames.h | 38 + larn/regen.c | 187 ++++ larn/savelev.c | 67 ++ larn/scores.c | 855 +++++++++++++++ larn/signal.c | 138 +++ larn/store.c | 910 ++++++++++++++++ larn/tok.c | 235 +++++ out | 404 +++++++ paranoia/Makefile | 16 + paranoia/README | 8 + paranoia/README.linux | 10 + paranoia/paranoia.c | 1036 ++++++++++++++++++ rogue/CHANGES | 55 + rogue/Makefile | 19 + rogue/USD.doc/Makefile | 10 + rogue/USD.doc/rogue.me | 834 +++++++++++++++ rogue/hit.c | 466 ++++++++ rogue/init.c | 366 +++++++ rogue/inventory.c | 841 +++++++++++++++ rogue/level.c | 900 ++++++++++++++++ rogue/machdep.c | 493 +++++++++ rogue/main.c | 84 ++ rogue/message.c | 378 +++++++ rogue/monster.c | 886 ++++++++++++++++ rogue/move.c | 649 ++++++++++++ rogue/object.c | 802 ++++++++++++++ rogue/pack.c | 574 ++++++++++ rogue/pathnames.h | 35 + rogue/play.c | 299 ++++++ rogue/random.c | 146 +++ rogue/ring.c | 338 ++++++ rogue/rogue.6 | 107 ++ rogue/rogue.h | 702 +++++++++++++ rogue/room.c | 671 ++++++++++++ rogue/save.c | 427 ++++++++ rogue/score.c | 674 ++++++++++++ rogue/spec_hit.c | 536 ++++++++++ rogue/throw.c | 324 ++++++ rogue/trap.c | 282 +++++ rogue/use.c | 625 +++++++++++ rogue/zap.c | 407 +++++++ tetris/Makefile | 13 + tetris/input.c | 162 +++ tetris/input.h | 39 + tetris/pathnames.h | 37 + tetris/scores.c | 960 +++++++++++++++++ tetris/scores.h | 89 ++ tetris/screen.c | 430 ++++++++ tetris/screen.c.orig | 424 ++++++++ tetris/screen.h | 54 + tetris/shapes.c | 106 ++ tetris/tetris.6 | 161 +++ tetris/tetris.c | 338 ++++++ tetris/tetris.h | 175 +++ 248 files changed, 66290 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.inc create mode 100644 README create mode 100644 boggle/Makefile create mode 100644 boggle/README create mode 100644 boggle/boggle/Makefile create mode 100644 boggle/boggle/bog.c create mode 100644 boggle/boggle/bog.h create mode 100644 boggle/boggle/boggle.6 create mode 100644 boggle/boggle/extern.h create mode 100644 boggle/boggle/help.c create mode 100644 boggle/boggle/helpfile create mode 100644 boggle/boggle/mach.c create mode 100644 boggle/boggle/prtable.c create mode 100644 boggle/boggle/timer.c create mode 100644 boggle/boggle/word.c create mode 100644 boggle/mkdict/Makefile create mode 100644 boggle/mkdict/mkdict.c create mode 100644 boggle/mkindex/Makefile create mode 100644 boggle/mkindex/mkindex.c create mode 100644 bs/Makefile create mode 100644 bs/bs.6 create mode 100644 bs/bs.c create mode 100644 cgram/Makefile create mode 100644 cgram/cgram.6 create mode 100644 cgram/cgram.c create mode 100644 cgram/pathnames.h create mode 100644 ching/Makefile create mode 100644 ching/Makefile.inc create mode 100644 ching/castching/Makefile create mode 100644 ching/castching/castching.c create mode 100644 ching/ching/Makefile create mode 100644 ching/ching/ching.6 create mode 100644 ching/ching/ching.sh create mode 100644 ching/ching/hexagrams create mode 100644 ching/ching/macros create mode 100644 ching/include/ching.h create mode 100644 ching/printching/Makefile create mode 100644 ching/printching/pathnames.h create mode 100644 ching/printching/printching.c create mode 100644 colorbars/Makefile create mode 100644 colorbars/colorbars.6 create mode 100644 colorbars/colorbars.c create mode 100644 dab/Makefile create mode 100644 dab/algor.cc create mode 100644 dab/algor.h create mode 100644 dab/board.cc create mode 100644 dab/board.h create mode 100644 dab/box.cc create mode 100644 dab/box.h create mode 100644 dab/dab.6 create mode 100644 dab/defs.h create mode 100644 dab/gamescreen.cc create mode 100644 dab/gamescreen.h create mode 100644 dab/human.cc create mode 100644 dab/human.h create mode 100644 dab/main.cc create mode 100644 dab/player.cc create mode 100644 dab/player.h create mode 100644 dab/random.cc create mode 100644 dab/random.h create mode 100644 dab/test.cc create mode 100644 dab/ttyscrn.cc create mode 100644 dab/ttyscrn.h create mode 100644 dm/Makefile create mode 100644 dm/dm.8 create mode 100644 dm/dm.c create mode 100644 dm/dm.conf.5 create mode 100644 dm/pathnames.h create mode 100644 grdc/Makefile create mode 100644 grdc/grdc.6 create mode 100644 grdc/grdc.c create mode 100644 hack/COPYRIGHT create mode 100644 hack/Makefile create mode 100644 hack/Makequest create mode 100644 hack/OWNER create mode 100644 hack/Original_READ_ME create mode 100644 hack/READ_ME create mode 100644 hack/alloc.c create mode 100644 hack/config.h create mode 100644 hack/data create mode 100644 hack/date.h create mode 100644 hack/def.edog.h create mode 100644 hack/def.eshk.h create mode 100644 hack/def.flag.h create mode 100644 hack/def.func_tab.h create mode 100644 hack/def.gold.h create mode 100644 hack/def.mkroom.h create mode 100644 hack/def.monst.h create mode 100644 hack/def.obj.h create mode 100644 hack/def.objclass.h create mode 100644 hack/def.objects.h create mode 100644 hack/def.permonst.h create mode 100644 hack/def.rm.h create mode 100644 hack/def.trap.h create mode 100644 hack/def.wseg.h create mode 100644 hack/hack.6 create mode 100644 hack/hack.Decl.c create mode 100644 hack/hack.apply.c create mode 100644 hack/hack.bones.c create mode 100644 hack/hack.c create mode 100644 hack/hack.cmd.c create mode 100644 hack/hack.do.c create mode 100644 hack/hack.do_name.c create mode 100644 hack/hack.do_wear.c create mode 100644 hack/hack.dog.c create mode 100644 hack/hack.eat.c create mode 100644 hack/hack.end.c create mode 100644 hack/hack.engrave.c create mode 100644 hack/hack.fight.c create mode 100644 hack/hack.fix create mode 100644 hack/hack.h create mode 100644 hack/hack.invent.c create mode 100644 hack/hack.ioctl.c create mode 100644 hack/hack.lev.c create mode 100644 hack/hack.main.c create mode 100644 hack/hack.makemon.c create mode 100644 hack/hack.mfndpos.h create mode 100644 hack/hack.mhitu.c create mode 100644 hack/hack.mklev.c create mode 100644 hack/hack.mkmaze.c create mode 100644 hack/hack.mkobj.c create mode 100644 hack/hack.mkshop.c create mode 100644 hack/hack.mon.c create mode 100644 hack/hack.monst.c create mode 100644 hack/hack.o_init.c create mode 100644 hack/hack.objnam.c create mode 100644 hack/hack.options.c create mode 100644 hack/hack.pager.c create mode 100644 hack/hack.potion.c create mode 100644 hack/hack.pri.c create mode 100644 hack/hack.read.c create mode 100644 hack/hack.rip.c create mode 100644 hack/hack.rumors.c create mode 100644 hack/hack.save.c create mode 100644 hack/hack.search.c create mode 100644 hack/hack.sh create mode 100644 hack/hack.shk.c create mode 100644 hack/hack.shknam.c create mode 100644 hack/hack.steal.c create mode 100644 hack/hack.termcap.c create mode 100644 hack/hack.timeout.c create mode 100644 hack/hack.topl.c create mode 100644 hack/hack.track.c create mode 100644 hack/hack.trap.c create mode 100644 hack/hack.tty.c create mode 100644 hack/hack.u_init.c create mode 100644 hack/hack.unix.c create mode 100644 hack/hack.vault.c create mode 100644 hack/hack.version.c create mode 100644 hack/hack.wield.c create mode 100644 hack/hack.wizard.c create mode 100644 hack/hack.worm.c create mode 100644 hack/hack.worn.c create mode 100644 hack/hack.zap.c create mode 100644 hack/help create mode 100644 hack/hh create mode 100644 hack/makedefs.c create mode 100644 hack/pathnames.h create mode 100644 hack/rnd.c create mode 100644 hack/rumors create mode 100644 hals_end/Makefile create mode 100644 hals_end/hals_end.6 create mode 100644 hals_end/hals_end.c create mode 100644 include/bsdcompat.h create mode 100644 include/sys/tty.h create mode 100644 larn/COPYRIGHT create mode 100644 larn/Fixed.Bugs create mode 100644 larn/Makefile create mode 100644 larn/OWNER create mode 100644 larn/README create mode 100644 larn/action.c create mode 100644 larn/bill.c create mode 100644 larn/config.c create mode 100644 larn/create.c create mode 100644 larn/data.c create mode 100644 larn/datfiles/larn.help create mode 100644 larn/datfiles/larnmaze create mode 100644 larn/datfiles/larnopts create mode 100644 larn/diag.c create mode 100644 larn/display.c create mode 100644 larn/extern.h create mode 100644 larn/fortune.c create mode 100644 larn/global.c create mode 100644 larn/header.h create mode 100644 larn/help.c create mode 100644 larn/io.c create mode 100644 larn/larn.6 create mode 100644 larn/main.c create mode 100644 larn/monster.c create mode 100644 larn/moreobj.c create mode 100644 larn/movem.c create mode 100644 larn/nap.c create mode 100644 larn/object.c create mode 100644 larn/pathnames.h create mode 100644 larn/regen.c create mode 100644 larn/savelev.c create mode 100644 larn/scores.c create mode 100644 larn/signal.c create mode 100644 larn/store.c create mode 100644 larn/tok.c create mode 100644 out create mode 100644 paranoia/Makefile create mode 100644 paranoia/README create mode 100644 paranoia/README.linux create mode 100644 paranoia/paranoia.c create mode 100644 rogue/CHANGES create mode 100644 rogue/Makefile create mode 100644 rogue/USD.doc/Makefile create mode 100644 rogue/USD.doc/rogue.me create mode 100644 rogue/hit.c create mode 100644 rogue/init.c create mode 100644 rogue/inventory.c create mode 100644 rogue/level.c create mode 100644 rogue/machdep.c create mode 100644 rogue/main.c create mode 100644 rogue/message.c create mode 100644 rogue/monster.c create mode 100644 rogue/move.c create mode 100644 rogue/object.c create mode 100644 rogue/pack.c create mode 100644 rogue/pathnames.h create mode 100644 rogue/play.c create mode 100644 rogue/random.c create mode 100644 rogue/ring.c create mode 100644 rogue/rogue.6 create mode 100644 rogue/rogue.h create mode 100644 rogue/room.c create mode 100644 rogue/save.c create mode 100644 rogue/score.c create mode 100644 rogue/spec_hit.c create mode 100644 rogue/throw.c create mode 100644 rogue/trap.c create mode 100644 rogue/use.c create mode 100644 rogue/zap.c create mode 100644 tetris/Makefile create mode 100644 tetris/input.c create mode 100644 tetris/input.h create mode 100644 tetris/pathnames.h create mode 100644 tetris/scores.c create mode 100644 tetris/scores.h create mode 100644 tetris/screen.c create mode 100644 tetris/screen.c.orig create mode 100644 tetris/screen.h create mode 100644 tetris/shapes.c create mode 100644 tetris/tetris.6 create mode 100644 tetris/tetris.c create mode 100644 tetris/tetris.h 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 + +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 +.include 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 + +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 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 +#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 +#include +#include +#include +#include +#include +#include +#include + +#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 +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 to begin..."); + while (inputch() != ' '); + + for (done = 0; !done;) { + newgame(bspec); + bspec = NULL; /* reset for subsequent games */ + playgame(); +#ifdef NEW_STYLE + prompt("Type uit, locate, any other key to continue..."); +#else + prompt("Type 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 + +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 +#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 +#include + +#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 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: or or +Delete previous character: or +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: 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: + 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 +#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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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': /* */ + case CTRL('h'): /* */ + 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(""); + 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': /* */ + case '\010': /* */ + 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 +#ifndef lint +__RCSID("$NetBSD: prtable.c,v 1.10 2013/10/19 17:23:08 christos Exp $"); +#endif /* not lint */ + +#include + +#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 +#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 +#include +#include + +#include +#include +#include +#include +#include + +#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 +#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 +#include + +#include +#include +#include +#include + +#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 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 +#include +#include +#include + +#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 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 +#include + +#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 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 + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 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 +#include +#include +#include +#include +#include +#include +#include +#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=0 && cury= 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 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 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 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 +#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 +#include +#include +#include +#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 + +.if ${MKSHARE} != "no" +FILES= hexagrams macros +FILESDIR=/usr/share/games/ching +.endif + +.include 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 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 +#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 +#include +#include +#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 . + */ + 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 + */ + 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 + * or of the form + * .LA + * or of the form + * .H + */ + 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 + */ + 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 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 +.\" 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 + * 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 +__RCSID("$NetBSD: colorbars.c,v 1.1 2012/06/06 00:13:36 christos Exp $"); + +#include +#include +#include +#include +#include + +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 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 +#include +#include +#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(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(*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(*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 + +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 + +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(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(e)); + return cnt; +} + +// Clear the box +void BOX::reset(void) +{ + for (int e = BOX::first; e < BOX::last; e++) + clr(static_cast(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 + +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 + +#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 + +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 +#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 +#include +#include +#include +#include +#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 ] [-n ] [ []]\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 + +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 +#include +#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 + +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 +#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 +#include +#include + +#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(tx) < *x * 2 + TTYSCRN::offsx + 14 + || static_cast(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 + +# -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 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 +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* 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 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 +#include +#include +#include +#include +#ifndef NONPOSIX +#include +#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 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 , 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 ) 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 +#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 nor 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 +#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 *)<h, 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 +#include +#include +#include +#include +#include +#include +#include + +#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 and define the + * same constants, and the C preprocessor complains. + */ +#include "hack.h" +#include +struct termios termio; + +void +getioctls(void) +{ + tcgetattr(fileno(stdin), &termio); +} + +void +setioctls(void) +{ + tcsetattr(fileno(stdin), TCSANOW, &termio); +} + +#ifdef SUSPEND +#include +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 +#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=\"\"' in your environment, or ", + "give the command 'o' followed by the line `' while playing. ", + "Here is a list of